<?xml version="1.0" encoding="UTF-8"?> <feed xmlns="http://www.w3.org/2005/Atom">
  <title>Krzysztof Kowalczyk blog</title>
  <link href="https://blog.kowalczyk.info/atom.xml" rel="alternate"></link>
  <id>https://blog.kowalczyk.info/atom.xml</id>
  <updated>2001-08-29T00:00:00Z</updated>
  <entry>
   <title>@levelsio and survivorship bias</title>
   <link href="https://blog.kowalczyk.info/article/de943f80c7924745abf9405f8c7a2c67/levelsio-and-survivorship-bias.html" rel="alternate"></link>
   <updated>2021-10-20T00:00:00Z</updated>
   <id>tag:blog.kowalczyk.info,2021-10-20:/article/de943f80c7924745abf9405f8c7a2c67/levelsio-and-survivorship-bias.html</id>
   <content type="html">&lt;p&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;notion-page&#34; id=&#34;de943f80-c792-4745-abf9-405f8c7a2c67&#34;&gt;&#xA;  &lt;div id=&#34;609961a1-7560-4296-b604-a08ea8cd06a3&#34; class=&#34;&#34;&gt;Pieter Levels is a prolific maker of software. &#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;5a876db7-2e98-43ee-b741-f8bc48a2cddd&#34; class=&#34;&#34;&gt;He&amp;#x27;s also very successful maker of software: he&amp;#x27;s close to making $1.5 million a year from his business, almost all of it profit.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;2c2e2cab-3947-46e0-8612-b004891d2097&#34; class=&#34;&#34;&gt;Almost all of it is his profit since for most of the time he was a sole developer / marketer / copy writer, with a part-time sysadmin for ensuring the server stays alive. Recently he hired customer support and chat moderator.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;8dfc20bd-aefd-4554-9b74-7428d57ccbec&#34; class=&#34;&#34;&gt;This level of success &lt;a href=&#34;https://news.ycombinator.com/item?id=28873956&#34;&gt;attracts attention&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;e08f5439-64cc-4f22-9dd1-233a01c4f4d3&#34; class=&#34;&#34;&gt;It also attracts inevitable claims of survivorship bias. The gist of it is: if you do things he did, you won&amp;#x27;t have the same level of success.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;9ec84a05-7784-4d79-acbf-f72bf6ed4744&#34; class=&#34;&#34;&gt;I think it&amp;#x27;s a very defeatist attitude. Life is hard, there is no step-by-step guide to $1.5 million a year business.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;770aadfe-705b-4e40-8b05-81e6231df6f5&#34; class=&#34;&#34;&gt;You can, however, do things right or you can do them badly.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;a53469d3-f4dc-4b07-a86d-b2624c7e1087&#34; class=&#34;&#34;&gt;To create a successful business you need to do more things right than badly.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;80314497-95a8-49a5-8405-8af87d5679ca&#34; class=&#34;&#34;&gt;Let&amp;#x27;s call it &lt;strong&gt;Do Things Right bias&lt;/strong&gt;.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;8f72a20d-c94d-43cd-90f3-e2af45d98e8f&#34; class=&#34;&#34;&gt;Let&amp;#x27;s dissect Peter Level&amp;#x27;s journey to see what he did right and how that differed from doing things badly.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;207dabed-8eab-4dc9-bc78-93bbef6a531c&#34; class=&#34;&#34;&gt;I&amp;#x27;ve been following Pieter for a long time and he created a large body of tweets and blog posts, a book, a few youtube talks, most of it related to his journey from making $0 a year to $1.5 million a year.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;f3b78b55-1250-4621-9608-fc88a19388e3&#34; class=&#34;&#34;&gt;People say you can learn from other people&amp;#x27;s mistakes. It&amp;#x27;s even better to learn from other people&amp;#x27;s successes.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;9e57e4b8-9ccc-430c-ba83-6bedd34c063f&#34; class=&#34;&#34;&gt;What did Pieter do right?&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;e290bb55-2245-4c7b-895b-fbd40f456da7&#34; class=&#34;&#34;&gt;Pieter ships products&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;9ac2df87-7a2f-434b-bf67-d13f6a9d0b08&#34; class=&#34;&#34;&gt;Here&amp;#x27;s a &lt;a href=&#34;https://levels.io/projects/&#34;&gt;list of his projects&lt;/a&gt; going back 11 years. Limiting the list to only software products:&#xA;  &lt;div class=&#34;indented&#34;&gt;&#xA;    &lt;ul id=&#34;d3829f57-aeb0-4717-b902-eea50374eb2d&#34; class=&#34;bulleted-list&#34;&gt;&#xA;      &lt;li&gt;2010: Uber clone&#xA;      &lt;/li&gt;&#xA;      &lt;li&gt;2012 dating site for college campuses, YouTube network&#xA;      &lt;/li&gt;&#xA;      &lt;li&gt;2013: YouTube analytics&#xA;      &lt;/li&gt;&#xA;      &lt;li&gt;2014: Slack community for digital nomads, GIF Book, Nomad Jobs, Nomad List 1.0, Go Fucking Do It, Play My Inbox&#xA;      &lt;/li&gt;&#xA;      &lt;li&gt;2015: Startup Retreats, Taylor Bot (a Telegram bot), Nomad List 2.0, Remote OK&#xA;      &lt;/li&gt;&#xA;      &lt;li&gt;2016: Virtual Reality, Places to Work, learning 3D modelling&#xA;      &lt;/li&gt;&#xA;      &lt;li&gt;2017: Nomad List 3.0, Nomad Gear, Mute, Hoodmaps&#xA;      &lt;/li&gt;&#xA;      &lt;li&gt;2018: Nomad List FIRE Calculator, MAKE Book, Maker Rank, No More Google&#xA;      &lt;/li&gt;&#xA;      &lt;li&gt;2019: Bali Sea Cable, Nomad List 5, How Much Is My Side Project Worth&#xA;      &lt;/li&gt;&#xA;      &lt;li&gt;2020: QR Menu Creator, IdeasAI, Remote OK Workers, Airline List, Nomad List Climate Finder&#xA;      &lt;/li&gt;&#xA;      &lt;li&gt;2021: Rebase, Inflation Chart, MAKE Book NFT,&#xA;      &lt;/li&gt;&#xA;    &lt;/ul&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;8aa9c788-bb23-4189-a9df-60a8a8220b39&#34; class=&#34;&#34;&gt;You can see him &lt;a href=&#34;https://levels.io/hoodmaps/&#34;&gt;building a product from first line of code to frontpage of Reddit&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;6f783ee7-6279-4c2a-b013-a061532dc18a&#34; class=&#34;&#34;&gt;&lt;strong&gt;Doing it badly&lt;/strong&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;de1fc0a3-04ce-4a42-bd2c-439ee43fff9e&#34; class=&#34;&#34;&gt;Not shipping products.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;74ad1c11-9599-4110-af07-6b3d40e8b8d4&#34; class=&#34;&#34;&gt;Commenting on Hacker News how you couldn&amp;#x27;t possibly take away from your precious time commenting on Hacker News and write some code instead.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;8dedfb17-703e-4f0e-a6bb-4bbd3bb6665f&#34; class=&#34;&#34;&gt;Pieter is persistent&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;a408c407-f243-4ab2-a2ce-0b7b8d4480f2&#34; class=&#34;&#34;&gt;He&amp;#x27;s clearly persistent.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;38d29991-b09a-4b41-8b41-516aac4da0a1&#34; class=&#34;&#34;&gt;His overnight success was 11 years in the making. &#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;55746c52-cc36-4982-bc82-1234cbb68d16&#34; class=&#34;&#34;&gt;A lot of his projects failed but he didn&amp;#x27;t give up. &#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;ee842500-6e36-4676-827f-948a7ce63015&#34; class=&#34;&#34;&gt;He kept making more.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;cb0930c9-c7f3-48d6-ad79-bd4e45f361c9&#34; class=&#34;&#34;&gt;He creates new things even when he doesn&amp;#x27;t have to because Nomad List and RemoteOk bring more money that he needs.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;f13c3271-4af6-44b3-b1ed-1f6e635de9c5&#34; class=&#34;&#34;&gt;&lt;strong&gt;Doing it badly&lt;/strong&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;0bd62edd-1d2e-4d0b-b420-b4289a903d4b&#34; class=&#34;&#34;&gt;Abandoning all work after first failure and using survivorship bias as a convenient excuse.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;539538dc-3bc3-460f-abdb-219779b44cba&#34; class=&#34;&#34;&gt;Pieter understands compund interest&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;35408d72-28e5-4845-99c5-457633db37f8&#34; class=&#34;&#34;&gt;If you have a profitable business, there are 2 ways to make even more money:&#xA;  &lt;div class=&#34;indented&#34;&gt;&#xA;    &lt;ul id=&#34;c45a8f36-1bbe-4f13-9556-d3f4b5153d9e&#34; class=&#34;bulleted-list&#34;&gt;&#xA;      &lt;li&gt;start a new business&#xA;      &lt;/li&gt;&#xA;      &lt;li&gt;sell more stuff to your existing customers&#xA;      &lt;/li&gt;&#xA;    &lt;/ul&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;ee4fa213-6c9c-4909-8c1f-53f0caf7c794&#34; class=&#34;&#34;&gt;The second option tends to be easier and more reliable way to increate revenue and profits.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;a3788cc7-7148-4ae7-85f6-6540cd1d6d00&#34; class=&#34;&#34;&gt;The hardest thing in business is getting attention of potential customers. &#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;d0a19cea-6dcb-4c78-b454-db6f41350516&#34; class=&#34;&#34;&gt;It&amp;#x27;s much easier to get attention of customers you already have.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;d29fea24-68ed-4bc3-b05a-07bff7be5eb0&#34; class=&#34;&#34;&gt;While Pieter starts new projects, he&amp;#x27;s smart enough to mostly do things related to hist most successful business: Nomad List.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;9513181a-b4bb-409f-9237-f295cb30b941&#34; class=&#34;&#34;&gt;He keeps improving Nomad List.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;fb0cd414-0fcd-4f1b-851e-d90c81e920ec&#34; class=&#34;&#34;&gt;He built jobs website for remote workers and does other related projects like Nomad List Climate Finder.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;ff92132b-a9f2-4811-b1e8-9f1e7f79656c&#34; class=&#34;&#34;&gt;This is similar to the magic of compounded interest in investing.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;5d31be83-2a1e-4a37-8a3b-d01f4e188df0&#34; class=&#34;&#34;&gt;&lt;strong&gt;Doing it badly&lt;/strong&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;2561e1ef-43e5-4f50-88ea-ece4e0ec93a9&#34; class=&#34;&#34;&gt;Not doubling down on things that work. Chasing distractions.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;a807f967-559c-4504-86ea-ba74d0b4a70f&#34; class=&#34;&#34;&gt;Pieter abandons projects that are not working&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;eca8dc6d-f4b9-429b-91f3-e205daf38980&#34; class=&#34;&#34;&gt;You need to be persistent, but in a smart way.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;2daadc42-f0a9-49d8-aef4-f3b18f4ce9f7&#34; class=&#34;&#34;&gt;You&amp;#x27;ll know when you have a product market fit.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;362638bb-2092-48ad-8720-6981924c26e6&#34; class=&#34;&#34;&gt;Nomad List started as a Google Spreadsheets that Pieter created for himself and accidently left unprotected. &#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;8360c121-d895-4fc4-bf8e-28bfc8a68e2b&#34; class=&#34;&#34;&gt;Within days people were adding more information to it. That was a clear sign of interest so he turned it into a website.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;208054c3-2391-4acd-b654-c94abebc4814&#34; class=&#34;&#34;&gt;All projects start as weakling babies that need to be initially nurtured to health. But if they never manage to get up on their feet, you have to throw them in the river.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;07bf306b-eabc-4b5d-bd13-e451f1252864&#34; class=&#34;&#34;&gt;Ok, that was exceedingly bad analogy.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;78cbd954-850c-456b-a432-64d4a12e4395&#34; class=&#34;&#34;&gt;&lt;strong&gt;Doing it badly&lt;/strong&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;d5803365-5fec-4381-9550-07b5e7ff490d&#34; class=&#34;&#34;&gt;Sticking with a product that doesn&amp;#x27;t perform instead of creating a new project that just might.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;9d5782b0-f606-4ef8-bfc9-76b924441e7c&#34; class=&#34;&#34;&gt;Pieter chargers money&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;1ed66392-103c-44c8-85d1-792d415ea9fc&#34; class=&#34;&#34;&gt;Freemium is a popular tactic for getting users.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;0f6f5bd8-4585-459a-84a5-bed8fa525771&#34; class=&#34;&#34;&gt;Many people find it psychologically hard to charge for software.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;b272a3de-bdb9-44ba-afd0-9d999ded2c47&#34; class=&#34;&#34;&gt;Those are potential reasons why people don&amp;#x27;t charge for their products.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;89bd6022-1b20-41e6-ae83-ccbb8e9a054c&#34; class=&#34;&#34;&gt;Not Pieter. He asks for money early and he asks for a lot. &lt;a href=&#34;https://makebook.io/&#34;&gt;His book&lt;/a&gt; is $49 (and I bought it). Nomad List is $199.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;acbaef87-9d93-428b-bb92-2a50f6beb71e&#34; class=&#34;&#34;&gt;&lt;a href=&#34;https://levels.io/idea-validation/&#34;&gt;The only real validation is people paying for your product&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;fe0643ce-98fd-4418-a2f8-6da0ae902e6c&#34; class=&#34;&#34;&gt;&lt;strong&gt;Doing it badly&lt;/strong&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;a3b2dffa-5aaa-4c1d-926d-e5e917828929&#34; class=&#34;&#34;&gt;Not charging at all, not charging early, not charging enough.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;06995e82-ca44-4efa-a495-d6ddb25f6d61&#34; class=&#34;&#34;&gt;Pieter ships quickly&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;8b3b4705-2c5f-4882-b674-1a829d24d4ee&#34; class=&#34;&#34;&gt;NomadList started as a Google Docs Spreadsheet.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;6fd14d17-f3c8-47bd-acae-68eb7ebe7469&#34; class=&#34;&#34;&gt;Pieter turned it into a simple website and made that simple website available immediately.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;b18c168c-b8e1-44dd-bf76-b7ecdc04c0c1&#34; class=&#34;&#34;&gt;He then kept working on it and improving it.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;dd79edba-ae24-44ce-b375-7bbb2b21551e&#34; class=&#34;&#34;&gt;This is true of all his projects: ship the smallest thing that provides value, promote it, double-down on things that are working.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;afcc95a0-61a8-48db-b228-19bf49c5fafc&#34; class=&#34;&#34;&gt;&lt;strong&gt;Doing it badly&lt;/strong&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;75972e10-923d-4cec-869a-de4d1329dd4f&#34; class=&#34;&#34;&gt;Spending a year writing that web app without showing it to anyone.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;8b86ba27-c562-4120-9074-350c910de327&#34; class=&#34;&#34;&gt;Pieter does a lot of marketing and promotion&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;8a6a5329-a8a3-4b4a-9f11-8d0f7bf0de2f&#34; class=&#34;&#34;&gt;From early days he was promoting his stuff on his blog, Hacker News, Product Hunt, Reddit, Twitter etc.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;cfcf3275-8e95-473b-ad2c-048eb36ce4e4&#34; class=&#34;&#34;&gt;There&amp;#x27;s a thin line between promotion and spamming. Don&amp;#x27;t cross it.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;ab168ee6-c523-4080-bf86-0a52375405eb&#34; class=&#34;&#34;&gt;Most people, especially people good at programming, neglect promotion and marketing.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;14917346-b201-4520-b86d-f51fbc9910fd&#34; class=&#34;&#34;&gt;Some say that you should spend as much time on marketing as you do on coding the product.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;d6b3f2d1-12ca-41cb-919a-df800165b3d9&#34; class=&#34;&#34;&gt;I don&amp;#x27;t know what the right amount is but it&amp;#x27;s not zero.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;98701648-45d8-4a18-90d3-b49edb3523ba&#34; class=&#34;&#34;&gt;&lt;strong&gt;Doing it badly&lt;/strong&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;e4d6f4b5-2c0f-49e9-be8a-222493f8bd02&#34; class=&#34;&#34;&gt;Not spending any time on promotion and marketing.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;70ccac72-4894-4d46-aa8b-4382a326f251&#34; class=&#34;&#34;&gt;Not writing blog posts related to your product.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;d1bf2167-e05f-4672-ac09-21a86a8da1f3&#34; class=&#34;&#34;&gt;Not tweeting, not trying to promote your writing on Hacker News or reddit or quora.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;23a05da6-353a-4709-883b-f6dbf33c2aa0&#34; class=&#34;&#34;&gt;If a tree falls in the forest and there&amp;#x27;s no one to see it, write a blog post about it and post it on Hacker News.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;fc500308-77b3-4733-b023-62a0b7ea6862&#34; class=&#34;&#34;&gt;Pieter is good at promotion and marketing&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;aaf83e72-c7cc-4383-b0fd-992bf1a3a483&#34; class=&#34;&#34;&gt;I&amp;#x27;m not sure if this can be thought but Pieter seems to have a natural gift for story telling and promotion.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;f1b9f493-07a1-4279-878a-d9b866610e13&#34; class=&#34;&#34;&gt;Consider those 2 examples:&#xA;  &lt;div class=&#34;indented&#34;&gt;&#xA;    &lt;ul id=&#34;12fa8694-78fd-4cf2-b9df-4c24a9aaf1c7&#34; class=&#34;bulleted-list&#34;&gt;&#xA;      &lt;li&gt;2014 &lt;a href=&#34;https://levels.io/12-startups-12-months/&#34;&gt;I&amp;#x27;m launching 12 Startups in 12 Months&lt;/a&gt;.&#xA;        &lt;ul id=&#34;9c3b025d-2eb2-48d6-a318-f55576287cae&#34; class=&#34;bulleted-list&#34;&gt;&#xA;          &lt;li&gt;&amp;quot;12 startups in 12 months&amp;quot; is a much more interesting story than &amp;quot;I&amp;#x27;m starting a startup&amp;quot;.&#xA;          &lt;/li&gt;&#xA;          &lt;li&gt;This is important because if you submit &amp;quot;I&amp;#x27;m starting a startup&amp;quot; blog post to Hacker News, it&amp;#x27;s unlikely to get traction. But &amp;quot;12 startups in 12 months&amp;quot; - it very well might.&#xA;          &lt;/li&gt;&#xA;          &lt;li&gt;People are attracted to new, never-been-done-before.&#xA;          &lt;/li&gt;&#xA;          &lt;li&gt;This trope is now over-used so you can&amp;#x27;t just copy it. But this is a big world and you don&amp;#x27;t have to be absolutely first and only. Pieter was inspired by &lt;a href=&#34;https://jenniferdewalt.com/&#34;&gt;180 websites in 180 days&lt;/a&gt; project.&#xA;          &lt;/li&gt;&#xA;        &lt;/ul&gt;&#xA;      &lt;/li&gt;&#xA;      &lt;li&gt;&lt;a href=&#34;https://levels.io/100-to-0-things/&#34;&gt;How I went from 100 to 0 things&lt;/a&gt;.&#xA;        &lt;ul id=&#34;fac251a2-8252-45c2-bcff-2a63faeaade3&#34; class=&#34;bulleted-list&#34;&gt;&#xA;          &lt;li&gt;He was robbed.&#xA;          &lt;/li&gt;&#xA;          &lt;li&gt;It happens to many people but most people don&amp;#x27;t blog about it and promote that blog on Hacker News&#xA;          &lt;/li&gt;&#xA;        &lt;/ul&gt;&#xA;      &lt;/li&gt;&#xA;    &lt;/ul&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;17232664-c3c2-4867-8874-00fca2189ee7&#34; class=&#34;&#34;&gt;It&amp;#x27;s more than a catchy headline, though.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;ac069ca0-8273-40ef-84c4-a65ac4b37ae7&#34; class=&#34;&#34;&gt;The content of those blog posts is good and goes beyond the obvious.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;77da8d03-d4c9-4e20-83b0-c6f988a2fe27&#34; class=&#34;&#34;&gt;Sharing a lousy story will not do you any good.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;b787a1d8-5624-4bb9-946c-a1e169980ec6&#34; class=&#34;&#34;&gt;&lt;strong&gt;Doing it badly&lt;/strong&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;93f068c6-cd0c-4b2e-a36e-37253f452f19&#34; class=&#34;&#34;&gt;Not spending any time on promotion and marketing.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;df97b201-c460-46a3-acd3-3dc933276c3f&#34; class=&#34;&#34;&gt;On the other hand, there are people who simply spam with low-effort, low-quality posts. That is not going to work either.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;e5f9b833-9f96-475d-abdf-b41092cdb5d8&#34; class=&#34;&#34;&gt;Pieter is productive&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;ad669e41-8928-4fd4-8cf3-5c1a5d2c35a2&#34; class=&#34;&#34;&gt;The amount of projects he shipped is impressive.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;1b484883-a47a-437d-89db-5e77afa2fd45&#34; class=&#34;&#34;&gt;The depth of some of his projects is impressive.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;4b6fb1a0-4e3f-423c-aab4-926a17416981&#34; class=&#34;&#34;&gt;It&amp;#x27;s not inhumanely great, though.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;851283ba-8ed8-4fa4-b650-5878cf9d5455&#34; class=&#34;&#34;&gt;When you consider he did it over 10 years, it&amp;#x27;s more than doable.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;afecee1e-9a86-4911-9106-a9e8ff8738fd&#34; class=&#34;&#34;&gt;The difference between &amp;quot;doable&amp;quot; and &amp;quot;done&amp;quot; is measured in hours spent in front of the monitor, writing code, testing code, learning new things, applying those learnings.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;2a78ac84-9c5d-4bee-9712-e26de48c53c3&#34; class=&#34;&#34;&gt;Most people just don&amp;#x27;t spend enough time and / or don&amp;#x27;t spend the time productively.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;274043a0-b682-4d1e-b414-04affb3a04a5&#34; class=&#34;&#34;&gt;&lt;strong&gt;Doing it badly&lt;/strong&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;e039881e-c4e5-4b2a-9b78-4c58fb0db718&#34; class=&#34;&#34;&gt;Writing a book is a time consuming activity.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;5716fc8b-bad6-4618-8c70-ac7e0c819a20&#34; class=&#34;&#34;&gt;Writing software is a time consuming activity.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;c46a893f-41fc-4922-b6de-f4e45fbefdec&#34; class=&#34;&#34;&gt;There&amp;#x27;s no around spending a lot of time on your business (occasional exception notwithstanding).&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;9766b5fc-8df2-40e6-b598-cb95e3890bbe&#34; class=&#34;&#34;&gt;Pieter learned programming by himself&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;558bd13b-d067-497d-b5e2-002ff2268437&#34; class=&#34;&#34;&gt;This is not about self-thought vs. going to college.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;5df0d9aa-ace7-4442-bb73-a5d0852b91bb&#34; class=&#34;&#34;&gt;This is about doing what&amp;#x27;s necessary.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;80073861-f1f1-4c54-9d32-24936127021d&#34; class=&#34;&#34;&gt;Pieter had ideas for software but didn&amp;#x27;t know how to program.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;932eb4e6-aba4-4a23-a440-e56c2398af6f&#34; class=&#34;&#34;&gt;For most people that&amp;#x27;s an insurmountable problem. It&amp;#x27;s learned helplessness.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;c2f61758-ce7e-403e-8ae6-38c554df4323&#34; class=&#34;&#34;&gt;All you need to learn programming is out there, for free. On YouTube, on blogs. &#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;44c6b56d-4574-41bf-adb1-a15afdc4f271&#34; class=&#34;&#34;&gt;You just need to start learning and keep going.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;34deb9bd-e0ff-4e21-934b-b66f13ad9b29&#34; class=&#34;&#34;&gt;You can &lt;a href=&#34;https://levels.io/diy/&#34;&gt;read what Pieter says&lt;/a&gt; about DYI ethos.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;1c3bf984-d756-4ba1-975b-fdadef4e3865&#34; class=&#34;&#34;&gt;&lt;strong&gt;Doing it badly&lt;/strong&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;d645b8ea-19ee-405e-b1ec-1a962f5da13d&#34; class=&#34;&#34;&gt;Trying to outsource programming to contractors or someone you find on Upwork, fantasizing about rising VC and hiring programmers, trying to find technical cofounder that will do all the work for 10% of the company.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;757f6f8b-0e90-4b1d-a9ef-30a6eebf0398&#34; class=&#34;&#34;&gt;Some people succeed by hiring programmers or contractors.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;d0564421-13ee-4d6a-a3c0-17e8c0406ef3&#34; class=&#34;&#34;&gt;Most people don&amp;#x27;t because most people suck at managing programmers even more than Winklevoss brothers.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;59d14ee7-dbb2-4ed5-a499-3dc409ea85c2&#34; class=&#34;&#34;&gt;If you are a broke college student, you just don&amp;#x27;t have the money to hire people.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;00114cb9-b6d3-4489-a4ea-b72545ef953b&#34; class=&#34;&#34;&gt;Pieter copies best practices&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;584f6bc8-cd82-4ef5-ae78-7ad67e7afec1&#34; class=&#34;&#34;&gt;We are not born being the best programmers, best marketers, best writers.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;8d422c88-d11c-4a46-82df-48021e132847&#34; class=&#34;&#34;&gt;There is no step-by-step guide to becoming the person that creates great products and markets them well.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;3cba0eb5-d021-4bd1-8dfe-953f72509a79&#34; class=&#34;&#34;&gt;But there are many businesses that do things well. &#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;6d95aa43-81ea-4faa-a9ef-5f5c7fb94db5&#34; class=&#34;&#34;&gt;You can deconstruct what they do well and copy it.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;3246cc82-cea4-49d1-9970-d7bd66c77481&#34; class=&#34;&#34;&gt;It requires an attentive, inquisitive mind.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;bbb2cedf-46b3-4f5f-8365-1c34b6792880&#34; class=&#34;&#34;&gt;Let&amp;#x27;s take &lt;a href=&#34;https://makebook.io/&#34;&gt;https://makebook.io/&lt;/a&gt; as an example. Do you see what Pieter did well there?&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;433fad43-5ae1-487f-82b4-f48405c1db07&#34; class=&#34;&#34;&gt;Go ahead, look some more. What did he do well on that website?&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;680c49f3-41de-451b-ae4a-02db4d00f51b&#34; class=&#34;&#34;&gt;Answer: social proof.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;34e92854-676b-4505-9a67-3273ff104991&#34; class=&#34;&#34;&gt;At the top: &amp;quot;Product Hunt&amp;#x27;s Book of the year&amp;quot;. At the bottom: twitter testimonials of people fawning over his book.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;5dc6ee14-b2fb-473d-b5f3-e707a05534c5&#34; class=&#34;&#34;&gt;Does your business use social proof to make people more likely to buy?&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;fb24402e-7dfe-4d10-bbee-8d32c76e73ad&#34; class=&#34;&#34;&gt;This is just one of many things that a business can do well.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;6c16cc9b-a165-4fee-b6b9-2b044fbb09fb&#34; class=&#34;&#34;&gt;For another example: go to &lt;a href=&#34;https://nomadlist.com/&#34;&gt;https://nomadlist.com/&lt;/a&gt; and try to sign up for Nomad List.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;a817461e-5620-4854-82b0-4f92120abf77&#34; class=&#34;&#34;&gt;It&amp;#x27;s a master class in how to convert a visitor into a customer.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;ef3a7f95-727d-4bfd-a713-d35308baec13&#34; class=&#34;&#34;&gt;Figuring it why it makes for a great conversion funnel is left as an exercise for the reader.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;915b0c9e-8f65-4147-8dd0-90c0108e25f0&#34; class=&#34;&#34;&gt;&lt;strong&gt;Doing it badly&lt;/strong&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;686595e1-78c2-4a66-8755-66267eb6920e&#34; class=&#34;&#34;&gt;Never asking yourself: how can I do X better? How can I make my business better?&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;e4183043-317e-4f54-a321-04a4b5357c35&#34; class=&#34;&#34;&gt;Not noticing the best practices that other businesses use.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;9119a814-cb15-4b38-8664-5c3392b1fdfe&#34; class=&#34;&#34;&gt;Not learning from blogs, books, tweet storms.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;9b117ea9-0eb8-409e-9eab-ebcb21abd4f4&#34; class=&#34;&#34;&gt;Not applying the things you learn to your business.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;f1524ec2-fe42-47d2-85ca-a5b6b8b53b30&#34; class=&#34;&#34;&gt;Pieter is not a technologist&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;a6b74164-79ae-4e94-a338-ad8d020dfb3c&#34; class=&#34;&#34;&gt;It&amp;#x27;s counter-intuitive that not being a technologist i.e. someone impressed by and interested in new technologies, can be an advantage when running a software business.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;649a71c3-2622-4fd4-b325-736390f21ceb&#34; class=&#34;&#34;&gt;He build his $1.5 million/year empire on PHP, sqlite, and a single VPS server.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;0d2150d7-c463-42b2-a469-2d956c74ea9b&#34; class=&#34;&#34;&gt;Those are not technologies that will impress anyone.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;955fbec0-ebc1-4c1d-a3f8-db7c224b3e1e&#34; class=&#34;&#34;&gt;PHP is 27 years old. It might be older than you. It sure isn&amp;#x27;t Rust or Swift.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;bd431c20-d29d-43b4-adf9-02b678c3423b&#34; class=&#34;&#34;&gt;SQLite is 21 years old and everyone knows that a serious web service needs to use a serious database, like PostgreSQL&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;24a33cd9-02e9-49a6-b91d-a70903d52c59&#34; class=&#34;&#34;&gt;But they get things done. More precisely: Pieter gets things done using them.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;df85647b-3c84-4bf8-a8a8-c3ce0ed97d8e&#34; class=&#34;&#34;&gt;&lt;strong&gt;Doing it badly&lt;/strong&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;cc78d601-5bab-4a46-ae12-00a0dc39d9da&#34; class=&#34;&#34;&gt;I&amp;#x27;m a technologies so trust me, I know the siren song of fancy new languages, Kubernetes clusters, latest web frameworks.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;7b044d60-e0af-4936-b8a8-8ad5291c1b92&#34; class=&#34;&#34;&gt;I&amp;#x27;m doing it badly. I&amp;#x27;m trying to do it less badly.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;2c203818-c696-43ca-86e0-1c61b0a14dc7&#34; class=&#34;&#34;&gt;When it comes to being productive, using a mature tool that you know well beats chasing the latest thing.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;86945e3d-950e-4fb3-9614-43b1c5d215d8&#34; class=&#34;&#34;&gt;For me it&amp;#x27;s Go on the backend and Svelte on the front-end but as Pieter shows, you can be very prolific with a very old technology.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;d29514a0-d4df-495c-bdd3-74b0be3cd15f&#34; class=&#34;&#34;&gt;Pieter surfs the high waves&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;53f28297-6cf7-4f64-b2f8-d7af25e3dfb7&#34; class=&#34;&#34;&gt;YouTube analytics in 2013. &#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;1237f149-27de-4206-8ab3-08c49fce5993&#34; class=&#34;&#34;&gt;Slack channel in 2014.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;181192cd-0051-403a-88ef-1ea64e96ee3a&#34; class=&#34;&#34;&gt;Virtual Reality in 2016.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;84eab0ba-e896-426b-9e6d-fb6f42cd8421&#34; class=&#34;&#34;&gt;Telegram bot in 2015.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;6a809ad2-e019-4324-a258-b6f3f4955b98&#34; class=&#34;&#34;&gt;Being a nomad in 2015.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;0099a6cd-383b-469a-8fb2-7d337180e2fa&#34; class=&#34;&#34;&gt;QR Menu creator in 2020.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;0c746dc7-fce7-410f-9aee-457939948dad&#34; class=&#34;&#34;&gt;Bitcoin / NFT in 2021.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;57614fa4-b259-473f-ad8d-bbb609ffc5ed&#34; class=&#34;&#34;&gt;The guy is at the forefront of trends.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;3f23db0d-95c0-4ca0-a7c8-5ecaad3fb145&#34; class=&#34;&#34;&gt;This doesn&amp;#x27;t necessarily make or break his businesses but it&amp;#x27;s easier to promote novel things.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;b18e0ed3-1598-484b-a55a-88442169137e&#34; class=&#34;&#34;&gt;This is not blind chasing of fads.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;2a80f036-c614-4f59-a399-693a7f0f3feb&#34; class=&#34;&#34;&gt;He doesn&amp;#x27;t have a podcast, a TikTok, or hangs out on Clubhouse.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;1bb96e4b-2461-47da-9015-b51980b728db&#34; class=&#34;&#34;&gt;He did nomadic lifestyle years before it was a trend.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;32391171-7357-4136-a918-e6e5c2ad7b80&#34; class=&#34;&#34;&gt;Don&amp;#x27;t ignore trends but do apply your personal and busines filter.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;62676d39-0100-48ea-bc9d-f3ffbbe34114&#34; class=&#34;&#34;&gt;It&amp;#x27;s also different than being a technologist.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;7bcc0fbc-72a5-481c-8024-6e7b34480de4&#34; class=&#34;&#34;&gt;He didn&amp;#x27;t rewrite his website on blockchain but he did notice that Bitcoin is a trendy thing and he did a project that is related to Bitcoin and provides value related to Bitcoin.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;d2c2151b-46b5-4dfc-b77d-8f75db2da391&#34; class=&#34;&#34;&gt;&lt;strong&gt;Doing it badly&lt;/strong&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;3a1695af-1ece-4d43-a3b3-6055642d7fd6&#34; class=&#34;&#34;&gt;Not paying attention to new trends.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;88d9b45f-7e20-4c80-aece-ac75dd42c7ba&#34; class=&#34;&#34;&gt;Not trying to figure out what product can you build to take advantage of the new trend.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;c3e330fd-310d-4a3f-9e93-60b8589929bd&#34; class=&#34;&#34;&gt;Not being selective about which trends to exploit.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;fe7bcc33-5b55-4651-8ac2-bbcf980132b2&#34; class=&#34;&#34;&gt;Pieter is humble&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;d16b7db0-b7f1-4d56-a650-818b37c242bc&#34; class=&#34;&#34;&gt;The first thing he said in &lt;a href=&#34;https://news.ycombinator.com/item?id=28874985&#34;&gt;this comment&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;&lt;blockquote id=&#34;5266c3e4-7a8c-47b4-ab64-20be85faf7d7&#34; class=&#34;&#34;&gt;All of this is possible because I&amp;#x27;ve been a HN reader since 2010 and was inspired by all of you and especially @patio11 on here to bootstrap my own things and do it VERY publicly.&#xA;&lt;/blockquote&gt;&#xA;  &lt;div id=&#34;87840d59-36e1-4e1f-a44d-3a076981e921&#34; class=&#34;&#34;&gt;This might not impact his business but it does impact how people perceive him.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;005d6f80-34ae-44da-a7a4-3d1890c13891&#34; class=&#34;&#34;&gt;&lt;strong&gt;Doing it badly&lt;/strong&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;44031749-c339-4be4-9bcc-43bc15907b3e&#34; class=&#34;&#34;&gt;Being an arrogant prick that people dislike and root against.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;367c7367-b62d-459a-acee-2edd6e83962f&#34; class=&#34;&#34;&gt;Doing Things Right bias&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;eb275198-a8ed-486b-9a15-17fb397be389&#34; class=&#34;&#34;&gt;Survivorship bias is for losers.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;356250e0-c385-45f3-93fd-b5fc41b392fc&#34; class=&#34;&#34;&gt;Doing Things Right bias is for winners.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;b120a30b-b660-4ebf-9f56-eb1356bb1ad2&#34; class=&#34;&#34;&gt;So put that coffee down until you start &lt;strong&gt;Doing Things Right&lt;/strong&gt;.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;307a6b23-c36e-42a4-ac5a-df0ef57f75ed&#34; class=&#34;&#34;&gt;Learning from Pieter&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;3cfbd387-0972-4acc-ad88-081755b97338&#34; class=&#34;&#34;&gt;Do you want to create successful projects? You can learn a lot from Pieter:&#xA;  &lt;div class=&#34;indented&#34;&gt;&#xA;    &lt;ul id=&#34;5313e498-786c-46ae-b1e7-008aee2a3d56&#34; class=&#34;bulleted-list&#34;&gt;&#xA;      &lt;li&gt;watch his &lt;a href=&#34;https://www.youtube.com/watch?v=6reLWfFNer0&#34;&gt;How to build a startup without funding&lt;/a&gt; talk&#xA;      &lt;/li&gt;&#xA;      &lt;li&gt;read his &lt;a href=&#34;https://levels.io/&#34;&gt;blog posts&lt;/a&gt;&#xA;      &lt;/li&gt;&#xA;      &lt;li&gt;read &lt;a href=&#34;https://makebook.io/&#34;&gt;his book&lt;/a&gt;&#xA;      &lt;/li&gt;&#xA;      &lt;li&gt;watch &lt;a href=&#34;https://levels.io/ama/&#34;&gt;4 hour AMA&lt;/a&gt;&#xA;      &lt;/li&gt;&#xA;      &lt;li&gt;listen to &lt;a href=&#34;https://levels.io/product-hunt-radio/&#34;&gt;an interview he gave&lt;/a&gt;&#xA;      &lt;/li&gt;&#xA;    &lt;/ul&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;7c0d0cf3-a793-4343-b9b4-5ee5a714f100&#34; class=&#34;&#34;&gt;Don&amp;#x27;t just read it passively.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;65bd9831-b36e-4f85-bae9-3a4af494c0b8&#34; class=&#34;&#34;&gt;You&amp;#x27;re a business detective. He has a successful business. Your job is to figure out all the things he did right, not just the things he&amp;#x27;s telling you.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;6bd8b30a-c22d-4dc4-b09f-b6669decbcd3&#34; class=&#34;&#34;&gt;You need to figure out that he&amp;#x27;s using social proof even if he never talks about using social proof.&#xA;  &lt;/div&gt;&#xA;&lt;/div&gt;</content>
  </entry>
  <entry>
   <title>Lessons learned from 15 years of SumatraPDF, an open source Windows app</title>
   <link href="https://blog.kowalczyk.info/article/2f72237a4230410a888acbfce3dc0864/lessons-learned-from-15-years-of-sumatrapdf-an-open-source-windows-app.html" rel="alternate"></link>
   <updated>2021-07-25T00:00:00Z</updated>
   <id>tag:blog.kowalczyk.info,2021-07-25:/article/2f72237a4230410a888acbfce3dc0864/lessons-learned-from-15-years-of-sumatrapdf-an-open-source-windows-app.html</id>
   <content type="html">&lt;p&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;notion-page&#34; id=&#34;2f72237a-4230-410a-888a-cbfce3dc0864&#34;&gt;&#xA;  &lt;div id=&#34;f95b0f0d-d10d-4b9a-9560-b059ddd6b30f&#34; class=&#34;&#34;&gt;I released first version of &lt;a href=&#34;https://www.sumatrapdfreader.org/free-pdf-reader&#34;&gt;SumatraPDF&lt;/a&gt; in 2006. That&amp;#x27;s 15 years ago which seems like a good time for a retrospective.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;f14f9454-6c60-44a1-91b6-ee1600f252c0&#34; class=&#34;&#34;&gt;The app&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;f44c4b7d-f190-497b-bd52-6c0afdb8d2a7&#34; class=&#34;&#34;&gt;SumatraPDF is a multi-format (PDF, ePub, Mobi, comic book, DjVu, XPS, CHM) viewer for Windows and currently looks like this:&#xA;  &lt;/div&gt;&#xA;&lt;img class=&#34;blog-img&#34; src=&#34;/img/983eae4f6a4cbd89dd91ae7df84663081bbda06d.png&#34;&gt;&#xA;  &lt;h2 id=&#34;72d4309c-2183-45d7-a698-43ad3d13aeac&#34; class=&#34;&#34;&gt;The code&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;db306b3b-22b6-422c-abc8-28ba38a59673&#34; class=&#34;&#34;&gt;SumatraPDF is an open-source document reader for Windows. It started as a PDF reader, hence the name. Over time I&amp;#x27;ve added for e-book formats (epub, mobi), comic books (cbz, cbr), DjVu, XPS, image formats etc.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;473d5437-237f-4e43-87ec-18433de53c84&#34; class=&#34;&#34;&gt;It&amp;#x27;s about 127k lines of C++ (not counting libraries written by others).&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;892eaf43-9d22-46bb-a28b-50188aa76826&#34; class=&#34;&#34;&gt;It&amp;#x27;s written against Win32 API, not using GUI abstraction libraries like Qt. This contributes to making it as small and fast as possible.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;782b22a5-51da-47dd-b61f-394bf5e64ec3&#34; class=&#34;&#34;&gt;Almost all of it was written by 2 people, with occasional contributions from others.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;71acbb14-575f-4b00-af05-2cdbe29f42f1&#34; class=&#34;&#34;&gt;The amount of code written is actually higher. It is the nature of long running code bases that the code gets written and re-written. We delete, add, change.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;dc18303b-b0bd-4e55-b4be-22fe66f31091&#34; class=&#34;&#34;&gt;It&amp;#x27;s a side project, done after hours, not a full time effort. How does a daily grind of working on an app looks like?&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;328f4d0a-bda5-46d8-b91e-73ec7cb84d35&#34; class=&#34;&#34;&gt;It looks like this:&#xA;  &lt;/div&gt;&#xA;&lt;img class=&#34;blog-img&#34; src=&#34;/img/251a5dbb72a41441d87187a4379b4b6532ff5506.png&#34;&gt;&#xA;  &lt;div id=&#34;a9d96464-4a19-4176-9ba1-21f19a4281aa&#34; class=&#34;&#34;&gt;You can also take a peek at &lt;a href=&#34;https://github.com/sumatrapdfreader/sumatrapdf/blob/master/src/docs/log.txt&#34;&gt;my dev log&lt;/a&gt;. I&amp;#x27;ve only started it a year ago so only covers 1 year out of 15.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;9a283ffc-9feb-47d9-9e98-494a7eb65a37&#34; class=&#34;&#34;&gt;Why I created SumatraPDF&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;81f7759c-f0e2-4594-b79f-eb5aebc61f51&#34; class=&#34;&#34;&gt;SumatraPDF is what I call an accidental success.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;dcbd79e8-2f16-4d71-a85d-8186465e5a8a&#34; class=&#34;&#34;&gt;I never wanted to write a PDF reader for Windows. &#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;064737bd-1d34-43d0-a646-d3666fe29798&#34; class=&#34;&#34;&gt;In 2006 I was working at Palm and one of my job duties was writing a PDF reader for &lt;a href=&#34;https://en.wikipedia.org/wiki/Palm_Foleo&#34;&gt;Foleo&lt;/a&gt;, an ARM and Linux powered mini laptop. You never heard of Foleo because it was cancelled weeks before launch for reasons I&amp;#x27;m not privy to.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;46d057fa-9eb7-4ee9-a4ba-9f3d57a847b4&#34; class=&#34;&#34;&gt;At the time I didn&amp;#x27;t know that PDF is popular but Palm management did which is why they decided that PDF reader is a must have application. I ended up being the (sole) dev on the project.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;cd107f0c-3212-4d06-95cf-f2e0dceb6b6a&#34; class=&#34;&#34;&gt;Writing a PDF rendering library is a multi-year effort. We didn&amp;#x27;t have years so I used &lt;a href=&#34;https://poppler.freedesktop.org/&#34;&gt;Poppler&lt;/a&gt; open-source library.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;29ad4295-1ce3-471d-b330-0a08fc69ae15&#34; class=&#34;&#34;&gt;My job was to write a basic PDF viewer that used Poppler to render PDF pages into a bitmap in memory and blit those bitmap on screen.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;15bff678-a73d-42a3-87a1-2b8abf6bebd2&#34; class=&#34;&#34;&gt;PDF is a complex format and rendering of some PDFs is slow. I wanted to improve the speed because Jeff Bezos told me that speed is something that customers will always care about.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;062b2b16-3f7b-4166-9b4c-b751f066f544&#34; class=&#34;&#34;&gt;Accidental app&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;3cfe68ee-1712-4de0-b46f-014fa1e2d2a5&#34; class=&#34;&#34;&gt;The way to improve speed is to profile the code and look at the result. &#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;eb7bdf13-22f9-4c42-812b-7826e743849f&#34; class=&#34;&#34;&gt;Unfortunately, the toolchain for unreleased ARM hardware wasn&amp;#x27;t very good. Forget about a profiler, kid, be grateful you have a C++ compiler and don&amp;#x27;t have to enter assembly by typing hex, like Steve Wozniak.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;fadec487-29c4-4693-b6f6-1e3492ebe20d&#34; class=&#34;&#34;&gt;Windows had decent profilers, so I compiled Poppler for Windows.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;1e93d322-4335-471b-872e-ac4cd67adb88&#34; class=&#34;&#34;&gt;Once I had the library working on Windows, I wrote simplest GUI app that would show the pages and allow navigating between pages.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;41f214f9-777f-4c58-91ad-beb2e50712e2&#34; class=&#34;&#34;&gt;What do you know: I had a simple PDF reader for Windows. &#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;864a5b12-7844-45de-a762-b4709b7a8aff&#34; class=&#34;&#34;&gt;I released it on my website. It couldn&amp;#x27;t do much so I tagged it as version 0.1.&#xA;  &lt;/div&gt;&#xA;&lt;blockquote id=&#34;01543cae-8dec-4828-adac-cd2e8546a162&#34; class=&#34;&#34;&gt;If you&amp;#x27;re not embarrassed by your app then you&amp;#x27;ve waited too long to release it&#xA;&lt;/blockquote&gt;&#xA;  &lt;div id=&#34;e03ce731-a7a9-4ff8-9218-ff59c1b01aaf&#34; class=&#34;&#34;&gt;I didn&amp;#x27;t come up with this nugget of wisdom but I agree with it. &#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;7e65ab7c-8264-484c-9860-279d3edc113d&#34; class=&#34;&#34;&gt;Getting early users, learning what features they want the most beats toiling for months or years and implementing lots of features before you know anyone even cares.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;13d06b9c-a635-4d02-a21f-7ff329b23139&#34; class=&#34;&#34;&gt;Profiling, performance optimization and contributing to open source&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;3425bf9e-f419-4f60-b95b-4109d90ab113&#34; class=&#34;&#34;&gt;Back to profiling: my plan worked.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;845fed6c-64fc-4b78-b94a-9f248c719ae3&#34; class=&#34;&#34;&gt;I profiled the documents that took the longest to render and made a few surprisingly simple and surprisingly effective optimizations.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;05725edb-ab08-44e3-998b-9a1b4ae81958&#34; class=&#34;&#34;&gt;If memory servers, 2 optimizations had the biggest effect:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;c3160200-6701-4bc5-9b64-96c0f4ce4885&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;optimizing string class to use what is know as &amp;quot;small string optimization&amp;quot; i.e. adding a small buffer inside string class to hold small strings inline (as opposed to always allocating memory for the string). Strings were used frequently and most of them were small&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;fixing byte-at-a-time i/o by converting it to bulk reads. The way the code was structured in some code-paths it would do a virtual C++ call and a call to C read() function for each byte. Those are extremely cheap but not when you do it 5 million times&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;div id=&#34;2035b5a8-5951-48c9-ac71-ec2305aaaad5&#34; class=&#34;&#34;&gt;As a good boy I did submit my changes to Poppler.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;7accf5ad-0eb0-4128-8db4-9fb7dd5424a7&#34; class=&#34;&#34;&gt;As is my experience with contributing to open source projects, it was more of a miss than a hit.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;06815b7e-7f98-4882-a8a9-30b878aabebf&#34; class=&#34;&#34;&gt;Yes, I got 13 commits in but the project wasn&amp;#x27;t very active and the maintainers weren&amp;#x27;t eager to accept anything beyond small changes. Forget any major refactors.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;57ffc482-7526-4780-9498-c07d94a11cdf&#34; class=&#34;&#34;&gt;I&amp;#x27;m not one to voluntarily bash my head against the wall so I stopped trying.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;8bf5be51-9625-4e9d-9b10-73cbc4d93f54&#34; class=&#34;&#34;&gt;(As you can see, I&amp;#x27;m a fantastic team player).&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;16cb5f36-b1c4-471c-8bb1-27029d8af9af&#34; class=&#34;&#34;&gt;Code quality&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;44696f0f-ceca-4662-b6d3-463d179b5c59&#34; class=&#34;&#34;&gt;I want it and you should want it to.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;80a7ce63-6339-488d-9d62-8d6d8e37f6de&#34; class=&#34;&#34;&gt;How to maintain high code quality while working mostly solo, with no-one doing code reviews, no dedicated QA team?&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;69d3e3c2-4e22-4a5f-aeec-8cafa9b04343&#34; class=&#34;&#34;&gt;Here&amp;#x27;s how:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;d471f5af-aca0-4556-ac8d-35616d8a7472&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;test the code yourself. Step through newly added code in the debugger, verify the newly added functionality works as expected and in general use the app a lot&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;automated crash reporting. Unfortunately it&amp;#x27;s a pain to build but this is single most important thing you can do to improve quality of your software. Briefly: setup exception handlers to catch crashes in the app, in crash handler download symbols from the server to get readable callstack, create a crash report that includes callstacks of all thread, program and os information, log and submit that to a server. On the server, process those files and generate web pages for easy viewing of the crashes. Like I said: it&amp;#x27;s a pain to build. Once you have crashes, look at them occasionally and try to figure out what went wrong and fix it&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;code&gt;assert()&lt;/code&gt;. asserts are well established practice in C++ code: an additional code only executed in debug builds that verifies some conditions are true. If they&amp;#x27;re not, something went wrong and you should investigate. I wrote wrote my own &lt;code&gt;assert&lt;/code&gt;-like function which I enable in non-debug pre-release builds so that I automatically get bug reports from people hitting those conditions. Trust me: there&amp;#x27;s no amount of testing you can do yourself that would match all the different things that a thousand people will do just by using the app.&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;logging. When investigating issues it helps to know what sequence of events led to a crash. My tiny logging module logs to a block of memory. That gets sent along with crash report. I also have an option to log to a file and I&amp;#x27;ve recently added logging to a separate logging app via named pipe. This is perfect because most of the time I don&amp;#x27;t care about the logs but when I do, I don&amp;#x27;t want to restart the app to enable logging. With separate logging app, SumatraPDF is logging all the time and when it detects that logging app is running, it&amp;#x27;ll also log to it. Implementation was trivial: logging app creates a named pipe, logger opens the pipe (like a file) and if open succeeds, it means the logger app is running and it reads the logs we write to the pipe&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;static code analysis: max level of warnings in C++ compiler, make warnings into errors, Visual Studio&amp;#x27;s `/analyze&amp;#x27; option, cppcheck, clang-tidy, GitHub&amp;#x27;s CodeQL. Run those occasionally and fix the errors and warnings&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;ASAN (&lt;a href=&#34;https://clang.llvm.org/docs/AddressSanitizer.html&#34;&gt;Address Sanitizer&lt;/a&gt;), is fantastic. Was added in some point release of Visual Studio 2019. At a very small performance cost it can detect if you over-write memory or try to read uninitialized memory. I have a configuration with ASAN enabled. It&amp;#x27;s fast enough to be used as a regular build.&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;stress testing. Sumatra&amp;#x27;s job is mostly to render complex document format. There often are crashes in specific files due to complexity of the formats. To ensure lack of crashes I wrote a stress test code that reads and renders all files in a directory. I typically run it before a release on a large collection of test files I amassed over the years&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;unit testing. I don&amp;#x27;t have a lot of them, they&amp;#x27;re mostly for testing edge cases for low-level functionality like string formatting. They occasionally find bugs.&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;memory leaks. It&amp;#x27;s surprisingly hard to find an easy to use memory leak detection tool. I&amp;#x27;m working on a very simple built-in leak detector. In the meantime I&amp;#x27;m using &lt;a href=&#34;https://drmemory.org/&#34;&gt;Dr. Memory&lt;/a&gt;. It works but it&amp;#x27;s super slow.&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;h2 id=&#34;4fe2c16b-c275-4a33-992e-a2d78d1f6ac6&#34; class=&#34;&#34;&gt;Frequent releases&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;54992dd3-ddd6-4376-ba11-5a9d0d55dc33&#34; class=&#34;&#34;&gt;When you don&amp;#x27;t have many features, improving the app is fast and easy. It doesn&amp;#x27;t take much effort to implement &amp;quot;Go to&amp;quot; dialog (implemented in v 0.2).&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;4ad571a1-1280-4883-8b26-5416030698e5&#34; class=&#34;&#34;&gt;On one hand I don&amp;#x27;t want to release too often but I also do want the users to get new features as quickly as possible.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;c09a2eb6-6e59-4d1b-9bc8-366329a6759b&#34; class=&#34;&#34;&gt;My policy of new releases is: release when there&amp;#x27;s at least one notable, user-visible improvement.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;ecfdd61c-1d62-494d-91b0-685363bc6e7e&#34; class=&#34;&#34;&gt;Web apps take it to the extreme (some companies deploy to production multiple times a day).&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;279dbad6-200b-4935-8302-c83c97b59f5e&#34; class=&#34;&#34;&gt;In desktop software it&amp;#x27;s a bit more involved and I had to build functionality to make it easy i.e. add a check for new releases, write an installer that can update the program.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;911db114-dc6f-4837-b4d9-5829fdf295fd&#34; class=&#34;&#34;&gt;BTW: I mean &amp;quot;frequent in proportion to amount of new code written&amp;quot;. SumatraPDF releases are not frequent in absolute terms but frequent if you consider that it&amp;#x27;s a part-time, after hours project.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;4cb762ea-5006-4045-9523-17d65acfe328&#34; class=&#34;&#34;&gt;Treat open source projects like commercial software&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;89381882-2ae9-4662-b501-1367f8140b88&#34; class=&#34;&#34;&gt;Majority of open source projects probably don&amp;#x27;t fall into this category, but if you want your open source to be as successful as possible, act as if it was a commercial product from a software company.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;7a8ccb51-8515-4123-bd73-357c0ed8b142&#34; class=&#34;&#34;&gt;What does it mean in practice?&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;aa57bab1-81b1-486c-ad16-e852dd5ad6d2&#34; class=&#34;&#34;&gt;From day one I created a website for the app. It had screenshots, it had documentation, it was easy to download and install. Granted, a kind soul on Reddit called it &amp;quot;a website made by a 6-year old&amp;quot;. The lesson here is two-fold:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;c9cd8864-16ff-4ab3-9379-3e5296f6bbc7&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;ignore haters and assholes&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;a website built by a 6-year old is better than no website. It doesn&amp;#x27;t have to be pretty, it has to be functional&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;div id=&#34;3a5986f1-c5c0-434d-8c64-feaf93a5717f&#34; class=&#34;&#34;&gt;I did basic SEO. Nothing beyond Google&amp;#x27;s &amp;quot;SEO 101&amp;quot; docs: just pay attention to URLs, put the right meta-data, use the right keywords.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;28ab74cf-af24-49d9-b0ab-acaa6f9934c6&#34; class=&#34;&#34;&gt;I had a forum for users to ask questions, submit feature requests and occasionally support each other.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;74e60447-ca4c-477b-83c2-25ca2bc3a326&#34; class=&#34;&#34;&gt;I made the installation process as easy as possible.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;9b2c7dc5-32f5-413b-a45b-03f7d990fe31&#34; class=&#34;&#34;&gt;Everything that is a good idea for promoting commercial software is also a good idea for open source project.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;f4772aab-4102-468d-9091-6c1a29cc0115&#34; class=&#34;&#34;&gt;Switching the engine while the car is running&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;0df8bdfd-f856-411a-a45f-320050943963&#34; class=&#34;&#34;&gt;At some point I decided to switch from Poppler to &lt;a href=&#34;https://mupdf.com/&#34;&gt;mupdf&lt;/a&gt; because mupdf was better and actively maintained.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;705b99a5-023c-44a9-8ba0-44dcdcdf5145&#34; class=&#34;&#34;&gt;Changing the app to use completely different library is not something you can do in an afternoon.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;82746a65-a8af-44cb-9896-6a15609de5a4&#34; class=&#34;&#34;&gt;It&amp;#x27;s demoralizing to work long time on code that doesn&amp;#x27;t even compile.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;cccbc96f-3898-4386-bccc-fd8dd4ec1c02&#34; class=&#34;&#34;&gt;To keep things compiling while also working towards supporting alternative rendering engine I developed an abstraction for the rendering engine.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;8080fa7d-2472-441b-9b80-179da814f5d9&#34; class=&#34;&#34;&gt;The engine would provide the functionality the UI needed: getting number of pages in the document, sizes of each page (to calculate layout), rendering a page as a bitmap etc.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;2738caf8-f91f-43d0-a311-b10e2b7bf8b9&#34; class=&#34;&#34;&gt;I&amp;#x27;m much less enthusiastic about abstractions than most programmers (at least those who like to opine on Hacker News) but in this case it served me well.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;4a6a8a92-092d-4651-84aa-68d8c0af8622&#34; class=&#34;&#34;&gt;I was able to incrementally convert program form using Poppler API to using Poppler via engine abstraction to using mupdf via Engine abstraction.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;606dbd8f-4790-4525-b623-b25ad7df5d07&#34; class=&#34;&#34;&gt;For a while I supported both engines at the same time but eventually I switched to just mupdf, to keep the app small.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;ce712683-d417-417a-a892-0d84508dc0a5&#34; class=&#34;&#34;&gt;This opened the door for supporting other formats via the same abstraction.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;f2ca367f-280d-4855-88ab-06a5461e2cb8&#34; class=&#34;&#34;&gt;Simplicity vs. customizability&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;0776a907-4c1a-42b4-bf7a-547f3c7cc4c2&#34; class=&#34;&#34;&gt;Simplicity sells.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;8f0760fa-d6ba-42bc-a151-7d4b874240c0&#34; class=&#34;&#34;&gt;I learned that from the history of Mozilla Firefox.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;bdd10519-cda3-47a8-8f68-1bb2aad9e397&#34; class=&#34;&#34;&gt;Before Firefox there was Netscape Navigator. It was a beast of an app, combining web browser with e-mail client.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;ec17be4a-c0fc-4966-b60f-b6a15e4862a6&#34; class=&#34;&#34;&gt;Netscape couldn&amp;#x27;t help themselves and was adding features upon features, leading to very complex UI.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;c329846f-b71e-4019-8d59-b7cf177fd51c&#34; class=&#34;&#34;&gt;A small group of renegades within Mozilla forked the code and focused on simple UI.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;ff61b278-71e9-413d-9cdd-d564e8959a1d&#34; class=&#34;&#34;&gt;Simple Firefox was much more popular than the complex Navigator and eventually ate it completely.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;b0060aea-8fad-45a0-8398-175aad638c24&#34; class=&#34;&#34;&gt;From the beginning my goal was to keep the UI of SumatraPDF as simple as possible. An 80/20 app: 80% of functionality with 20% of the UI.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;addcf01a-5fd4-4d2e-9ea0-d9de67d6ced6&#34; class=&#34;&#34;&gt;This requires resolve. I constantly get requests to add more icons to the toolbar and I constantly have to say &amp;quot;no&amp;quot; because adding 2 more icons to the toolbar to satisfy 10% of users makes the app slightly worse for 100% of the users.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;0ab5d538-c2e5-450a-90e5-b6781b712d23&#34; class=&#34;&#34;&gt;Another trap is a siren song of additional settings. Sometimes people suggest that instead of doing X, the program should do Y. Not willing to remove X, they suggest adding a new UI setting &amp;quot;[ ] Do Y instead of X&amp;quot;.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;16746eb8-d26a-4dee-951d-540312c5947b&#34; class=&#34;&#34;&gt;Having settings dialog with 100 settings is not a good solution. It makes the app worse for everyone due to overwhelming them with choices and hiding important options in a sea of non-important options.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;c266e2b6-4965-47b4-8015-034715e19263&#34; class=&#34;&#34;&gt;Not to mention that every conditional behavior requires more code, more potential bugs and more testing.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;59ebf698-b7e6-4bbc-b5d7-fd43f6a3a03e&#34; class=&#34;&#34;&gt;That being said, I also believe customizability is important. I believe that a big reason for Winamp being such a dominant music player (at the time) was its ability to skin the whole UI.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;13ac341f-d628-4fb0-be65-37084b49a353&#34; class=&#34;&#34;&gt;Some advanced features might only be used by 20% of users but those users are most likely power users that will evangelize the app more than the other 80% of the users.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;4a29792e-b1df-409d-ac2f-021a7c8bda81&#34; class=&#34;&#34;&gt;My solution to UI simplicity vs. customizability: advanced settings file.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;be22466c-19e7-4ad7-9838-e261ed70e6aa&#34; class=&#34;&#34;&gt;I designed a simple, human readable (and human writeable) textual format for &lt;a href=&#34;https://www.sumatrapdfreader.org/settings/settings&#34;&gt;advanced settings&lt;/a&gt;. Think JSON, but better.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;3fcb5d38-29b2-443c-89d5-03421f931810&#34; class=&#34;&#34;&gt;I didn&amp;#x27;t bother to write UI for changing those advanced settings. I just launch notepad.exe with the file. When user changes the settings and saves the file, I reload it and apply the changes.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;25a53576-5322-4848-b859-47ae59927c9d&#34; class=&#34;&#34;&gt;Be water, my friend&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;b990d4b5-8747-4d2c-9d20-a7ea2bed10b4&#34; class=&#34;&#34;&gt;Change is the only constant. We must adapt to the changes in the world.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;843a7743-3ce6-435c-be91-98f0699c6163&#34; class=&#34;&#34;&gt;I can&amp;#x27;t believe how many popular projects still use craptastic Sourceforge for source repository or mailing list.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;b3154919-dc11-4f92-bc28-c2bccc4e2b69&#34; class=&#34;&#34;&gt;Actually, I can believe: changing things takes effort and the path of least resistance is to do nothing.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;a3ce2200-0ae8-42a0-aa7f-30bd111d564c&#34; class=&#34;&#34;&gt;I started with Sourceforge, switched to code.google.com and then to github.com.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;826325c5-e02f-49f7-9b5f-b0620f51a371&#34; class=&#34;&#34;&gt;I switched forum software three times.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;2e54ab18-a6f8-4b68-af90-e4e1b26997df&#34; class=&#34;&#34;&gt;I&amp;#x27;ve added a browser plugin and then removed it when browsers stopped supporting such plugins.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;88bf434c-e09f-477d-aa6e-32876568e5fc&#34; class=&#34;&#34;&gt;I changed the format for storing preferences from binary to human readable text.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;f0bb2dd0-619d-41ef-8181-423fb3180310&#34; class=&#34;&#34;&gt;Windows XP went from being the OS used by majority of users to no longer being supported (long after Microsoft stopped supporting it).&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;89e69d53-0bb5-4afa-bb9a-433686f7e17e&#34; class=&#34;&#34;&gt;At first I only had 32-bit build and now I have both but emphasize 64-bit builds.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;037fe291-5491-4e0c-94c7-3bf69db1b9aa&#34; class=&#34;&#34;&gt;Think outside of the box&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;6dee430c-0248-4554-b97f-d4c987341dad&#34; class=&#34;&#34;&gt;Thinking outside of the box is hard because the box is invisible.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;101d0c71-54d0-4599-825a-eb0940be86bb&#34; class=&#34;&#34;&gt;SumatraPDF wasn&amp;#x27;t the first PDF reader application ever written.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;7f47b164-34d7-4b42-a3d9-4a4fbd01a585&#34; class=&#34;&#34;&gt;But most PDF readers do not become multi-format readers.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;6300dd8a-fed3-44e8-9e9e-c0679c5efe91&#34; class=&#34;&#34;&gt;In hindsight it&amp;#x27;s an obvious idea to support as many document formats as possible but it took me 5 years to realize it.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;1b911eea-3608-4664-9d7d-0e144cdf56b8&#34; class=&#34;&#34;&gt;Most readers are still single format and I do believe being multi-format helped SumatraPDF become popular.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;0ca88977-408c-4a22-90ac-5194c9f6ac5f&#34; class=&#34;&#34;&gt;I can&amp;#x27;t say it&amp;#x27;s totally unique idea. There were multi-format image viewers long before SumatraPDF and I probably was inspired by them.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;78d5c807-4bb8-4601-98bf-5e635b5d1ce5&#34; class=&#34;&#34;&gt;Small and fast - pick both&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;0cad095f-d412-4133-a4bb-86534c4dbf2f&#34; class=&#34;&#34;&gt;By today&amp;#x27;s standards SumatraPDF is tiny (installer smaller than 10 MB) and starts up instantly.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;0758bf81-7da2-4da3-aade-eb863789a13e&#34; class=&#34;&#34;&gt;I believe being small and seemingly fast was a big reason for adoption.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;34fa1719-02a4-4ec4-9a1e-d50cce69f59a&#34; class=&#34;&#34;&gt;This comes back to Jeff Bezos&amp;#x27; wisdom: there will never be a time when users want bloated and slow apps so being small and fast is a permanent advantage.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;4c92b333-4928-4bd5-b646-6444a290c586&#34; class=&#34;&#34;&gt;How do I keep SumatraPDF small?&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;751a217e-8f37-4faf-b599-e8e6872565d5&#34; class=&#34;&#34;&gt;I avoid unnecessary abstractions. Window&amp;#x27;s system of controls is a giant pain in the ass to program against. I could use wrappers like Qt, WxWindows or Gtk. They are easier to use but cause instant, giant bloat.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;824da8a3-e464-420e-97c2-e4a3695ddc09&#34; class=&#34;&#34;&gt;I&amp;#x27;m not afraid to write my own implementation of things. I have my own JSON, HTML / XML parsers that are a fraction of size of the popular libraries for those tasks.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;a2c3e35d-17cf-4a29-915b-a15822584cb9&#34; class=&#34;&#34;&gt;I aggressively take advantage of rich functionality included in Windows.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;7ebacd4e-32f8-4251-a7bd-4768b46f7e39&#34; class=&#34;&#34;&gt;Let&amp;#x27;s say I need to do a network request. I could include a monster library like curl or I could write 300 lines of code using win32 APIs. I wrote 300 lines of code.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;d9eccbf3-f079-4d51-af78-9e4269ac5f80&#34; class=&#34;&#34;&gt;An absence of bloat is hard to notice because it isn&amp;#x27;t there.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;1d833084-47c9-43d9-bbcc-da147e35333c&#34; class=&#34;&#34;&gt;My pet peeve is over-using XML for storing data.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;6f0a49eb-ab24-47fe-8437-4421035c0fb5&#34; class=&#34;&#34;&gt;When I worked at Palm I was at a design meeting for auto-update system for a phone. Part of it was storing information about the current version in the image, downloading information about the latest version and comparing them.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;88678e47-18a0-462f-85b8-2abefbf7d7fe&#34; class=&#34;&#34;&gt;The developer decided to use XML for storing that information. That seemed like a lot of bloat for storing simple information like a version number. An compliant XML parser alone is a lot of code. Surely a simple binary format would be easier to implement, I suggested and was ignored.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;f603beb1-dbc0-49a3-a28f-ec29dae2a965&#34; class=&#34;&#34;&gt;If you don&amp;#x27;t have the power to fire someone, your ideas will be ignored.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;82c7a9a5-1045-42c6-a07b-0f276982d777&#34; class=&#34;&#34;&gt;(As you can see, I&amp;#x27;m a great team player.)&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;edb418f5-5d63-494c-9cf9-521578cbffcd&#34; class=&#34;&#34;&gt;For storing advanced settings I designed and implemented a file format that is smaller than XML, readable and writeable by humans and can be implemented in few hundred lines of code. It&amp;#x27;s as powerful as JSON and even more readable.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;791d09e3-6606-4682-82a5-eb64996603bc&#34; class=&#34;&#34;&gt;It&amp;#x27;s so simple that after implementing it I had the time to implement a serialization system for C++ objects and a Go code generator. To add more settings I don&amp;#x27;t have to write more C++ code. I just add data definition to Go generator, re-run it and get data-driven C++ parsing auto-generated.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;7eca9313-98ff-44cd-be4c-ee4250ffc76d&#34; class=&#34;&#34;&gt;It&amp;#x27;s my project and I act like it&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;ea4949d9-fc96-428b-8bd8-805e134af035&#34; class=&#34;&#34;&gt;When someone pays you to write code you have to do it the way they like it.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;bdcdc297-4499-4ada-bb36-dab6949eb2cd&#34; class=&#34;&#34;&gt;A big attraction of working on code you&amp;#x27;re not paid for is that there is no one who can tell you what to do or how to do it.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;f2c298ac-cf3e-4c76-ac42-f30c12e5453f&#34; class=&#34;&#34;&gt;My code would not pass a code review at Google and not because it&amp;#x27;s bad but because it&amp;#x27;s often unorthodox. Outside of accepted dogma.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;3a354d25-51b9-4887-bf84-0778612b254f&#34; class=&#34;&#34;&gt;(As you can see, I&amp;#x27;m a great team player.)&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;e6a6e46e-0939-4601-9b08-74a7b821a921&#34; class=&#34;&#34;&gt;I always used SumatraPDF as my playground for testing crazy ideas.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;b95e1d85-162e-45f3-bf98-7a39b8082116&#34; class=&#34;&#34;&gt;Minimize the code size by not using STL? That&amp;#x27;s crazy but I did it. Granted, in 2006 STL wasn&amp;#x27;t very good.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;8fedc05c-85dc-4f86-ac88-116abe90ca53&#34; class=&#34;&#34;&gt;I learned about how Plan 9 C code had non-traditional scheme of #include files where they don&amp;#x27;t put #ifdef wrappers in each .h file to allow multiple inclusion and .h files don&amp;#x27;t include other .h files. As a result .c files have to include every .h file they need and in the right order. It&amp;#x27;s a bit of a pain and no other modern C++ codebase I know of maintains such discipline.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;4a67d057-3758-4a98-8b1c-ff8121cc8df4&#34; class=&#34;&#34;&gt;But it&amp;#x27;s my project so I did it and I keep doing it. It prevents circular dependencies between .h files and doesn&amp;#x27;t inflate C++ build times because of careless including the same files over and over again.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;8fd27fec-c8a9-4df4-a6b5-6a6247d96d4d&#34; class=&#34;&#34;&gt;I implemented a CSS inspired UI system. Not great, but mine. And I plan to replace with a different one. &#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;75ae0ec3-d369-4df2-ac77-11277f2d9be4&#34; class=&#34;&#34;&gt;Because I can. &#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;2de46301-5252-4086-a674-509037aae8e7&#34; class=&#34;&#34;&gt;Because no one can tell me not to.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;7be74119-9e25-4e3b-b5e6-93aa45050560&#34; class=&#34;&#34;&gt;Cross-platform is over-rated&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;793f3a47-2efd-4c42-aad0-8ddfee8d277f&#34; class=&#34;&#34;&gt;SumatraPDF is unabashedly a &lt;a href=&#34;https://www.sumatrapdfreader.org/docs/Why-only-Windows&#34;&gt;Windows only&lt;/a&gt; app.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;237249b6-3fd7-4391-a02f-69838acf7115&#34; class=&#34;&#34;&gt;Supporting other platforms (Linux, Mac, Android) is one of the most frequent requests. A request that I have to decline.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;3ac092da-6a5b-436e-8297-4ecfa117fcaa&#34; class=&#34;&#34;&gt;First, there is a pragmatic reason: I just don&amp;#x27;t have the bandwidth to write code for 3 platforms.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;2578add0-a0d9-4854-a388-00450fd6c8aa&#34; class=&#34;&#34;&gt;Second, I believe an excellent app for one platform can become more popular than a mediocre app for 3 platforms.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;eb144292-8cff-427f-8c01-8f8beefd808e&#34; class=&#34;&#34;&gt;Coming back to the first reason: I don&amp;#x27;t have the bandwidth to write 3 excellent apps. Part of the reason SumatraPDF is small is my use of win32 APIs for the UI.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;ee527bb9-e8f1-4b8a-bdc1-f07c55b0c0f9&#34; class=&#34;&#34;&gt;The only way for one person to even attempt cross-platform app is to use a UI abstraction layer like Qt, WxWidgets or Gtk.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;503c2314-05f7-4e38-b49f-9777b9ce69f3&#34; class=&#34;&#34;&gt;The problem is that Gtk is ugly, Qt is extremely bloated and WxWidgets barely works.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;3282ad25-f57c-4e17-aec3-6db8ad7d6e49&#34; class=&#34;&#34;&gt;Tests are not necessary, neither are code reviews&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;463d5c27-608a-4934-b295-5bc20edb334c&#34; class=&#34;&#34;&gt;I&amp;#x27;m not saying tests are bad or that you shouldn&amp;#x27;t write test or do code reviews.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;edeac620-0f03-4833-87a9-f64920f4e52b&#34; class=&#34;&#34;&gt;I&amp;#x27;m saying that they are not necessary.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;b3a7a721-01da-4917-98ef-021e28e358fa&#34; class=&#34;&#34;&gt;Dogma is powerful. Sometimes in my corporate life I felt like writing tests was just going through motion. Maybe we should spend more time writing code instead, I though?&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;99f6a97e-6ca7-455b-aadb-3b4e3d1c4fc2&#34; class=&#34;&#34;&gt;But try to make a nuanced point about more tests vs. more code to your fellow developers and you&amp;#x27;ll be burned at stake and your smoldering carcass will be thrown to wild dogs. Village children will use your severed head to play soccer.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;9be3fa34-67eb-4a99-869e-c5fc2458da2a&#34; class=&#34;&#34;&gt;(As you can see, I&amp;#x27;m a great team player.)&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;81203dd3-aee9-43eb-a393-f325b6a12fb3&#34; class=&#34;&#34;&gt;And yet I do know that you can write complex, relatively bug free code without tests, because I did it.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;af324f92-611a-484f-823f-ed4c67941e5f&#34; class=&#34;&#34;&gt;I do know that you can write complex, relatively bug free code without anyone looking over your code, because I did it.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;34f7cb56-e377-46be-b504-381d36d495e4&#34; class=&#34;&#34;&gt;If no one uses your app then who cares if it crashes.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;0d551fca-546e-4ce9-a888-fec6e68f2979&#34; class=&#34;&#34;&gt;If many people use your app and it crashes, they&amp;#x27;ll tell you and then you&amp;#x27;ll fix it.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;888eaae0-0148-4dc5-b18c-cd78e89ac4bc&#34; class=&#34;&#34;&gt;Overnight success takes a decade&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;4bf0b62b-f433-46ba-a957-40a73e5ba51b&#34; class=&#34;&#34;&gt;SumatraPDF is relatively popular. Not Facebook popular or DOOM popular, but more popular than most apps. A respectable level of popular.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;acc0c4ff-6e17-44c8-ae1f-24873ea49f7d&#34; class=&#34;&#34;&gt;It all started with v 0.1 and a trickle of downloads. It remained a trickle for many, many months.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;24c53042-339e-49f8-86e5-16e15c478edd&#34; class=&#34;&#34;&gt;I&amp;#x27;m not sure there&amp;#x27;s a lesson here.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;7cec04a1-d81a-4d60-81de-5d7b7443a5da&#34; class=&#34;&#34;&gt;Success often takes a long time. &#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;e4af0765-846a-454c-acd0-485e5c63f62b&#34; class=&#34;&#34;&gt;Unfortunately, at that stage it&amp;#x27;s undistinguishable from (eventual) failure so this wisdom doesn&amp;#x27;t help you if you&amp;#x27;re working on a not-yet-successful project and debating if you should continue or abandon&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;d89f087c-968e-4b77-8342-e36cafab2c7e&#34; class=&#34;&#34;&gt;The money&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;247b8fb5-3494-410a-a1f5-1fe6701a9604&#34; class=&#34;&#34;&gt;Open source is not a good business model.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;c357e867-8ded-4f25-84b4-7db5de27348d&#34; class=&#34;&#34;&gt;If you want to make money do literally anything else: try to sell software, do consulting, build a SAAS and charge monthly for it, rob a bank.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;dffcb23b-ff84-4327-ac2a-a4635c228189&#34; class=&#34;&#34;&gt;I did experiment with making money and made some.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;b3558a99-f668-4c5f-a7d0-878959399c20&#34; class=&#34;&#34;&gt;There was a time AdSense would pay decent CPM so I put AdSense ads on the website and it made some money. I no longer do because the rates did plummet and it isn&amp;#x27;t worth annoying people. My soul has a price and AdSense can no longer afford it.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;15ad5aa0-b51f-4323-8a15-c918f2e0221e&#34; class=&#34;&#34;&gt;Now I&amp;#x27;m experimenting with Patreon and Paypal donations. It makes more than $100 a month but not much more than that.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;79580d1e-2e7e-4e8b-8611-1ae59bcd1ed6&#34; class=&#34;&#34;&gt;Like I said: don&amp;#x27;t start open source project with intent to make money.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;b8fc5670-a4dd-4b99-9aad-1d5e613492d9&#34; class=&#34;&#34;&gt;Rarely you can have both: freedom to do whatever you want and a good pay so pick what is more important to you. Open source gives you freedom but not money.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;3a635c7a-807d-4f2f-966b-f55efac43848&#34; class=&#34;&#34;&gt;On to the future&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;da8b51ed-f853-4790-801e-df7136942d38&#34; class=&#34;&#34;&gt;I need to continue being like water.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;7c1e3da4-426b-49b1-847c-0ca6ad4b0068&#34; class=&#34;&#34;&gt;For years I resisted adding editing features. &amp;quot;It&amp;#x27;s just a reader&amp;quot; I said. But why not add editing? If people want it, give it to them.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;01be1934-12be-48a4-9a90-7980b056f19a&#34; class=&#34;&#34;&gt;The future of all software is as a web app. Why not bring the spirit of SumatraPDF to the web?&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;c7664905-0423-4ed6-9474-e3a1668dc811&#34; class=&#34;&#34;&gt;Those are just a few ideas I have today.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;9c52815c-31f7-4f56-9eaa-3039342921b1&#34; class=&#34;&#34;&gt;Being like water means that in 5 years I&amp;#x27;ll have other ideas, informed by what&amp;#x27;s happening at that time.&#xA;  &lt;/div&gt;&#xA;&lt;/div&gt;</content>
  </entry>
  <entry>
   <title>How I use Roam Research</title>
   <link href="https://blog.kowalczyk.info/article/2af6a10ab74d43a58053b709badb4ad0/how-i-use-roam-research.html" rel="alternate"></link>
   <updated>2021-06-27T00:00:00Z</updated>
   <id>tag:blog.kowalczyk.info,2021-06-27:/article/2af6a10ab74d43a58053b709badb4ad0/how-i-use-roam-research.html</id>
   <content type="html">&lt;p&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;notion-page&#34; id=&#34;2af6a10a-b74d-43a5-8053-b709badb4ad0&#34;&gt;&#xA;  &lt;div id=&#34;32850f2f-2810-4239-915f-402c7eec9583&#34; class=&#34;&#34;&gt;I take lots of notes and I&amp;#x27;m always looking for that perfect note taking app.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;0641c140-107c-42be-b112-4a0251cae1d0&#34; class=&#34;&#34;&gt;Because I take a lot of notes, mobile-first note taking apps (e.g. Google&amp;#x27;s Keep) are of no interest to me. &#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;fd80aa18-bb07-4217-9525-ca9643ee64fe&#34; class=&#34;&#34;&gt;To me most important thing is:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;3f6e0c28-584e-4890-8583-fe6df734eb82&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;lowest possible friction in adding new notes&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;fast way of finding existing notes&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;div id=&#34;4495c5f2-c049-44af-9e09-f02a5c470a92&#34; class=&#34;&#34;&gt;In theory I could use Google Docs as a note taking app but both &lt;a href=&#34;https://notion.so&#34;&gt;Notion&lt;/a&gt; and &lt;a href=&#34;https://roamresearch.com/&#34;&gt;Roam Research&lt;/a&gt; make it much faster to jot a new note or find a note I wrote in the past.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;f2c7b89d-e428-4d8e-a954-3115ebd1d92b&#34; class=&#34;&#34;&gt;Over the years I&amp;#x27;ve cycled through many note taking systems: a single big text file, collection of markdown files in a git repository, a hosted wiki, a self-hosted wiki, Evernote, my own note taking web app.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;647a7fef-7906-4011-a4dd-8200f5a43b23&#34; class=&#34;&#34;&gt;A combination of &lt;a href=&#34;https://www.notion.so/&#34;&gt;Notion &lt;/a&gt; and &lt;a href=&#34;https://roamresearch.com/&#34;&gt;Roam Research&lt;/a&gt; is, I hope, the end of the road and will be good enough for the rest of my life.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;80aaf1f2-5f8c-4f98-8be2-299bc54f0319&#34; class=&#34;&#34;&gt;This article describes how I use &lt;a href=&#34;https://roamresearch.com/&#34;&gt;Roam Research.&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;09da430e-cc02-4597-a12a-f8129973c2e0&#34; class=&#34;&#34;&gt;Roam use cases&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;bda113f9-69bb-4079-9bd3-86ce2cd77c95&#34; class=&#34;&#34;&gt;Here are my main use cases of Roam:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;905dd924-10f2-48b3-8903-9be00522abc4&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;taking notes / writing&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;keeping a log of activity (what I did)&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;managing todo list&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;managing projects&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;bookmarking&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;div id=&#34;1564628a-dded-4986-8bef-dc5bb14dd4cf&#34; class=&#34;&#34;&gt;Roam can be hard to grasp at first. Things you need to know:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;2ce196eb-c125-45bf-9e06-720e910ee355&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;Roam is a collection of named pages&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;you can trivially link between pages. Writing &lt;code&gt;[[foo]]&lt;/code&gt; or &lt;code&gt;#foo&lt;/code&gt; anywhere creates a link to a page named &lt;code&gt;foo&lt;/code&gt;. A page also lists backlinks (i.e. pages linking to this page) at the end&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;there&amp;#x27;s automatically a page for every day and the default view is a page for current day&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;div id=&#34;14f796d6-1c36-43e6-ac02-e1b5cb5e50ac&#34; class=&#34;&#34;&gt;Knowing that, let&amp;#x27;s go through my use cases.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;9a9030ae-98f1-4689-8e4d-958a189aa502&#34; class=&#34;&#34;&gt;Taking notes / writing&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;1b8ce837-e771-4b0a-b258-45843ddbdbf6&#34; class=&#34;&#34;&gt;Let&amp;#x27;s say I have an idea to write an article about my use of Roam Research (i.e. this very article).&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;68cfc1ef-3c1b-4dc8-9bda-fb497df5171c&#34; class=&#34;&#34;&gt;In most other note taking apps I would first have to think about where to put that note.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;91b7f5ba-5068-4e4f-b756-3697a1b97229&#34; class=&#34;&#34;&gt;In Roam I just start writing in the page for current day and tag it with &lt;code&gt;#draft&lt;/code&gt;.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;cd0fb2ca-7771-463c-8977-3142d2ba516a&#34; class=&#34;&#34;&gt;Later on I can go to a &lt;code&gt;draft&lt;/code&gt; page and see all the drafts I&amp;#x27;m currently working on.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;05b1b78d-34d6-4b96-89c3-eab4b25605ff&#34; class=&#34;&#34;&gt;There is a greater point here: Roam is &amp;quot;write now, organize later&amp;quot; system.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;b17c62f6-c0e3-4e85-bba1-4a8651c4f358&#34; class=&#34;&#34;&gt;When I have a though, I jot it down in today&amp;#x27;s page and tag it with keywords that will help me find it later.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;fe393253-dfef-4455-a0f1-216c15261647&#34; class=&#34;&#34;&gt;It&amp;#x27;s the least amount of friction between having a thought and recording that thought for the future.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;4fb74337-8272-4268-ba89-ac518539598b&#34; class=&#34;&#34;&gt;Taxonomy is personal. I use &lt;code&gt;#draft&lt;/code&gt; for my draft articles, but you could just as well use &lt;code&gt;#article&lt;/code&gt; or &lt;code&gt;#articles&lt;/code&gt; (using singular vs. plural for tags is surprising hard to decide).&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;8d50e964-8ed8-4dbd-a026-4d5cbe5d6ac9&#34; class=&#34;&#34;&gt;Logging of activity&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;23452d71-4467-4f92-b621-5f47dbd2d567&#34; class=&#34;&#34;&gt;In personal life logging of activity is just for curiosity.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;cb75079f-2525-42da-a6bc-34d62e52aa48&#34; class=&#34;&#34;&gt;I write down what I did in a page for current day and tag log entries with &lt;code&gt;#done&lt;/code&gt;.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;ae164863-bd55-4cec-91ea-43b9097ef3c3&#34; class=&#34;&#34;&gt;I visit &lt;code&gt;done&lt;/code&gt; page to review what I did in the past.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;1415a4cb-e856-44a5-b03b-3692b1003beb&#34; class=&#34;&#34;&gt;What if I want to track what I did on a specific project, e.g. &lt;a href=&#34;https://www.sumatrapdfreader.org/free-pdf-reader&#34;&gt;SumatraPDF&lt;/a&gt;?&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;a730f776-10dc-4610-b7d8-bbbcb77d4820&#34; class=&#34;&#34;&gt;I further tag the entry with a tag for the project e.g. &lt;code&gt;#sumatrapdf&lt;/code&gt;. Roam has filtering capabilities that allows me to see entries tagged with &lt;code&gt;#done&lt;/code&gt; AND &lt;code&gt;#sumatrapdf&lt;/code&gt;.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;aa4eb86b-b166-4e5d-ab31-9e5bd91495f2&#34; class=&#34;&#34;&gt;In professional life logging your work could help you level up. If no-one knows what you did, did it happen? Will it lead to a promotion?&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;5d4d88df-ddfd-424d-8b19-5cc1225248a0&#34; class=&#34;&#34;&gt;Roam is perfect for logging what you did so that you can review it later and send a summary to your manager or consulting client.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;1309d6cb-062d-4b84-82e4-1c0f008e2ad6&#34; class=&#34;&#34;&gt;If I were a Google software engineer, I would tag my entries with &lt;code&gt;#done&lt;/code&gt; and &lt;code&gt;#work&lt;/code&gt;.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;42aa402a-3ec9-481f-93fc-3a1778e4f092&#34; class=&#34;&#34;&gt;If I was doing a consulting job I would tag them as &lt;code&gt;#done&lt;/code&gt;, &lt;code&gt;#consulting&lt;/code&gt; and maybe with a tag specific to the project or the client.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;14dca06a-1422-4932-bdc6-e0f61921e7f9&#34; class=&#34;&#34;&gt;Again, taxonomy is personal. You can use other tags if they make more sense to you.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;17e7e702-81bc-4252-b040-fdac724be77c&#34; class=&#34;&#34;&gt;Managing projects&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;bd5606ca-ca1c-4509-bb51-cf18ef7a03f3&#34; class=&#34;&#34;&gt;I work on several software projects at the same time.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;4a967f1e-89e8-4942-9624-6da73633071e&#34; class=&#34;&#34;&gt;I have a page per project that serves as a hub of information related to the project.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;3e42933f-3c57-427a-92fa-c66864a0a393&#34; class=&#34;&#34;&gt;On that page I have:&#xA;  &lt;div class=&#34;indented&#34;&gt;&#xA;    &lt;ul id=&#34;d7ff7528-7297-436c-8c76-cb240bcdcc29&#34; class=&#34;bulleted-list&#34;&gt;&#xA;      &lt;li&gt;most important links (e.g. to github repository, Digital Ocean dashboard for deployed project etc.)&#xA;      &lt;/li&gt;&#xA;      &lt;li&gt;list of things to do next&#xA;      &lt;/li&gt;&#xA;      &lt;li&gt;list of ideas for future improvements&#xA;      &lt;/li&gt;&#xA;      &lt;li&gt;links to competing products&#xA;      &lt;/li&gt;&#xA;      &lt;li&gt;technical overviews. Memory is always fading so it pays to jot down things that might help you remember key information&#xA;      &lt;/li&gt;&#xA;      &lt;li&gt;documentation&#xA;      &lt;/li&gt;&#xA;    &lt;/ul&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;8f65bcfb-91ae-481a-8c1d-bcd878de8083&#34; class=&#34;&#34;&gt;Todo list&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;5cca409a-5bdd-48d8-b5d3-f0a209063f0a&#34; class=&#34;&#34;&gt;I don&amp;#x27;t use dedicated todo list applications. I think that creating hundreds of todo items is overwhelming and counter-prodcutive.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;14e48079-235c-4f72-bfa9-2157a24d39ca&#34; class=&#34;&#34;&gt;I do believe in one of the tenets of Getting Things Done system: writing down your ideas and todo tasks to get them out of your head but be able to review them in the feature.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;45c022ee-a728-4d07-a0e4-dadbffa4c949&#34; class=&#34;&#34;&gt;I maintain a &amp;quot;maybe do list&amp;quot; instead of &amp;quot;todo list&amp;quot;.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;7da23382-d9f6-4e27-903d-ae8f13cc719b&#34; class=&#34;&#34;&gt;For example, if I have an idea for SumatraPDF, I jot it down in Roam and tag it with &lt;code&gt;#sumatrapdf&lt;/code&gt;. I can then review those ideas in the future when I have time to work on SumatraPDF.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;50db2f37-1b69-470e-a576-e5863a607782&#34; class=&#34;&#34;&gt;I do maintain a very short (few items) todo list for near future. Mostly today and tomorrow.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;3e071b5a-dae1-43a3-bb04-a56a27636034&#34; class=&#34;&#34;&gt;This is an anti-procrastination device.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;50ed53a9-beb5-49ff-a4e4-cbf501fb0fc2&#34; class=&#34;&#34;&gt;I find that when I start working on a task, I manage to finish it.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;68e1cbbc-46f8-49ac-b8ed-34858ded0f81&#34; class=&#34;&#34;&gt;Procrastination is what keeps me from even starting.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;8f0d3aa1-1a0c-4b54-9f55-08928488fdd7&#34; class=&#34;&#34;&gt;Having a list of things to do in the morning helps me start working (as opposed to starting to browse Twitter).&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;d66dd635-aa6c-47a8-bb4c-7d348fd1e097&#34; class=&#34;&#34;&gt;It&amp;#x27;s worth mentioning that other people do use Roam as a sophisticated todo list. Roam is flexible enough. For example Roam understands dates so you can have pages with tags that record a completion date for a task and you can use Roam&amp;#x27;s powerful query syntax to find tasks that are supposed to be done in the next week.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;ad5a792e-7079-4cfa-8ee5-ec23cb14b579&#34; class=&#34;&#34;&gt;Bookmarking&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;d9a2c43a-8b66-40f7-bb67-35d6fde89a83&#34; class=&#34;&#34;&gt;Bookmark managers in browsers are not good at managing large list of bookmarks. Hierarchies don&amp;#x27;t scale.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;bd8d1f55-29d4-4622-ad99-03ce55dfde6c&#34; class=&#34;&#34;&gt;Roam&amp;#x27;s easy tagging makes it a replacement for services like &lt;a href=&#34;https://pinboard.in/&#34;&gt;pinboard.in&lt;/a&gt; (and once-famous-now-defunct del.icio.us). Found a page about Docker you want to bookmark for later? Drop the link in Roam and tag it with &lt;code&gt;#docker&lt;/code&gt;. Find it later in the &lt;code&gt;docker&lt;/code&gt; page.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;a108c982-91a7-4f18-bf5c-41e4f7c0d41d&#34; class=&#34;&#34;&gt;There is a use case for browser&amp;#x27;s bookmarks: a fast way to get to most frequently used websites. I&amp;#x27;m experimenting with using Roam for that as well.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;8ff3a57d-2d56-4e27-8827-822e85da470c&#34; class=&#34;&#34;&gt;I&amp;#x27;ve created page &lt;code&gt;bookmarks&lt;/code&gt; with most frequently used links. Opening a Roam page from scratch is relatively slow so I keep that page open at all times and as a pinned tab (a Chrome thing).&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;1ff2b396-0f8a-4cd7-ad0e-07044725a17d&#34; class=&#34;&#34;&gt;One more thing...&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;d930ce0b-60ab-4491-a777-3ab75a89c0f5&#34; class=&#34;&#34;&gt;Actually, lots of things.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;02c42e45-d9ac-4d16-b999-05369639d0fc&#34; class=&#34;&#34;&gt;I&amp;#x27;m relatively light user of Roam. I&amp;#x27;m still discovering new use cases for Roam and better ways of managing notes.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;3de375fe-cf3a-40ac-a5ca-a7d5aaa02cfe&#34; class=&#34;&#34;&gt;But even just the basics usage of jotting things down and tagging them for cross-referencing is very useful. Speed and flexibility are important and Roam has both.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;97d9a65f-9bb6-4ec7-aa7e-fbcee297a05a&#34; class=&#34;&#34;&gt;Roam can offer much more if you&amp;#x27;re willing to invest time. Powerful search queries, tags with values, storing files. There&amp;#x27;s a reason people become obsessed with Roam.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;5748604a-cb85-451e-ac6e-824755bf2c5c&#34; class=&#34;&#34;&gt;I&amp;#x27;m not obsessed but I do appreciate a great tool.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;6be0c2e9-eaf4-4cdf-9348-eb1caa29e352&#34; class=&#34;&#34;&gt;Notion and Roam?&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;233cc55e-140e-40f2-8698-ea8ceaf344e4&#34; class=&#34;&#34;&gt;Why use both &lt;a href=&#34;https://www.notion.so/&#34;&gt;Notion &lt;/a&gt; and &lt;a href=&#34;https://roamresearch.com/&#34;&gt;Roam&lt;/a&gt;? Why not stick with just one of them?&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;c86f9d64-54dc-44f6-9ee8-eec00e106c10&#34; class=&#34;&#34;&gt;Notion has some use cases that Roam Research doesn&amp;#x27;t support well. For example sharing a subset of notes with other people.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;59c54354-6b90-4f70-8f32-3a931dc62572&#34; class=&#34;&#34;&gt;They also have different philosophies of managing notes.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;bfbea900-afce-4069-a798-9b981da3a55d&#34; class=&#34;&#34;&gt;Notion is strongly hierarchical. Pages are nested within pages. It&amp;#x27;s great when hierarchy is obvious but sometimes I spend brain cycles figuring out where in the hierarchy should a new page go.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;636ada30-1c2b-498e-a8c6-8caea7031d5c&#34; class=&#34;&#34;&gt;Roam is a flat collection of interlinked pages. It&amp;#x27;s more difficult to grasp but removes the thinking. Write things down, tag them now and maybe organize later.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;56b562a4-51c0-4f47-bced-12bb14b6666a&#34; class=&#34;&#34;&gt;Roam is more quirky. While Notion has more polished UI, Roam is often faster to use. Trying to change a link destination in Notion can be a struggle.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;d55a90e4-c407-47d5-89e2-3bf59d718cff&#34; class=&#34;&#34;&gt;Final thoughts&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;32c9e8f2-3e2a-40a2-aafa-38302405eb71&#34; class=&#34;&#34;&gt;We live in a golden era of note taking apps. I wish I had Notion or Roam Research 10 years ago.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;8dff0c97-d759-4924-8091-03d060685c0e&#34; class=&#34;&#34;&gt;&lt;a href=&#34;https://www.notion.so/&#34;&gt;Notion &lt;/a&gt; opened floodgates of high quality note taking apps. Before Notion there was just Evernote. After Notion we got lots of other options, including Roam.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;2d826eb6-e7a4-4419-ad61-a9d0b3cfd51e&#34; class=&#34;&#34;&gt;I think they are both powerful enough to serve as a note taking system for life.&#xA;  &lt;/div&gt;&#xA;&lt;/div&gt;</content>
  </entry>
  <entry>
   <title>The things we do to ship desktop software</title>
   <link href="https://blog.kowalczyk.info/article/39a15945117440d99a9ef0f7de1b618a/the-things-we-do-to-ship-desktop-software.html" rel="alternate"></link>
   <updated>2019-05-01T00:00:00Z</updated>
   <id>tag:blog.kowalczyk.info,2019-05-01:/article/39a15945117440d99a9ef0f7de1b618a/the-things-we-do-to-ship-desktop-software.html</id>
   <content type="html">&lt;p&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;notion-page&#34; id=&#34;39a15945-1174-40d9-9a9e-f0f7de1b618a&#34;&gt;&#xA;  &lt;div id=&#34;98a73fba-b84e-414d-addf-85d79cdde7b6&#34; class=&#34;&#34;&gt;I wrote a &lt;a href=&#34;https://blog.kowalczyk.info/software/fast-file-finder-for-windows/&#34;&gt;small utility for Windows&lt;/a&gt;. It indexes a hard-drive and allows to find a file by name in under a second.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;e74d785b-17b8-4c47-a516-d77faec50ebd&#34; class=&#34;&#34;&gt;It might surprise you that I spent more time on things that are not related to core functionality. Let&amp;#x27;s call it a tax of shipping desktop software.&#xA;  &lt;/div&gt;&#xA;&lt;a href=&#34;https://blog.kowalczyk.info/software/fast-file-finder-for-windows/&#34; target=&#34;_blank&#34;&gt;&#xA;&lt;img class=&#34;blog-img&#34; src=&#34;/img/fed7bc7dbed0e68db1e07b3dfee36e73528bcb2d.png&#34;&gt;&#xA;&lt;/a&gt;&#xA;  &lt;div id=&#34;e0b9d485-cf61-4f80-b5ad-52d1fdabb5b7&#34; class=&#34;&#34;&gt;Here are some of the taxes you need to pay to ship a desktop application to users.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;ca20ffeb-8f51-4746-9b58-8535f2ec7a42&#34; class=&#34;&#34;&gt;Logging&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;bae7aa38-3a49-49a0-9208-185c1add597b&#34; class=&#34;&#34;&gt;In the long term you want to be able to diagnose problems quickly and logging helps in that.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;41701f62-413e-44b6-b347-e4cbb88014a3&#34; class=&#34;&#34;&gt;So I&amp;#x27;ve implemented logging to a file.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;3cbd590e-81d6-4721-bd9a-ea7e2d14c23b&#34; class=&#34;&#34;&gt;I went beyond basics and implemented a way to easily see the logs in a window. Simple to implement as all you need is to use built-in read-only text box control.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;dfd52e6d-3673-444d-a978-0ace128c95fb&#34; class=&#34;&#34;&gt;You can toggle log window from File menu. As an additional touch, if it logs an error and it&amp;#x27;s a dev build, it automatically opens a window.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;653a2410-3fb2-4d93-814a-40bdb77f3753&#34; class=&#34;&#34;&gt;Website&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;6fb32419-2e87-4f6f-8876-3693d26726da&#34; class=&#34;&#34;&gt;You need to have &lt;a href=&#34;https://blog.kowalczyk.info/software/fast-file-finder-for-windows/&#34;&gt;a website&lt;/a&gt;. It&amp;#x27;s a simple website: few screenshots and a download button, but it does take a few hours to make.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;324f9ba5-3bf0-4240-b113-1847249d05a2&#34; class=&#34;&#34;&gt;It also has a &lt;a href=&#34;https://blog.kowalczyk.info/software/fast-file-finder-for-windows/feedback.html&#34;&gt;feedback page,&lt;/a&gt; so that people can tell me how to improve the program.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;cc06c332-34ff-403b-a46b-c6a30567d153&#34; class=&#34;&#34;&gt;Signing certificate&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;f4b64af6-fbed-4329-b2e7-1fd26116521d&#34; class=&#34;&#34;&gt;On Windows anti-virus software and Microsoft&amp;#x27;s anti-phishing systems are lousy and often flag innocent software as malware.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;2fdff308-a80b-42b2-9531-ea29319fc1bb&#34; class=&#34;&#34;&gt;You can decrease the probability of that by signing your installer and executables with software signing certificate.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;4a696400-1df8-4106-80d3-3e7cc33b6345&#34; class=&#34;&#34;&gt;Unfortunately, this certificate is both expensive and pain in the ass to buy. Entities selling them want a proof of your (or your company) identity but the rules are often bureaucratic idiocy.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;bf5cbd52-5437-440e-9fa7-c6dace4d1652&#34; class=&#34;&#34;&gt;Installer&#xA;  &lt;/h2&gt;&#xA;&lt;div id=&#34;c3d6cc82-79c9-449e-8bdf-a10431c9d0f8&#34; class=&#34;column-list&#34;&gt;&#xA;&lt;div id=&#34;6f99bf12-207e-46a8-9203-8e951618614a&#34; style=&#34;width:50%&#34; class=&#34;column&#34;&gt;&#xA;  &lt;div id=&#34;caa035e0-0822-4bff-9052-6ba2432c68e6&#34; class=&#34;&#34;&gt;You need an installer because it makes the life of your users easier.&#xA;  &lt;/div&gt;&#xA;&lt;/div&gt;&#xA;&lt;div id=&#34;5b436e20-cb96-4dc9-ac4c-7ab0db5b8737&#34; style=&#34;width:50%&#34; class=&#34;column&#34;&gt;&#xA;&lt;img class=&#34;blog-img&#34; src=&#34;/img/29082c30f062f87de8996cd9a5e07eba3e2fd67e.png&#34;&gt;&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;&#xA;  &lt;div id=&#34;0edf85f0-9b25-4bf5-92e6-63e580ac3122&#34; class=&#34;&#34;&gt;What installer does is not complicated:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;9def2635-e270-4b21-b01e-1e7c14094a83&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;copy the files in the right place&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;create necessary registry entries for un-installation&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;create a shortcut on the desktop and in Start Menu&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;implement un-installation logic to delete that which has been created during installation (files, shortcuts, registry entries)&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;h2 id=&#34;e5af1107-a494-401d-8dad-c53545579040&#34; class=&#34;&#34;&gt;Auto-update system&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;56c5f212-5866-4a2b-b308-43d8964cc991&#34; class=&#34;&#34;&gt;I want the users to get the latest version in the easiest possible way, One possible way to implement it:&#xA;  &lt;/div&gt;&#xA;&lt;img class=&#34;blog-img&#34; src=&#34;/img/062fada349b758d345579a3730b6448d369e1f1c.png&#34;&gt;&#xA;  &lt;h2 id=&#34;683d5251-21b9-4c5c-98ec-4a34f52334f7&#34; class=&#34;&#34;&gt;Build system&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;25bce43f-cc93-4ea5-a444-82b927cf89dd&#34; class=&#34;&#34;&gt;To make release process smooth, you need automated build and release system.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;b299623a-2845-4fb4-bfdb-2ecf1a5e5720&#34; class=&#34;&#34;&gt;In my case it&amp;#x27;s a Go program that builds the software, signs it, uploads to Digital Ocean Spaces for storage, updates the info needed for auto-update system.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;497d2df3-7e23-4708-9b32-1df6a2620f64&#34; class=&#34;&#34;&gt;The taxes add up&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;e9b349d5-fe71-4df0-bbb5-ff8d090030e6&#34; class=&#34;&#34;&gt;Those things are not hard to implement. Individually they don&amp;#x27;t take a lot of time to implement.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;15d1280d-1d6e-4c8e-b5c1-1bb102eae83d&#34; class=&#34;&#34;&gt;But when you add it all up, for this particular project I estimate I spent more time building those system than on the core functionality.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;c2f02785-2935-4801-bcac-e89b141dbc86&#34; class=&#34;&#34;&gt;The bright light in the tunnel is:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;948171db-424e-4278-82b1-0e34725b06b6&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;I can re-use most of this code in other software&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;it only needs to be written once. The more time I spend on the app, the less expensive those things are as percentage of total development time&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;&lt;/div&gt;</content>
  </entry>
  <entry>
   <title>Lessons learned porting 50k loc from Java to Go</title>
   <link href="https://blog.kowalczyk.info/article/19f2fe97f06a47c3b1f118fd06851fad/lessons-learned-porting-50k-loc-from-java-to-go.html" rel="alternate"></link>
   <updated>2019-04-05T00:00:00Z</updated>
   <id>tag:blog.kowalczyk.info,2019-04-05:/article/19f2fe97f06a47c3b1f118fd06851fad/lessons-learned-porting-50k-loc-from-java-to-go.html</id>
   <content type="html">&lt;p&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;notion-page&#34; id=&#34;19f2fe97-f06a-47c3-b1f1-18fd06851fad&#34;&gt;&#xA;  &lt;div id=&#34;6114e64c-354c-46aa-bd2e-cf235c36473f&#34; class=&#34;&#34;&gt;I was contracted to port a large Java code base to Go.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;e2ee09a5-225c-41a2-9109-5235641ebc45&#34; class=&#34;&#34;&gt;The code in question is a Java client for &lt;a href=&#34;https://ravendb.net/&#34;&gt;RavenDB&lt;/a&gt;, a NoSQL JSON document database. Code with tests was around 50 thousand lines.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;4fb604ea-35e8-4213-8a67-e5ee6389e070&#34; class=&#34;&#34;&gt;The result of the port is a &lt;a href=&#34;https://github.com/ravendb/ravendb-go-client&#34;&gt;Go client&lt;/a&gt;.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;ec80e79f-3b58-4a79-98d8-9583b19fbb65&#34; class=&#34;&#34;&gt;This article describes what I&amp;#x27;ve learn in the process.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;52e66306-2a2a-43a0-8f6d-c09116d698a1&#34; class=&#34;&#34;&gt;Testing, code coverage&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;9ea766c6-c727-4532-9ab9-650905ad2641&#34; class=&#34;&#34;&gt;Large projects benefit greatly from automated testing and tracking code coverage.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;fbe325b0-8342-41cc-b998-af52e0e74c4b&#34; class=&#34;&#34;&gt;I used TravisCI and AppVeyor for testing. &lt;a href=&#34;http://codecov.io&#34;&gt;Codecov.io&lt;/a&gt; for code coverage. There are many other services.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;cdb6965d-9b07-437e-b339-ece39f722ee8&#34; class=&#34;&#34;&gt;I used both AppVeyor and TravisCI because a year ago Travis didn&amp;#x27;t have Windows support and AppVeyor didn&amp;#x27;t have Linux support.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;f77ffdf9-6e66-42b0-918c-5ec49ca63868&#34; class=&#34;&#34;&gt;Today if I was settings this up from scratch, I would stick with just AppVeyor, as it can now do both Linux and Windows testing and the future of TravisCI is murky, after it was acquired by private equity firm and reportedly fired the original dev team.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;797aafaa-5aa5-45e2-9cae-4761f41813cf&#34; class=&#34;&#34;&gt;Codecov is barely adequate. For Go, they count non-code lines (comments etc.) as not executed. It&amp;#x27;s impossible to get 100% code coverage as reported by the tool. Coveralls seems to have the same problem.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;b3b6a8e5-5fee-43e6-b0d1-4664deb1288c&#34; class=&#34;&#34;&gt;It&amp;#x27;s better than nothing but there&amp;#x27;s an opportunity to do things better, especially for Go programs.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;2a1e7058-9854-4bbd-ad11-98e06d2a7011&#34; class=&#34;&#34;&gt;Go&amp;#x27;s race detector is great&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;0bca9e12-f42c-4228-ac7a-dec7abb64988&#34; class=&#34;&#34;&gt;Parts of the code use concurrency and it&amp;#x27;s really easy to get concurrency wrong.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;b1cb29bb-2572-4a5f-8c0e-09184b793d60&#34; class=&#34;&#34;&gt;Go provides race detector that can be enabled with &lt;code&gt;-race&lt;/code&gt; flag during compilation.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;2cf9c9a7-abc0-4940-b1b3-5666e9948e62&#34; class=&#34;&#34;&gt;It slows down the program but additional checks can detect if you&amp;#x27;re concurrently modifying the same memory location.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;fe1eaf60-ab91-4fbc-82f9-82d969fcbb1b&#34; class=&#34;&#34;&gt;I always run tests with &lt;code&gt;-race&lt;/code&gt; enabled and it alerted me to numerous races, which allowed me to fix them promptly.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;6940f4bc-0007-473c-a770-10146e6f6d32&#34; class=&#34;&#34;&gt;Building custom tools for testing&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;7ea53a91-4abd-4ce3-a62a-553972e993e0&#34; class=&#34;&#34;&gt;In a project that big it&amp;#x27;s impossible to verify correctness by inspection. Too much code to hold in your head at once.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;160c29d1-01ce-450a-93ea-c4f7d208ed35&#34; class=&#34;&#34;&gt;When a test fails, it can be a challenge to figure out why just from the information in the test failure.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;e99818eb-a0e3-4d5c-86d9-3c6255228942&#34; class=&#34;&#34;&gt;Database client driver talks to RavenDB database server over HTTP using JSON to encode commands and results.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;2a771103-871b-4aed-b357-e26e7e2d7366&#34; class=&#34;&#34;&gt;When porting Java tests to Go, it was very useful to be able to capture the HTTP traffic between Java client and server and compare it with HTTP traffic generated by  Go port.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;45f0b0db-f371-443e-8286-12f6fb0eac2c&#34; class=&#34;&#34;&gt;I built custom tools to help me do that.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;461b2e1b-4cc6-4c00-afc6-04ebde77d108&#34; class=&#34;&#34;&gt;For capturing HTTP traffic in Java client, I built a &lt;a href=&#34;https://github.com/kjk/httplogproxy&#34;&gt;logging HTTP proxy&lt;/a&gt; in Go and directed Java client to use that HTTP proxy.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;a9ad9b31-0a17-4448-97fc-628b48a67c4b&#34; class=&#34;&#34;&gt;For Go client, I built&lt;a href=&#34;https://github.com/ravendb/ravendb-go-client/blob/20fade9ee6d22d60c7babf4a155c4de5bf4cfd3b/request_executor.go#L23&#34;&gt; a hook in the library&lt;/a&gt; that allows to intercept HTTP requests. I used it to log the traffic to a file.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;cab0cd70-08fa-4a66-9cb9-e9f772275cbc&#34; class=&#34;&#34;&gt;I was then able to compare HTTP traffic generated by Java client to traffic generated by my Go port and spot the differences. &#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;7a0901e7-7680-43db-a009-f99982927a5d&#34; class=&#34;&#34;&gt;Porting process&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;39b113c3-8bb6-4647-8766-39cb9ee6926d&#34; class=&#34;&#34;&gt;You can&amp;#x27;t just start porting 50 thousand lines of code in random order. Without testing and validating after every little step I&amp;#x27;m sure I would be defeated by complexity.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;ff860c2d-d2df-4363-b62d-f74f3ecefc19&#34; class=&#34;&#34;&gt;I was new to RavenDB and Java code base. My first step was to get a high-level understanding how Java code works.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;7879a133-560f-4e57-b218-3031093240f6&#34; class=&#34;&#34;&gt;At the core the client talks to the server via HTTP protocol. I captured the traffic, looked at it and wrote the simplest Go code to talk the server.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;a490bd05-c2cd-4652-ad2a-36edf4eeb3a6&#34; class=&#34;&#34;&gt;When that was working it gave me confidence I&amp;#x27;ll be able to replicate the functionality.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;f63902db-b522-4a3d-875d-6986ef8f99ef&#34; class=&#34;&#34;&gt;My first milestone was to port enough code to be able to port the simplest Java test.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;4c14b650-2077-4dc1-852f-17cb3df257d8&#34; class=&#34;&#34;&gt;I used a combination of bottom-up and top-down approach.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;8af3aa96-dd67-4144-be85-ce551eba43d0&#34; class=&#34;&#34;&gt;Bottom-up part is where I identified the code at the bottom of call chain responsible for sending commands to the server and parsing responses and ported those.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;838a7d16-dc7f-43b8-a7f8-83cb833150f4&#34; class=&#34;&#34;&gt;The top-down part is where I stepped through the test I was porting to identify which parts of the code need to be ported to implement that part.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;7334eafd-9f42-4c9c-a57a-47653d0742dc&#34; class=&#34;&#34;&gt;After successfully porting the first step, the rest of the work was porting one test at a time, also porting all the necessary code needed to make the test work.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;7570fc0d-1498-4036-9895-1189d122de9d&#34; class=&#34;&#34;&gt;After the tests were ported and passing, I did improvements to make the code more Go-ish.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;16b6d38f-ba3c-4d2c-bf50-3707ab542578&#34; class=&#34;&#34;&gt;I believe that this step-by-step approach was crucial to completing the work.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;ff73f82d-8554-40a2-816b-0a118c00ff21&#34; class=&#34;&#34;&gt;Psychologically, when faced with a year-long project, it&amp;#x27;s important to have smaller, intermediate milestones. Hitting those kept me motivated.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;d630e4ec-f083-4a54-afc3-5237752a326f&#34; class=&#34;&#34;&gt;Keeping the code compiling, running and passing tests at all times is also good. Allowing bugs to accumulate can make it very hard to fix them when you finally get to it.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;77e19346-b99c-4b16-bef9-7a0337dd9801&#34; class=&#34;&#34;&gt;Challenges of porting Java to Go&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;36fefd42-e06a-47a3-ad97-010f3a534004&#34; class=&#34;&#34;&gt;The objective of the port was to keep it as close as possible to Java code base, as it needs to be kept in sync with Java changes in the future.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;bcffa408-2aeb-4a80-b325-2d49d11187a6&#34; class=&#34;&#34;&gt;I&amp;#x27;m somewhat surprised how much code I ported in a line-by-line fashion. The most time consuming part of the port was reversing the order of variable declaration, from Java&amp;#x27;s &lt;code&gt;type name&lt;/code&gt; to Go&amp;#x27;s &lt;code&gt;name type&lt;/code&gt;. I wish there was a tool that would do that part for me.&#xA;  &lt;/div&gt;&#xA;  &lt;h3 id=&#34;cd4917b9-2889-4da5-a234-bab324656d46&#34; class=&#34;&#34;&gt;String vs. string&#xA;  &lt;/h3&gt;&#xA;  &lt;div id=&#34;a3591584-88c9-4b55-ab8b-ca215c6b39c5&#34; class=&#34;&#34;&gt;In Java, &lt;code&gt;String&lt;/code&gt; is an object that really is a reference (a pointer). As a result, a string can be &lt;code&gt;null&lt;/code&gt;.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;11a73eec-2ec1-4566-aa20-b69f73532b42&#34; class=&#34;&#34;&gt;In Go &lt;code&gt;string&lt;/code&gt; is a value type. It can&amp;#x27;t be &lt;code&gt;nil&lt;/code&gt;, only empty.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;1b150518-06b3-4d0d-8dba-010caa9803ca&#34; class=&#34;&#34;&gt;It wasn&amp;#x27;t a big deal and most of the time I could mechanically replace &lt;code&gt;null&lt;/code&gt; with &lt;code&gt;&amp;quot;&amp;quot;&lt;/code&gt;.&#xA;  &lt;/div&gt;&#xA;  &lt;h3 id=&#34;6d968c25-fdbe-485e-9fc7-94ff463a3e7c&#34; class=&#34;&#34;&gt;Errors vs. exceptions&#xA;  &lt;/h3&gt;&#xA;  &lt;div id=&#34;5ea62092-a11e-48d1-958e-5690bf05e58f&#34; class=&#34;&#34;&gt;Java uses exceptions to communicate errors.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;41192f60-1758-4c9c-9973-98a7655a1fb9&#34; class=&#34;&#34;&gt;Go returns values of &lt;code&gt;error&lt;/code&gt; interface.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;89108d29-52e2-43f9-9eae-9f0b18962b31&#34; class=&#34;&#34;&gt;Porting wasn&amp;#x27;t difficult but it did require changing lots of function signatures to return error values and propagate them up the call stack.&#xA;  &lt;/div&gt;&#xA;  &lt;h3 id=&#34;078f42fa-f02a-44be-863a-71001b897650&#34; class=&#34;&#34;&gt;Generics&#xA;  &lt;/h3&gt;&#xA;  &lt;div id=&#34;94dbe4ce-463b-4f28-af9d-3a995bcfede4&#34; class=&#34;&#34;&gt;Go doesn&amp;#x27;t have them (yet).&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;ffd61de2-3c30-4f86-8539-fb40d76743f5&#34; class=&#34;&#34;&gt;Porting generic APIs was the biggest challenge.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;85ac1e7a-e05e-4a9c-8587-393c29a94133&#34; class=&#34;&#34;&gt;Here&amp;#x27;s an example of a generic method in Java:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;T&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;load&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Class&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;clazz&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;String&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;c60fc3d5-4f04-4a09-9df1-01e455b0f746&#34; class=&#34;&#34;&gt;And the caller:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;Foo&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;foo&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;load&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Foo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;a13a41ab-10c1-42fa-9887-b4881a5f6baf&#34; class=&#34;&#34;&gt;In Go, I used two strategies.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;151268f5-45f0-4562-9955-2b79fc81cd3c&#34; class=&#34;&#34;&gt;One is to use &lt;code&gt;interface{}&lt;/code&gt;, which combines value and its type, similar to &lt;code&gt;object&lt;/code&gt; in Java. This is not preferred approach. While it works, operating on &lt;code&gt;interface{}&lt;/code&gt; is clumsy for the user of the library.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;96feec96-9ede-4d98-aaf4-3b794cca607e&#34; class=&#34;&#34;&gt;In some cases I was able to use reflection and the above code was ported as:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;Load&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;result&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;interface&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{},&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;error&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;d614d6b0-685d-413e-9419-0e875071fde4&#34; class=&#34;&#34;&gt;I could use reflection to query type of &lt;code&gt;result&lt;/code&gt; and create values of that type from JSON document.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;bcac8583-1308-4f6c-9232-ea60a1704ebe&#34; class=&#34;&#34;&gt;And the caller side:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;result&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Foo&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;Load&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;result&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;h3 id=&#34;40cc3348-5dff-4b84-aef1-1aa2cb9f8d91&#34; class=&#34;&#34;&gt;Function overloading&#xA;  &lt;/h3&gt;&#xA;  &lt;div id=&#34;72b0d9e2-fe6e-431b-b836-c8780163b35f&#34; class=&#34;&#34;&gt;Go doesn&amp;#x27;t have it (and most likely will never have it).&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;3a46d3e6-2723-4a7b-aabe-8fa833dbc491&#34; class=&#34;&#34;&gt;I can&amp;#x27;t say I found a good solution to port those.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;312bd748-48b0-4f99-9ec5-5951ee28d6f6&#34; class=&#34;&#34;&gt;In some cases overloading was used to create shorter helpers:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;foo&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;String&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;foo&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;foo&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;a72f9601-b642-4d71-ac80-29f97b086cf2&#34; class=&#34;&#34;&gt;Sometimes I would just drop the shorter helper.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;45806cdd-f0e5-4e54-a1fd-717d1e40ae81&#34; class=&#34;&#34;&gt;Sometimes I would write 2 functions:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;foo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;fooWithB&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;b&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;936a7d68-5e56-46d8-ac8b-c923f9d639f3&#34; class=&#34;&#34;&gt;When number of potential arguments was large I would sometimes do:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;type&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;FooArgs&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;A&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;B&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;foo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;args&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;FooArgs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;h3 id=&#34;4d9993ae-af1a-4ce1-9761-b83ac363a1f1&#34; class=&#34;&#34;&gt;Inheritance&#xA;  &lt;/h3&gt;&#xA;  &lt;div id=&#34;f818e408-afeb-4b55-9cb5-0baab94ca58c&#34; class=&#34;&#34;&gt;Go is not especially object-oriented and doesn&amp;#x27;t have inheritance.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;86e462a7-4424-4ce3-a222-31a6bdc6a9cb&#34; class=&#34;&#34;&gt;Simple cases of inheritance can be ported with embedding.&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;B&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;A&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;083e90b3-6768-4540-8fae-90db7606b0ee&#34; class=&#34;&#34;&gt;Can sometimes be ported as:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;type&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;A&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;type&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;B&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;A&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;2aeafae5-62d5-48e6-b8fa-cb61119d78b6&#34; class=&#34;&#34;&gt;We&amp;#x27;ve embedded &lt;code&gt;A&lt;/code&gt; inside &lt;code&gt;B&lt;/code&gt;, so &lt;code&gt;B&lt;/code&gt; inherit all the methods and fields of &lt;code&gt;A&lt;/code&gt;.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;fbada6ad-d706-4cf2-bb01-47c192b28ee5&#34; class=&#34;&#34;&gt;It doesn&amp;#x27;t work for virtual functions.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;dab787db-5aa5-4781-8175-d6a60105a767&#34; class=&#34;&#34;&gt;There is no good way to directly port code that uses virtual functions.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;5fdf7b54-b140-4acd-a784-2b6c089bd584&#34; class=&#34;&#34;&gt;One option to emulate virtual function is to use embedding of structs and function pointers. This essentially re-implements virtual table that Java gives you for free as part of &lt;code&gt;object&lt;/code&gt; implementation.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;a53917bb-5134-48f4-83d1-2bf09db0ecde&#34; class=&#34;&#34;&gt;Another option is to write a stand-alone function that dispatches the right function for a given type by using type switch.&#xA;  &lt;/div&gt;&#xA;  &lt;h3 id=&#34;e21c4d3a-5145-4fe8-85e5-abd2e2ccc599&#34; class=&#34;&#34;&gt;Interfaces&#xA;  &lt;/h3&gt;&#xA;  &lt;div id=&#34;f9e1b97a-0c8b-4c79-8b2a-c31f7b3a2e2e&#34; class=&#34;&#34;&gt;Both Java and Go have interfaces but they are different things, like apples and salami.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;581e6555-051f-4334-a4bf-496c5527649f&#34; class=&#34;&#34;&gt;A few times I did create a Go interface type that replicated Java interface.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;6177a0d5-7d2e-48c2-98d3-b487a5bf675c&#34; class=&#34;&#34;&gt;In more cases I dropped interfaces and instead exposed concrete structs in the API.&#xA;  &lt;/div&gt;&#xA;  &lt;h3 id=&#34;bdadbde2-8b24-4a83-a2be-21aea5b4fd58&#34; class=&#34;&#34;&gt;Circular imports between packages&#xA;  &lt;/h3&gt;&#xA;  &lt;div id=&#34;8ede1482-94ed-4b11-8943-165a989dd466&#34; class=&#34;&#34;&gt;Java allows circular imports between packages.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;c72789c0-c387-4f74-8c2e-31bdb02bf611&#34; class=&#34;&#34;&gt;Go does not.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;8c854967-f90b-4286-bc3e-df00df101817&#34; class=&#34;&#34;&gt;As a result I was not able to replicate the package structure of Java code in my port.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;8c5e0f66-19c6-4a62-a522-fdebeccdd2e0&#34; class=&#34;&#34;&gt;For simplicity I went with a single package. Not ideal, because it ended up being very large package. So large, in fact, that Go 1.10 couldn&amp;#x27;t handle so many source files in a single package on Windows. Luckily it was fixed in Go 1.11.&#xA;  &lt;/div&gt;&#xA;  &lt;h3 id=&#34;1bcbdcc9-ed9c-46f2-a52f-4c4790b5721c&#34; class=&#34;&#34;&gt;Private, public, protected&#xA;  &lt;/h3&gt;&#xA;  &lt;div id=&#34;84bab2fd-bdfa-4f7e-a44f-78fbf0c7d98a&#34; class=&#34;&#34;&gt;Go&amp;#x27;s designers are under-appreciated. Their ability to simplify concepts is unmatched and access control is one example of that.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;8a84bd2f-9129-4d6b-9916-15ef68b194b5&#34; class=&#34;&#34;&gt;Other languages gravitate to fine-grained access control: public, private, protected specified with the smallest possible granularity (per class field and method).&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;6a975d37-2aec-4fae-81df-14ca80c3024d&#34; class=&#34;&#34;&gt;As a result a library implementing some functionality has the same access to other classes in the same library as external code using that library.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;99b201f1-ccd3-40cd-8d91-6eb163bee013&#34; class=&#34;&#34;&gt;Go simplified that by only having public vs. private and scoping access to package level.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;f44d4d8f-b0db-4358-a962-6a4999c9e35a&#34; class=&#34;&#34;&gt;That makes more sense. &#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;cd9e6ae7-4fe4-4504-8665-44c6dd2d5cc5&#34; class=&#34;&#34;&gt;When I write a library to, say, parse markdown, I don&amp;#x27;t want to expose internals of the implementation to users of the library. But hiding those internals from myself is counter-productive.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;fa0934d7-1f20-476a-91ee-09ef9063b273&#34; class=&#34;&#34;&gt;Java programmers noticed that issue and sometimes use an interface as a hack to fix over-exposed classes. By returning an interface instead of a a concrete class, you can hide some of the public APIs available to direct users of the class.&#xA;  &lt;/div&gt;&#xA;  &lt;h3 id=&#34;d8e321e3-c253-4023-b3f0-c8237cad6142&#34; class=&#34;&#34;&gt;Concurrency&#xA;  &lt;/h3&gt;&#xA;  &lt;div id=&#34;659863f6-b8ac-4360-81c4-1f47067313bf&#34; class=&#34;&#34;&gt;Go&amp;#x27;s concurrency is simply the best and a built-in race detector is of great help in repelling concurrency bugs.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;b52778b2-f905-4c32-871b-09724c3a423b&#34; class=&#34;&#34;&gt;That being said, in my first porting pass I went with emulating Java APIs. For example, I implemented a facsimile of Java&amp;#x27;s &lt;code&gt;CompletableFuture&lt;/code&gt; class.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;a10fc13f-c457-4e2a-b10d-18f0ad704f7c&#34; class=&#34;&#34;&gt;Only after the code was working I would re-structure it to be more idiomatic Go.&#xA;  &lt;/div&gt;&#xA;  &lt;h3 id=&#34;93db593f-dead-4683-94f3-f7d080751e3a&#34; class=&#34;&#34;&gt;Fluent function chaining&#xA;  &lt;/h3&gt;&#xA;  &lt;div id=&#34;339ec36d-e62a-4453-bbe9-831a80ee5061&#34; class=&#34;&#34;&gt;RavenDB has very sophisticated querying capabilities. Java client uses method chaining for building queries:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;List&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ReduceResult&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;results&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;session&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;query&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;User&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                        &lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;groupBy&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                        &lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;selectKey&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                        &lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;selectCount&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                        &lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;orderByDescending&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;count&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                        &lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;ofType&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ReduceResult&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                        &lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;toList&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;();&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;e09d2dac-48fa-46f8-943b-ca0b81711bb1&#34; class=&#34;&#34;&gt;This only works in languages that communicate errors via exceptions. When a function additionally returns an error, it&amp;#x27;s no longer possible to chain it like that.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;9545838e-57e9-4a47-959c-01acc08d9e8f&#34; class=&#34;&#34;&gt;To replicate chaining in Go I used a &amp;quot;stateful error&amp;quot; approach:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;type&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Query&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;error&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;q&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Query&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;WhereEquals&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;field&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;val&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;interface&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{})&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Query&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;q&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;q&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;c1&#34;&gt;// logic that might set q.err&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&#x9;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;q&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;q&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Query&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;GroupBy&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;field&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Query&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;q&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;q&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;c1&#34;&gt;// logic that might set q.err&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&#x9;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;q&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;q&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Query&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;Execute&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;result&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;inteface&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{})&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;error&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;q&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;q&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;c1&#34;&gt;// do logic&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;7a1f1ea5-9274-460f-b1a2-48024c727551&#34; class=&#34;&#34;&gt;This can be chained:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;result&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Foo&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;NewQuery&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;WhereEquals&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;Name&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;Frank&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;).&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;GroupBy&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;Age&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;).&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Execute&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;result&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;h3 id=&#34;26d5c1f1-ae57-4d21-91f3-08feed3dfab5&#34; class=&#34;&#34;&gt;JSON marshaling&#xA;  &lt;/h3&gt;&#xA;  &lt;div id=&#34;27fc2816-989b-42bd-bc5b-849e0a435824&#34; class=&#34;&#34;&gt;Java doesn&amp;#x27;t have a built-in marshaling and the client uses Jackson JSON library.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;1b30e153-e8ef-46f8-a295-95bbd067fd56&#34; class=&#34;&#34;&gt;Go has JSON support in standard library but it doesn&amp;#x27;t provide as many hooks for tweaking marshaling process.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;fdab1a32-3acf-4ab3-bccd-8920d909c765&#34; class=&#34;&#34;&gt;I didn&amp;#x27;t try to match all of Java&amp;#x27;s functionality as what is provided by Go&amp;#x27;s built-in JSON support seems to be flexible enough.&#xA;  &lt;/div&gt;&#xA;  &lt;h3 id=&#34;ae9f2e7f-0a80-42f9-9db7-ce5963228b8c&#34; class=&#34;&#34;&gt;Go code is shorter&#xA;  &lt;/h3&gt;&#xA;  &lt;div id=&#34;c6be0b1f-cd3c-4c7f-8f1a-9dac8822fe98&#34; class=&#34;&#34;&gt;This is not so much a property of Java but the culture which dictates what is considered an idiomatic code.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;07b6d978-4b74-4b5f-94e7-5732818fe47b&#34; class=&#34;&#34;&gt;In Java setter and getter methods are common. As a result, Java code:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Foo&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;kd&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;bar&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;setBar&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;bar&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;bar&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;bar&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;getBar&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;bar&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;bbb580be-9259-4870-8a57-83bdc192f205&#34; class=&#34;&#34;&gt;ends up in Go as:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;type&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Foo&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;Bar&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;f3328c9c-d624-4389-b79c-dea7de10028f&#34; class=&#34;&#34;&gt;3 lines vs. 11 lines. It does add up when you have a lot of classes with lots of members.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;310c2689-8448-415b-9ed1-ef03a461b187&#34; class=&#34;&#34;&gt;Most other code ends up being of equivalent length.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;6e64b428-a0d7-466f-a228-6f76f5947ec4&#34; class=&#34;&#34;&gt;Notion for organizing the work&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;5d035232-aae4-407a-9318-c469b3b0dbad&#34; class=&#34;&#34;&gt;I&amp;#x27;m a heavy user of &lt;a href=&#34;https://www.notion.so&#34;&gt;Notion.so&lt;/a&gt;. Simplifying a lot, Notion is a hierarchical note taking application. Think a cross of Evernote and a wiki, exquisitely designed and implemented by top notch software designers.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;0f5af36c-76e2-4b48-8724-50defd82174b&#34; class=&#34;&#34;&gt;Here&amp;#x27;s how I used Notion to organize my work on Go port:&#xA;  &lt;/div&gt;&#xA;&lt;img class=&#34;blog-img&#34; src=&#34;/img/842c3652a30dcf4c430b62f9594d05c605f12dc8.png&#34;&gt;&#xA;  &lt;div id=&#34;33c022ec-390c-4f12-8167-3f1a9fde546c&#34; class=&#34;&#34;&gt;Here&amp;#x27;s what&amp;#x27;s there:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;56bbd2b9-7648-4c39-9b7a-321679385cf9&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;not shown above, I have a page that is a calendar view where I take short notes about what I work on on a given day and how much time I spent. This is important information since it was a hourly contract. Thanks to those notes I know that I spent 601 hours over 11 months&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;clients like to know the progress. I had a page for each month were I summarized the work done like this:&#xA;    &lt;img class=&#34;blog-img&#34; src=&#34;/img/93ab5039a61400a8966adda9b1775025125ed050.png&#34;&gt;&#xA;      &lt;div id=&#34;c1e193bf-2ac5-4679-bfc7-2499241ced1c&#34; class=&#34;&#34;&gt;Those pages were shared with the client.&#xA;      &lt;/div&gt;&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;A short-term todo list helps when starting work each day:&#xA;    &lt;img class=&#34;blog-img&#34; src=&#34;/img/791f7ae2d106b220e48a56d599119b64452ace6b.png&#34;&gt;&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;I even managed invoices as Notion pages and used &amp;quot;Export to PDF&amp;quot; function to generate PDF version of the invoice&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;h2 id=&#34;08f3abc6-d3d6-4cb0-baf2-62578364c72c&#34; class=&#34;&#34;&gt;Additional resources&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;a15bd0bf-ed74-4cc8-b384-4c69c07692c1&#34; class=&#34;&#34;&gt;I&amp;#x27;ve provided some additional commentary in response to questions:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;098fa7ed-6217-418d-aa82-2b8dd3d39e72&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;in &lt;a href=&#34;https://news.ycombinator.com/item?id=19589614&#34;&gt;Hacker News discussion&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;in &lt;a href=&#34;https://old.reddit.com/r/golang/comments/ba0lsm/lessons_learned_porting_50k_loc_from_java_to_go/&#34;&gt;/r/golang discussion&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;div id=&#34;3355af87-12af-493d-8617-d9abfc05a587&#34; class=&#34;&#34;&gt;Other material:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;3747b873-c7b8-46b8-88b7-bc05284bef50&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;if you need a NoSQL, JSON document database, give &lt;a href=&#34;https://ravendb.net/&#34;&gt;RavenDB&lt;/a&gt; a try. It&amp;#x27;s chock full of advanced features&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;if you&amp;#x27;re programming in Go, try a free &lt;a href=&#34;https://www.programming-books.io/essential/go/&#34;&gt;Essential Go&lt;/a&gt; programming book&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;if you&amp;#x27;re interested in Notion, I&amp;#x27;m world&amp;#x27;s most advanced user of Notion:&#xA;      &lt;ul id=&#34;645ee41c-69f8-4357-ad8a-1a9784a2e525&#34; class=&#34;bulleted-list&#34;&gt;&#xA;        &lt;li&gt;I &lt;a href=&#34;/article/88aee8f43620471aa9dbcad28368174c/how-i-reverse-engineered-notion-api.html&#34;&gt;reverse engineered&lt;/a&gt; Notion API&#xA;        &lt;/li&gt;&#xA;        &lt;li&gt;I wrote an unofficial &lt;a href=&#34;https://github.com/kjk/notionapi&#34;&gt;Go library&lt;/a&gt; for Notion API&#xA;        &lt;/li&gt;&#xA;        &lt;li&gt;all content on this website is written in Notion and published with &lt;a href=&#34;/article/a8cf04d756ec4963905960822b004440/powering-a-blog-with-notion-and-netlify.html&#34;&gt;my custom toolchain&lt;/a&gt;&#xA;        &lt;/li&gt;&#xA;      &lt;/ul&gt;&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;&lt;/div&gt;</content>
  </entry>
  <entry>
   <title>Trade offs in designing versatile log format</title>
   <link href="https://blog.kowalczyk.info/article/fc9203f7c72a4532b1ae51d018fef7b3/trade-offs-in-designing-versatile-log-format.html" rel="alternate"></link>
   <updated>2019-03-25T00:00:00Z</updated>
   <id>tag:blog.kowalczyk.info,2019-03-25:/article/fc9203f7c72a4532b1ae51d018fef7b3/trade-offs-in-designing-versatile-log-format.html</id>
   <content type="html">&lt;p&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;notion-page&#34; id=&#34;fc9203f7-c72a-4532-b1ae-51d018fef7b3&#34;&gt;&#xA;  &lt;div id=&#34;91a851ff-e06f-4fa7-a1b8-56088c6157e7&#34; class=&#34;&#34;&gt;This article shows that when designing software even seemingly simple things are complicated and trade offs abound.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;d2348f01-8913-42e4-9d04-097a507f4443&#34; class=&#34;&#34;&gt;I wanted to log events to a file. I had several requirements for my design:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;cee41567-c2a2-4f9c-9ece-293fef9cfba5&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;it should be simple and therefore easy to implement&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;it should be human-readable&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;it should allow various types of events&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;div id=&#34;17a84120-c606-4558-a7a3-fd78ecea0758&#34; class=&#34;&#34;&gt;It&amp;#x27;s not a hard problem. &#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;1227e188-9c13-40e6-bf45-7a51ad1d1e05&#34; class=&#34;&#34;&gt;I could log it as stream of JSON objects. It would allow different types of events. It&amp;#x27;s easy to implement (in the sense that there&amp;#x27;s a library in every language for the hard part of encoding/decoding JSON).&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;2155d8c5-435a-47c8-b4fd-3a96c5879bd8&#34; class=&#34;&#34;&gt;One thing: it&amp;#x27;s not particularly human friendly. I wouldn&amp;#x27;t enjoy looking at &lt;code&gt;tail -f&lt;/code&gt; of a JSON log.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;2e2a4087-0223-4812-a1ff-0af50a3eab32&#34; class=&#34;&#34;&gt;How about something like Apache server logs:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;127.0.0.1 - frank [10/Oct/2000:13:55:36 -0700] &amp;#34;GET /apache_pb.gif HTTP/1.0&amp;#34; 200 2326 &amp;#34;http://www.example.com/start.html&amp;#34; &amp;#34;Mozilla/4.08 [en] (Win98; I ;Nav)&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;1048ebb8-aec9-404b-bcf9-bd00494141c6&#34; class=&#34;&#34;&gt;It&amp;#x27;s readable but doesn&amp;#x27;t allow for different types of events.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;77f50f3d-ebde-447f-a898-2197077f0438&#34; class=&#34;&#34;&gt;Implementing this format isn&amp;#x27;t challenging but the format is ad-hoc. For each kind of data I would have to write a completely new formatter / parser.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;9cf269ce-d5fb-43ee-ab65-5da4dd09d355&#34; class=&#34;&#34;&gt;How about something more structured:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ip: 127.0.0.1&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;request: GET /apache_pb.gif HTTP/1.0&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;status code: 200&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;4c17f5e1-7694-4ee0-b5d3-f134d6f3cfac&#34; class=&#34;&#34;&gt;We can write a library that serializes key / value pairs and that gives us ability to write different kinds of events to the same file with generic code.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;4aa61e37-3c9c-47fd-8f61-c8f1d0e8b74d&#34; class=&#34;&#34;&gt;But hold on, how do we know where one event ends and another starts?&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;363337c2-7dbe-4d62-85b9-ec1aa716cc84&#34; class=&#34;&#34;&gt;We must amend our format add add a record separator, like &lt;code&gt;---&lt;/code&gt;:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ip: 127.0.0.1&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;request: GET /apache_pb.gif HTTP/1.0&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;status code: 200&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;---&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;22954cc8-0b37-49fa-ac11-5f9b03299c7d&#34; class=&#34;&#34;&gt;We&amp;#x27;re not there yet. We use newline to separate lines that encode a single key / value pair. What if the value has a newline in it?&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;body: hello fred&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;I&amp;#39;m writing to you...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;0ea045cb-68ce-43a9-b208-06dc0a1290f4&#34; class=&#34;&#34;&gt;We could escape the value:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;body: hello fred\nI&amp;#39;m writing to you...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;e014c572-bb86-44fd-9be0-2ef06093ea83&#34; class=&#34;&#34;&gt;Escaping is not a good solution.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;f1028975-116b-45dd-8ef3-e5cda305c847&#34; class=&#34;&#34;&gt;Without escaping, we can just write out the data as-is.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;e77dd19b-6086-4209-96f9-db40ff07dc6b&#34; class=&#34;&#34;&gt;With escaping we have to scan the whole thing to determine if it needs escaping, and escape if it does. &#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;b66523ba-8271-4e86-a0ed-ec23ae9c5128&#34; class=&#34;&#34;&gt;Same goes for un-escaping.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;3f3f09cc-2303-4752-87a2-413321458d99&#34; class=&#34;&#34;&gt;This is slow and error prone.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;89ecb83d-2802-4c35-a0b9-b14fbc4816dd&#34; class=&#34;&#34;&gt;Furthermore, what if the value is really large? It won&amp;#x27;t be readable encoded on a single line.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;f3a05622-7d92-4f27-aff2-e77dd8ab63c6&#34; class=&#34;&#34;&gt;Let&amp;#x27;s amend our format to accommodate large values:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;body:+33&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;hello fred&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;I&amp;#39;m writing to you...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;8ad93a78-9e96-45de-b682-e1ff713ca9a3&#34; class=&#34;&#34;&gt;&lt;code&gt;33&lt;/code&gt; is the size of the value. Knowing the size we don&amp;#x27;t need escaping. It&amp;#x27;s faster, simpler to implement, more readable and supports binary data (like images).&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;1da0342b-6d2d-4d41-8d7c-3a5ceb8a71b2&#34; class=&#34;&#34;&gt;To formalize, key / value pair can be encoded in 2 ways:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;27aace91-0b23-41c7-ba85-6b9d40fbcfec&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;&lt;code&gt;${key}: ${value}\n&lt;/code&gt;&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;code&gt;${key}:+${sizeOfValue}\n${value}&lt;/code&gt;&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;div id=&#34;9d21b815-09ee-426e-94aa-c63ef34031bf&#34; class=&#34;&#34;&gt;Let&amp;#x27;s revisit the idea of using a separator:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ip: 10.0.0.1&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;request: GET / HTTP/1.0&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;---&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ip: 10.0.0.2&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;request: GET /index.html HTTP/1.0&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;---&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;ec48bb07-778a-402f-bd97-a99d7b12cf0a&#34; class=&#34;&#34;&gt;For delimiting records we can use the same tricks we used for encoding large values:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;36&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ip: 10.0.0.1&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;request: GET / HTTP/1.0&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;43&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;car: Toyota Corolla&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;year: 2018&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;price: 21000&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;b5fa4b6f-9650-4635-90a1-ec814dba7347&#34; class=&#34;&#34;&gt;First line is size of data as a string + newline. The data of this size follows.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;c39453a7-f8ef-4719-9568-00089e34c612&#34; class=&#34;&#34;&gt;This is very generic framing, agnostic to what is inside. It could be a picture, a JSON-encoded data or our key / value format.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;19c948a7-49ed-49e7-8f6f-d6a7e581f9e2&#34; class=&#34;&#34;&gt;We&amp;#x27;ve arrived at a layered design:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;2a528a33-a7ee-4233-939a-fb7102bb53d9&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;first layer is encoding arbitrary chunks of data by writing size and then data&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;second layer is key / value format inside the data&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;div id=&#34;6eee407c-1b56-41f3-9f7f-d1f8bb25b731&#34; class=&#34;&#34;&gt;It would be even simpler if the size was 8 byte, 64-bit integer. It wouldn&amp;#x27;t be human-readable, though, so I picked a slightly more complicated, string-based encoding.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;3143be67-3471-4c02-9e27-f683645add34&#34; class=&#34;&#34;&gt;It&amp;#x27;s not quite right yet. &#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;4391f252-0777-4dc3-b120-a6569ebde52a&#34; class=&#34;&#34;&gt;Above we have logged an HTTP requests info and car info in the same file. &#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;b9c1180e-4ad1-487c-9651-212a2c0b81d0&#34; class=&#34;&#34;&gt;How do we know what type of record did we read?&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;20bec5ae-9907-48db-9f25-18ccf46a2562&#34; class=&#34;&#34;&gt;Let&amp;#x27;s amend our framing with optional name:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;36 httplog&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ip: 10.0.0.1&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;request: GET / HTTP/1.0&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;43 carinfo&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;car: Toyota Corolla&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;year: 2018&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;price: 21000&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;39300743-f394-40ca-b87a-75759f33cc90&#34; class=&#34;&#34;&gt;Adding optional name (&lt;code&gt;httplog&lt;/code&gt; and &lt;code&gt;carinfo&lt;/code&gt;) allows us to know what kind of data is encoded in a given chunk of data.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;3abf3ab4-14b9-41da-9748-8c184174d8da&#34; class=&#34;&#34;&gt;Finally let&amp;#x27;s add non-optional timestamp in &lt;a href=&#34;https://en.wikipedia.org/wiki/Unix_time&#34;&gt;Unix Epoch&lt;/a&gt; format in milliseconds:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;36 1553564864010 httplog&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ip: 10.0.0.1&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;request: GET / HTTP/1.0&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;43 1553564864115 carinfo&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;car: Toyota Corolla&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;year: 2018&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;price: 21000&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;05bb2f05-5d3b-4af7-b83b-16be1f436308&#34; class=&#34;&#34;&gt;Unlike name, which is optional, I decided timestamp is not optional. You can set it to 0 if you don&amp;#x27;t need it but for most logging needs you want a timestamp.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;10bc8cbd-a7fa-429c-b719-2ef8bf2af9d8&#34; class=&#34;&#34;&gt;Traditional Unix Epoch has precision of a second and that seems not enough. Millisecond-precision seems good enough. Nanosecond was also an option but seems like an over-kill.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;a2b8c156-0957-4df5-8267-d9853fb790a3&#34; class=&#34;&#34;&gt;What if you need more structure than key / value pairs? &#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;1f8042fe-738d-41a2-8717-817b31f78474&#34; class=&#34;&#34;&gt;Simplicity means that you can&amp;#x27;t implement every possible feature. This format doesn&amp;#x27;t preclude more structure: you can always use JSON as the value in key / value field. It&amp;#x27;s just not going to be as easy to use.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;b9fbe048-6bef-493c-a4bb-cfb02ec41bf7&#34; class=&#34;&#34;&gt;Implementing the thing&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;32b3af3d-6aab-4260-bde4-21dde83ae8c7&#34; class=&#34;&#34;&gt;This is not just theoretical exploration. I&amp;#x27;ve implemented this format as a &lt;a href=&#34;https://github.com/kjk/siser&#34;&gt;Go package siser&lt;/a&gt;.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;66e61816-8cc0-4291-805a-0d35a6590f75&#34; class=&#34;&#34;&gt;It took me 2 days to implement. It&amp;#x27;s just under 500 lines of code:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ wc -l reader.go record.go util.go writer.go&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;     167 reader.go&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;     196 record.go&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      79 util.go&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      57 writer.go&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;     499 total&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;dab233e6-33a3-426a-8c7d-09508a2fa007&#34; class=&#34;&#34;&gt;Not counting tests but I do have them because I need this code to be rock solid. Storing data is serious business and has to be reliable.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;d83a7700-bfe8-4617-9503-c1f9b860e3ee&#34; class=&#34;&#34;&gt;How fast is it? I benchmarked it against JSON encoding:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;BenchmarkSiserMarshal-12      &#x9; 1000000&#x9;      1136 ns/op&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;BenchmarkJSONMarshal-12       &#x9; 1000000&#x9;      1407 ns/op&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;BenchmarkSiserUnmarshal-12    &#x9; 5000000&#x9;       374 ns/op&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;BenchmarkJSONUnmarshal-12     &#x9;  500000&#x9;      3353 ns/op&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;3b726c0e-34e0-4244-847f-d3f813d2d8c2&#34; class=&#34;&#34;&gt;The benchmark is mostly to make sure that I didn&amp;#x27;t make a big performance mistake. It would be embarrassing to be slower than JSON encoding. As a side note: it&amp;#x27;s impressive how  fast JSON marshaling in Go standard library is.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;58e72b49-ebf4-4ce3-bffd-b1d5dc0f33e4&#34; class=&#34;&#34;&gt;I&amp;#x27;m moving all my logging needs to this format.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;d20027d5-d31d-442c-b896-bbc462ffb064&#34; class=&#34;&#34;&gt;This format is also good for very simple stores aka databases. The file can be seen as an append-only database log. To update a record I just write a new entry and it&amp;#x27;ll over-write earlier entry.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;fe36fee7-3795-4bff-aeab-5f9e1e5f47e3&#34; class=&#34;&#34;&gt;The roads not taken&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;27baac34-a3cb-4948-9744-877b483146ea&#34; class=&#34;&#34;&gt;After reading this you might not be impressed. This design is clearly inspired by many others. All I did is put known things in a specific, but very familiar, way.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;a2e367aa-d41a-42ff-be83-c92ec9ba33e1&#34; class=&#34;&#34;&gt;Simplicity is insidiously non-trivial. The design described here is a v2 of siser library. First design used &lt;code&gt;---&lt;/code&gt; for record separator, didn&amp;#x27;t have name and timestamp.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;b9a4c6c4-875f-484b-af9f-0a273c40b91c&#34; class=&#34;&#34;&gt;Only several months after first version I got enough insight to improve it.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;9f2783fb-fa39-4262-91f5-683966302dbd&#34; class=&#34;&#34;&gt;In turn siser was an evolution of less robust ideas I implemented earlier. It took experiencing the limitations of those earlier designs in real use for better ideas to emerge.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;49241da0-7453-4ac3-bf45-50adde8398f4&#34; class=&#34;&#34;&gt;This is even more apparent when you look at mistake of others.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;41c79182-92ca-4a39-a113-89be41d397a8&#34; class=&#34;&#34;&gt;MIME is a format used for encoding e-mail messages. While in some ways it&amp;#x27;s very close to this format, they made a mistake of using a boundary string for separating multiple parts. Compared to framing data with size prefix it&amp;#x27;s so much hard to implement. A MIME decoder is more than 500 lines of code and doesn&amp;#x27;t offer more features.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;72c1598f-2c70-44c0-8949-87644bd45adc&#34; class=&#34;&#34;&gt;Other example of massive mistakes of the past is choosing XML as a format for describing ant or Visual Studio build files. XML is super slow, unreadable for humans, hard to work with programmatically. A conforming implementation of XML parser requires thousands of lines of code.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;898ff87e-0d3a-439a-afab-803fe2ccc07b&#34; class=&#34;&#34;&gt;Obvious things are often only obvious in hindsight.&#xA;  &lt;/div&gt;&#xA;&lt;/div&gt;</content>
  </entry>
  <entry>
   <title>How I implemented Oembed Proxy for GitHub</title>
   <link href="https://blog.kowalczyk.info/article/7d25ad342c514a47a00f6e0c4ba84cbc/how-i-implemented-oembed-proxy-for-github.html" rel="alternate"></link>
   <updated>2018-10-13T00:00:00Z</updated>
   <id>tag:blog.kowalczyk.info,2018-10-13:/article/7d25ad342c514a47a00f6e0c4ba84cbc/how-i-implemented-oembed-proxy-for-github.html</id>
   <content type="html">&lt;p&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;notion-page&#34; id=&#34;7d25ad34-2c51-4a47-a00f-6e0c4ba84cbc&#34;&gt;&#xA;  &lt;h1 id=&#34;50ecc9bf-659d-4ab1-b41c-e39b96342586&#34; class=&#34;&#34;&gt;Why Oembed Proxy for GitHub&#xA;  &lt;/h1&gt;&#xA;  &lt;div id=&#34;0342a078-44f3-485e-8a77-be67de7d4613&#34; class=&#34;&#34;&gt;I&amp;#x27;m writing a programming book &lt;a href=&#34;https://www.programming-books.io/essential/go/&#34;&gt;Essential Go&lt;/a&gt; in &lt;a href=&#34;https://www.notion.so&#34;&gt;Notion&lt;/a&gt; and I need to include code snippets.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;9099794b-18ac-467c-ba50-b0218deafcbf&#34; class=&#34;&#34;&gt;Notion has support for code blocks but it&amp;#x27;s not good enough for my use case. &#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;8cdeeb0e-c63d-41df-a7b6-f1e5e3ec0f36&#34; class=&#34;&#34;&gt;I want to make sure the code compiles so I write small programs and store them in GitHub repository. My custom book building script compiles and runs the programs to ensure the code is correct.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;03781c8c-3321-4e0a-b873-3f6c8382d587&#34; class=&#34;&#34;&gt;Notion supports embedding GitHub&amp;#x27;s gists but not files from git repositories hosted on GitHub.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;3d05208b-8f89-4e34-8bac-94b5924c864c&#34; class=&#34;&#34;&gt;I researched things and turns out there&amp;#x27;s a standard called &lt;a href=&#34;https://oembed.com/&#34;&gt;Oembed&lt;/a&gt; that was created to enable embedding arbitrary content from one website in another. &#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;26019607-fd7e-4577-af9f-e9cc97cfb18d&#34; class=&#34;&#34;&gt;Notion supports &lt;a href=&#34;https://oembed.com/&#34;&gt;Oembed&lt;/a&gt;.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;03d173b8-36a5-44a0-851d-66a67297c79b&#34; class=&#34;&#34;&gt;I didn&amp;#x27;t find existing service that can provide Oembed support for GitHub repositories so I built one myself.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;f23e98f5-b35d-43e5-9f20-302c018cb2fd&#34; class=&#34;&#34;&gt;This article describes the high-level design of &lt;a href=&#34;https://www.onlinetool.io/gitoembed/&#34;&gt;Oembed Proxy for GitHub&lt;/a&gt;.&#xA;  &lt;/div&gt;&#xA;  &lt;h1 id=&#34;0fcdad98-0edc-49b7-9293-d5f159c2de96&#34; class=&#34;&#34;&gt;What is Oembed?&#xA;  &lt;/h1&gt;&#xA;  &lt;div id=&#34;ee4dc7e9-1ca9-419a-be9c-31c823f39f26&#34; class=&#34;&#34;&gt;Let&amp;#x27;s say you&amp;#x27;re implementing a rich-text editor on the web and you want to allow embedding arbitrary content from other services: a tweet, a youtube video, a flickr photo.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;c8b1df28-f220-41f4-ae87-7b1328a67eaf&#34; class=&#34;&#34;&gt;You can add code to support each service you know about (and pray that they provide a stable way to get the necessary information) but it&amp;#x27;s not scalable. There are way too many web services out there and more are created every day.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;b97ae744-9270-4ceb-9713-145bb11a70c7&#34; class=&#34;&#34;&gt;Some people noticed the problem and created Oembed protocol that provides a standard way to expose content for embedding. Now you need to only write code to support Oembed.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;0f5fb71d-2698-4b58-ac25-f67e232c36d5&#34; class=&#34;&#34;&gt;Here&amp;#x27;s how it works, using Oembed Proxy for GitHub as an example.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;9ac12dfb-e003-4bd9-b58d-4d3cfccb1620&#34; class=&#34;&#34;&gt;In our example Notion is an Oembed client that wants to embed a file from GitHub repository &lt;a href=&#34;https://github.com/essentialbooks/books/blob/master/README.md&#34;&gt;https://github.com/essentialbooks/books/blob/master/README.md&lt;/a&gt; in the body of a document.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;918b5b97-9413-480d-837c-0d11810ef6b9&#34; class=&#34;&#34;&gt;Notion supports Oembed standard. If GitHub supported Oembed on their servers, you would add embed block and use &lt;a href=&#34;https://github.com/essentialbooks/books/blob/master/README.md&#34;&gt;https://github.com/essentialbooks/books/blob/master/README.md&lt;/a&gt; link directly.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;602171f0-0626-42a3-be21-95f1c5c17e57&#34; class=&#34;&#34;&gt;GitHub doesn&amp;#x27;t support Oembed so instead, you can use URL via my Oembed Proxy: &lt;a href=&#34;https://www.onlinetool.io/gitoembed/widget?url=https%3A%2F%2Fgithub.com%2Fessentialbooks%2Fbooks%2Fblob%2Fmaster%2FREADME.md&#34;&gt;https://www.onlinetool.io/gitoembed/widget?url=https%3A%2F%2Fgithub.com%2Fessentialbooks%2Fbooks%2Fblob%2Fmaster%2FREADME.md&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;31087424-3c51-42b1-8725-2d4a057efdcf&#34; class=&#34;&#34;&gt;This is &lt;a href=&#34;https://www.onlinetool.io/gitoembed/widget&#34;&gt;https://www.onlinetool.io/gitoembed/widget&lt;/a&gt; page with GitHub link provided as &lt;code&gt;url&lt;/code&gt; argument.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;56974adf-f5d3-46c2-9077-1f6a07ec887a&#34; class=&#34;&#34;&gt;If you view that page it&amp;#x27;s the file from GitHub with source code highlighting.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;ab71a6c1-de30-423d-8eaf-fa30747294fa&#34; class=&#34;&#34;&gt;Oembed supports auto-discovery. If you peek at HTML of that page, you&amp;#x27;ll see this is &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; section:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;link&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;rel&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;alternate&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;application/json+oembed&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;href&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;https://www.onlinetool.io/gitoembed/oembed?format=json&amp;amp;url=https://github.com/essentialbooks/books/blob/master/README.md&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;title&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;README.md&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;/&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;link&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;rel&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;alternate&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;text/xml+oembed&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;href&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;https://www.onlinetool.io/gitoembed/oembed?format=xml&amp;amp;url=https://github.com/essentialbooks/books/blob/master/README.md&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;title&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;README.md&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;5247c6a7-06bb-471a-9d08-0fa09edda3a1&#34; class=&#34;&#34;&gt;Those are instructions telling Oembed client (Notion in this example) how to get embeddable HTML.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;4d71dbdf-a97b-49bd-807a-1eb1cad4befc&#34; class=&#34;&#34;&gt;Oembed supports 2 formats for providing this information: JSON and XML. In my testing Notion worked with just &lt;code&gt;application/json+oembed&lt;/code&gt; but I implemented both just in case other clients only understand XML.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;eaa73f19-d6be-4819-9e68-6627d14492f8&#34; class=&#34;&#34;&gt;Oembed client parses HTML to extract those links and, if present, gets Oembed information. In our example it&amp;#x27;s in &lt;a href=&#34;https://www.onlinetool.io/gitoembed/oembed?format=json&amp;amp;url=https://github.com/essentialbooks/books/blob/master/README.md&#34;&gt;https://www.onlinetool.io/gitoembed/oembed?format=json&amp;amp;url=https://github.com/essentialbooks/books/blob/master/README.md&lt;/a&gt; and looks like this:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nt&#34;&gt;&amp;#34;version&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nt&#34;&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;rich&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nt&#34;&gt;&amp;#34;provider_name&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;gitoembed&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nt&#34;&gt;&amp;#34;provider_url&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;https://www.onlinetool.io/gitoembed/&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nt&#34;&gt;&amp;#34;height&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;320&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nt&#34;&gt;&amp;#34;width&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;720&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nt&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;README.md&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nt&#34;&gt;&amp;#34;html&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;\u003ciframe width=\&amp;#34;100%\&amp;#34; height=320 src=\&amp;#34;https://www.onlinetool.io/gitoembed/widget?url=https://github.com/essentialbooks/books/blob/master/README.md\&amp;#34; frameborder=\&amp;#34;0\&amp;#34; onload=\&amp;#34;resizeFrame(this);\&amp;#34;\u003e\u003c/iframe\u003e&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;567c2e73-4801-4ab9-980a-7babca1d9e2f&#34; class=&#34;&#34;&gt;I hope the format is mostly self-explanatory.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;200af81b-59ff-4557-aac7-b290a0344fb5&#34; class=&#34;&#34;&gt;The interesting bit is &lt;code&gt;html&lt;/code&gt; field, which is:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;iframe&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;na&#34;&gt;width&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;100%&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;na&#34;&gt;height&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;320&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;na&#34;&gt;src&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;https://www.onlinetool.io/gitoembed/widget?url=https://github.com/essentialbooks/books/blob/master/README.md&amp;#34;&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;na&#34;&gt;frameborder&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;0&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;na&#34;&gt;onload&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;resizeFrame(this);&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;iframe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;b87d4620-a206-4ef7-a926-f2f0d8160b9f&#34; class=&#34;&#34;&gt;We could send the actual HTML content to insert but it&amp;#x27;s more customary to send an iframe which loads the html.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;fb38d790-a25d-4ca1-8bb7-5552a63a52b0&#34; class=&#34;&#34;&gt;In my implementation &lt;code&gt;src&lt;/code&gt; of the iframe is the same page from which we extracted Oembed JSON link so it serves double-duty as both the content and an Oembed pointer to the content. Those could be different URLs.&#xA;  &lt;/div&gt;&#xA;  &lt;h1 id=&#34;2cd6f6c9-0f72-4e9a-a399-843ec19d4e45&#34; class=&#34;&#34;&gt;Implementation details of Oembed Proxy for GitHub&#xA;  &lt;/h1&gt;&#xA;  &lt;div id=&#34;1c592e14-c89f-46da-92bb-f09308a04d63&#34; class=&#34;&#34;&gt;First I needed a server. Usually, I use Digital Ocean but this time I went for biggest bang for the buck and used &lt;a href=&#34;https://www.scaleway.com/pricing/&#34;&gt;C2L server&lt;/a&gt; from &lt;a href=&#34;https://www.scaleway.com&#34;&gt;Scaleway&lt;/a&gt;.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;d5f0664f-9fdb-4355-b788-e335e4126505&#34; class=&#34;&#34;&gt;For ~$30 I  get 8 core server with 32 GB of RAM, 250 GB SSD drive and 600 MBits/s unmetered bandwidth. It&amp;#x27;s a bare metal server, not a VPS, so it&amp;#x27;s all mine, eliminating risk of noisy neighbors.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;739c2c63-078a-46de-a564-7bd7bbd9dc2a&#34; class=&#34;&#34;&gt;On Digital Ocean the closest server with such specs would be $160. I keep a list of &lt;a href=&#34;/article/dd5c0a813dfe4487a6cd432f82c0c2fc/comparing-prices-of-vps-servers.html&#34;&gt;cheap VPS servers&lt;/a&gt; for comparison.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;95b8bfa9-e358-44e2-bc04-cfd0ac289310&#34; class=&#34;&#34;&gt;The downside is that the servers are in Europe (you can choose between Amsterdam or Paris) so the latency for users in US will be higher than if the server was hosted in US.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;ca9b5751-9015-4df1-bb1f-556a5deb163a&#34; class=&#34;&#34;&gt;For the OS I went with Ubuntu 18.04. I know it best and it&amp;#x27;s one of the most popular distros.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;9bd5f6da-8196-4531-86b3-1ac67c111014&#34; class=&#34;&#34;&gt;The server is written in Go. It&amp;#x27;s my go-to language for writing backend code.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;0f62515e-21c9-4513-a964-1aba53e88fd5&#34; class=&#34;&#34;&gt;The service isn&amp;#x27;t very complicated:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;c35eb523-574e-422e-bfc9-f01048ac8f32&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;it downloads the file via GitHub&amp;#x27;s &lt;a href=&#34;https://raw.githubusercontent.com/&#34;&gt;https://raw.githubusercontent.com&lt;/a&gt; url&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;to avoid over-loading GitHub servers (and exceeding their throttling limits) I cache downloaded files for a day. It&amp;#x27;s not infinitely long cache because files on GitHub can  change and I don&amp;#x27;t want to cache outdated version forever&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;for code highlighting I use &lt;a href=&#34;https://github.com/alecthomas/chroma&#34;&gt;chroma&lt;/a&gt; library&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;div id=&#34;2615c1cb-f2f2-4e56-af63-54584959c31e&#34; class=&#34;&#34;&gt;There&amp;#x27;s even less of front-end code. Explanation of the service and a way to test it implemented with a form and few lines of JavaScript.&#xA;  &lt;/div&gt;&#xA;&lt;/div&gt;</content>
  </entry>
  <entry>
   <title>Powering a blog with Notion and Netlify</title>
   <link href="https://blog.kowalczyk.info/article/a8cf04d756ec4963905960822b004440/powering-a-blog-with-notion-and-netlify.html" rel="alternate"></link>
   <updated>2018-07-30T00:00:00Z</updated>
   <id>tag:blog.kowalczyk.info,2018-07-30:/article/a8cf04d756ec4963905960822b004440/powering-a-blog-with-notion-and-netlify.html</id>
   <content type="html">&lt;p&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;notion-page&#34; id=&#34;a8cf04d7-56ec-4963-9059-60822b004440&#34;&gt;&#xA;  &lt;div id=&#34;a863c05b-9961-4fdb-9677-35ad0a52b6aa&#34; class=&#34;&#34;&gt;The last iteration of this blog was a Go program running on &lt;a href=&#34;https://www.digitalocean.com/&#34;&gt;Digital Ocean&lt;/a&gt;&amp;#x27;s cheapest VM ($5/month). &#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;ba51eda9-5969-496d-a45c-e52dad0e74a6&#34; class=&#34;&#34;&gt;Recently I&amp;#x27;ve made 2 big changes:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;9b86554d-a122-4b7f-b4d2-24ad97b0d775&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;I converted it to a static site hosted on &lt;a href=&#34;https://www.netlify.com/&#34;&gt;Netlify&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;I used &lt;a href=&#34;https://notion.so&#34;&gt;Notion&lt;/a&gt; for writing the posts instead of writing markdown files in a text editor&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;h1 id=&#34;412aa587-b340-42ed-9ed9-33b9ffcdb552&#34; class=&#34;&#34;&gt;Moving to Netlify&#xA;  &lt;/h1&gt;&#xA;  &lt;div id=&#34;8e092b02-3159-463d-86e1-e926b56e84e3&#34; class=&#34;&#34;&gt;My blog was effectively a static website. It didn&amp;#x27;t need a backend so writing a custom server and running it on a VPS was overkill.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;3a5046cc-5389-44a1-9b9a-e940b43cdf38&#34; class=&#34;&#34;&gt;Few months back Netlify reduced the price of their cheapest plan to $0 (from $10/month).&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;190d7a80-58fd-4d68-911b-06a0a4bd21d4&#34; class=&#34;&#34;&gt;I&amp;#x27;m always looking to simplify and cheapify my life so I bit the bullet  and converted my custom server to generate static HTML files suitable for hosting on Netlify.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;f021dfda-af37-4ffc-a318-d7592f059fd6&#34; class=&#34;&#34;&gt;If you want to publish a static website and are starting from scratch, the best approach is to use one of the many static site generators (e.g. Hugo or Jekyll).&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;20a5d368-b6cf-4292-bf5f-b6c84698f2f0&#34; class=&#34;&#34;&gt;I already had a lot of content in .html and markdown files accumulated over the years and code to generate the website for serving from custom web server so I refactored to code to generate static HTML instead.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;618f3ab6-7078-46a4-aa0e-3a5152a7fdda&#34; class=&#34;&#34;&gt;Refactoring process was time consuming but simple. &#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;5b51c19d-73dc-455d-beb3-34e17ca4784c&#34; class=&#34;&#34;&gt;For dynamically generated html I changed the code to generate a .html file and setup appropriate url =&amp;gt; file mapping using &lt;code&gt;_redirect&lt;/code&gt;  file.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;48f58f6e-8a4b-45c5-aa7c-6f40327d5470&#34; class=&#34;&#34;&gt;For local testing I use &lt;a href=&#34;https://caddyserver.com/&#34;&gt;Caddy&lt;/a&gt; and generate &lt;code&gt;Caddyfile&lt;/code&gt; with appropriate redirects. There are minor differences when testing locally because there are semantic differences between redirect capabilities of Netlify and Caddy but it&amp;#x27;s good enough.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;c5c42bd1-680f-4f94-aa75-edf67e3e8393&#34; class=&#34;&#34;&gt;Netlify also has an option to do a draft deploy under a unique URL. This is good for previewing the changes before publishing.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;b1c7159a-d3ea-4f94-aa3e-5a5f2cec1cf4&#34; class=&#34;&#34;&gt;At the end of code refactoring I effectively ended up with a custom static site generator.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;7833465b-ecde-4907-a863-8ab12b2dc4a7&#34; class=&#34;&#34;&gt;The verdict&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;317bf520-023a-498e-8f03-c27f01ce9ebf&#34; class=&#34;&#34;&gt;I&amp;#x27;m happy with the result.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;6a6d0375-b0ae-4ab2-96bd-3faf16da0ca7&#34; class=&#34;&#34;&gt;Netlify has all the features I care about for a basic website.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;a9bc45f3-a170-4397-b241-f9a372c1685d&#34; class=&#34;&#34;&gt;Notably they provide free SSL with Let&amp;#x27;s Encrypt, allow custom domains and have capable redirect capabilities. They use CDN so should be faster than hosting on a single host.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;74aa0779-8e16-4453-841f-878ed7f3b1a3&#34; class=&#34;&#34;&gt;The only thing I miss is being able to see analytics for 404. With my own server I was logging all requests for pages that don&amp;#x27;t exist on my server. Many of them were bots trying to hack me via known vulnerabilities in popular software like WordPress but sometimes it would be caused by my mistake or a request for a valid article incorrectly linked. Seeing those I was able to fix most of them by adding redirects.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;3b85fe5d-f7d2-4ae9-84eb-834502a5fa6a&#34; class=&#34;&#34;&gt;I hope Netlify can sustain their generous free plan. Luckily there are plenty of options to host a static website so even if Netlify goes under, it&amp;#x27;ll be easy to move somewhere else, like &lt;a href=&#34;https://firebase.google.com/docs/hosting/&#34;&gt;Firebase Hosting&lt;/a&gt;, &lt;a href=&#34;http://surge.sh/&#34;&gt;surge.sh&lt;/a&gt;, &lt;a href=&#34;https://pages.github.com/&#34;&gt;GitHub pages&lt;/a&gt;, &lt;a href=&#34;https://about.gitlab.com/features/pages/&#34;&gt;GitLab pages&lt;/a&gt; and &lt;a href=&#34;/article/c674bebe8adf44d18c3a36cc18c131e2/web-services-for-hosting-static-websites.html&#34;&gt;many others&lt;/a&gt;.&#xA;  &lt;/div&gt;&#xA;  &lt;h1 id=&#34;9401ecf0-dd7d-4251-887e-4b76cb00f880&#34; class=&#34;&#34;&gt;Using Notion as Content Management System&#xA;  &lt;/h1&gt;&#xA;  &lt;div id=&#34;141ed7e7-bee3-4875-8f71-b80dc34eeab2&#34; class=&#34;&#34;&gt;The easier it is to write, the more I write.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;ac1ca7fb-ab5c-4efc-9399-13cb2ceb4c69&#34; class=&#34;&#34;&gt;Using markdown files fails the &amp;quot;as easy as possible&amp;quot; part.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;0766b60d-c1a8-4e4e-977f-c14edcfea2b7&#34; class=&#34;&#34;&gt;In absolute terms, creating a new markdown file doesn&amp;#x27;t take much time.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;096fd318-5ae8-4e8a-9a5d-2c5b473bace7&#34; class=&#34;&#34;&gt;In practice it&amp;#x27;s enough friction to deter me from writing. In my worst year I only wrote 1 new blog post.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;2f073a1a-df87-44f2-a0cd-8ede68019013&#34; class=&#34;&#34;&gt;In a perfect world I would open an app and start writing. When I&amp;#x27;m done writing I would publish with a click of a button.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;acd49162-335b-4c90-9afe-86dd0a42bd44&#34; class=&#34;&#34;&gt;Enter Notion&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;0c430be9-1f4b-4bf7-9710-a17ed66846b2&#34; class=&#34;&#34;&gt;&lt;a href=&#34;https://www.notion.so/&#34;&gt;Notion&lt;/a&gt; is as close to a perfect writing tool as it gets: open a page and start writing.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;573d7116-684d-437c-bb60-232a9c545c65&#34; class=&#34;&#34;&gt;The problem is: all that content is trapped in Notion. You can publish (publicly share) a page but I want more flexibility:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;cfabbd2a-b98b-4b14-9fa8-9f11b335cf8a&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;I want to host on my own domain; my website is partly a tool for marketing myself&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;I want integration with Google Analytics&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;I want a custom design&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;I want to provide rss feed&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;div id=&#34;44506055-483c-475b-9f3e-bd0671eddeba&#34; class=&#34;&#34;&gt;Thankfully I&amp;#x27;m a programmer: if something can be done with software, I can do it.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;10ed8530-ee7d-4b96-a947-596cdf85438a&#34; class=&#34;&#34;&gt;I started a one-man Notion Liberation Front. My goal: liberate content trapped inside Notion.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;7eb7882f-4598-45f2-8ebe-9070dca720d9&#34; class=&#34;&#34;&gt;I &lt;a href=&#34;/article/88aee8f43620471aa9dbcad28368174c/how-i-reverse-engineered-notion-api.html&#34;&gt;reverse-engineered their API&lt;/a&gt;, wrote a &lt;a href=&#34;https://github.com/kjk/notionapi&#34;&gt;Go library&lt;/a&gt; and after another round of code refactoring I had my blog powered by Notion thanks to this &lt;a href=&#34;https://github.com/kjk/blog&#34;&gt;Go program&lt;/a&gt;.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;6ea092b9-1f85-4124-b1c8-50eb556ed969&#34; class=&#34;&#34;&gt;I imported my old blog posts into Notion. They have a decent markdown importer although I did have to do some cleaning up.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;57dadc37-ad4b-415a-9af4-dad640a37f5b&#34; class=&#34;&#34;&gt;I went even further and published most of my notes to my website as well. I still have many private notes in Notion but most of them can be just as well be publicly visible.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;0dfa242c-26db-4d95-aea9-1976457fc6e0&#34; class=&#34;&#34;&gt;There&amp;#x27;s no way I could do publish so much content without Notion.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;ecd25b0d-de7f-4a73-b843-84c83a6eb6ee&#34; class=&#34;&#34;&gt;I also automated publishing by using cron functionality in &lt;a href=&#34;https://travis-ci.org/&#34;&gt;Travis CI&lt;/a&gt;. Every day a script downloads latest content from Notion, caches it in git repository and re-generates a website.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;dd38c65f-4ac4-408a-bae3-ad25de20c351&#34; class=&#34;&#34;&gt;Everything now runs on auto-pilot. I can just write new articles in Notion and my website will be automatically updated every day.&#xA;  &lt;/div&gt;&#xA;&lt;/div&gt;</content>
  </entry>
  <entry>
   <title>How I reverse engineered Notion API</title>
   <link href="https://blog.kowalczyk.info/article/88aee8f43620471aa9dbcad28368174c/how-i-reverse-engineered-notion-api.html" rel="alternate"></link>
   <updated>2018-07-23T00:00:00Z</updated>
   <id>tag:blog.kowalczyk.info,2018-07-23:/article/88aee8f43620471aa9dbcad28368174c/how-i-reverse-engineered-notion-api.html</id>
   <content type="html">&lt;p&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;notion-page&#34; id=&#34;88aee8f4-3620-471a-a9db-cad28368174c&#34;&gt;&#xA;  &lt;div id=&#34;b3bb6c55-3ad2-4b38-9f0f-fd42366105f9&#34; class=&#34;&#34;&gt;&lt;a href=&#34;/article/88aee8f43620471aa9dbcad28368174c/how-i-reverse-engineered-notion-api.html&#34;&gt;Notion&lt;/a&gt; is a great tool for writing but the content is trapped inside the web app.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;22aad22c-e1b8-4c8e-bcaa-ee5d699cae61&#34; class=&#34;&#34;&gt;The company is working on an official API but I&amp;#x27;m impatient.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;750d31b6-2d14-42bd-a8ea-069c35244db2&#34; class=&#34;&#34;&gt;This article describes how I reverse engineered their API and created a Go library &lt;a href=&#34;https://github.com/kjk/notionapi&#34;&gt;notionapi&lt;/a&gt;.&#xA;  &lt;/div&gt;&#xA;  &lt;h1 id=&#34;f97ccfc4-fbe2-4dd7-9571-caf6c56b5a1f&#34; class=&#34;&#34;&gt;It all began with a failure.&#xA;  &lt;/h1&gt;&#xA;  &lt;div id=&#34;5351e604-7556-4c12-9090-057500fd9251&#34; class=&#34;&#34;&gt;My first attempt at extracting notion content was traditional web scraping.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;4fa7289a-2886-4ca8-9016-e1abd0be739b&#34; class=&#34;&#34;&gt;I found a Python &lt;a href=&#34;https://github.com/shariq/notion-on-firebase&#34;&gt;script&lt;/a&gt; that uses Selenium to recursively spider a Notion page and publish it to Firebase Hosting.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;7edac8dd-067f-4503-94af-f0e9dfc1c23c&#34; class=&#34;&#34;&gt;I ported it to Node to use &lt;a href=&#34;/article/ea07db1b9bff415ab180b0525f3898f6/advanced-web-spidering-with-puppeteer.html&#34;&gt;Puppeteer&lt;/a&gt; (better technology than Selenium).&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;5a7eb4a1-add5-4dcf-83c9-7effa04211ff&#34; class=&#34;&#34;&gt;While it worked this approach is limited to getting a verbatim HTML of the pages as they are rendered by the Notion application.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;785b173c-9617-44a2-95e7-ec2747ea3d42&#34; class=&#34;&#34;&gt;I wanted to be able to change the look of the page, add elements like footers and headers and navigation bar.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;72918988-57bb-474e-889f-44cf5f5477b9&#34; class=&#34;&#34;&gt;I briefly considered trying to reconstruct the structure of the page from rendered HTML but at best that would be a lot of ugly guesswork.&#xA;  &lt;/div&gt;&#xA;  &lt;h1 id=&#34;7bd8bb4e-7ce8-4269-9a5f-099d8b59e190&#34; class=&#34;&#34;&gt;The lightbulb moment&#xA;  &lt;/h1&gt;&#xA;  &lt;div id=&#34;22863798-5e5e-479c-8a20-3a7da0e4ba74&#34; class=&#34;&#34;&gt;Modern Single Page Applications (SPA) work by getting data from the server in structured format (most often JSON) and rendering HTML in the browser with JavaScript.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;4ff747e4-3871-45f0-8bca-1387313882a3&#34; class=&#34;&#34;&gt;A trip to Chrome Dev Tools confirmed that Notion works like that.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;9c818728-c54d-4499-a209-04e1167c6cf7&#34; class=&#34;&#34;&gt;When loading a Notion page I saw XHR requests like &lt;code&gt;/api/v3/getRecordValues&lt;/code&gt; and &lt;code&gt;/api/v3/loadPageChunk&lt;/code&gt;.&#xA;  &lt;/div&gt;&#xA;&lt;img class=&#34;blog-img&#34; src=&#34;/img/1d0a837dbe1bd3454d177f08cee83255f2401591.png&#34;&gt;&#xA;  &lt;div id=&#34;c0aa3e6b-1046-46d9-b155-a196ad6d1404&#34; class=&#34;&#34;&gt;Lucky for me the API is not obfuscated. It returns responses as JSON data. It isn&amp;#x27;t hard to figure out the meaning of fields. &#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;9bffee0b-d1d4-4e2b-af1e-4188bc585193&#34; class=&#34;&#34;&gt;Working with the original JSON structure is much easier that trying to reconstruct it from rendered HTML.&#xA;  &lt;/div&gt;&#xA;  &lt;h1 id=&#34;0ca3a424-52a5-4a2d-9a09-5ad192987042&#34; class=&#34;&#34;&gt;Building tools&#xA;  &lt;/h1&gt;&#xA;  &lt;div id=&#34;85e4b259-df34-49cb-b5ab-b0d6dd267426&#34; class=&#34;&#34;&gt;I could have looked at API requests between client and server in Chrome dev tools but it&amp;#x27;s not the best workflow.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;5938dbdd-d137-48aa-b16a-67a496996822&#34; class=&#34;&#34;&gt;Instead I wrote node.js script that logs all XHR requests that web browser makes when rendering a given page.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;51e9c09f-b47f-4f8c-a200-ae4a0d40825d&#34; class=&#34;&#34;&gt;That has several advantages over using dev tools:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;c34e6bab-79a5-46b4-a6c4-cfbc26b189e6&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;I could filter out requests to third-party services like amplitude, fullstory and intercom&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;I could filter out requests that are not interesting like &lt;code&gt;/api/v3/ping&lt;/code&gt;&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;I could pretty-print JSON&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;I could write captured traffic to a file for further analysis&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;div id=&#34;dba18e4e-07c4-4f48-9d7b-49dc786bafd6&#34; class=&#34;&#34;&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;4c3cdc96-b0ca-46e4-b930-e73dada54975&#34; class=&#34;&#34;&gt;Here&amp;#x27;s the script:&#xA;  &lt;/div&gt;&#xA;&lt;script src=&#34;https://gist.github.com/kjk/f33bc37d6ca8282b5c52b17391384693.js&#34;, class=&#34;notion-embed-gist&#34;&gt;&lt;/script&gt;&#xA;  &lt;h1 id=&#34;da60d8a5-8581-445a-826f-c56a8b82a1a3&#34; class=&#34;&#34;&gt;The big picture analysis&#xA;  &lt;/h1&gt;&#xA;  &lt;div id=&#34;7048fb98-5033-4009-8728-11be9a9450d4&#34; class=&#34;&#34;&gt;After looking at captured data, the structure of Notion content is not complicated.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;5a5a3ce1-afc7-43eb-9bec-e84ea6d8d6de&#34; class=&#34;&#34;&gt;Everything, including a top-level page, is a block.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;d246dd26-4895-4fa9-b401-d17bf1c20a02&#34; class=&#34;&#34;&gt;Blocks are identified by a unique id which looks like a standard UUID format.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;a6bb5b68-e502-4ede-b6e3-3fa2ad1c4156&#34; class=&#34;&#34;&gt;Blocks are arranged into a tree i.e. some blocks have children.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;7fd915f0-277e-4dbd-b53b-019a5b675350&#34; class=&#34;&#34;&gt;Blocks have metadata, like creation time, last edit time, version etc.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;52b272f6-c24c-454f-8cc4-859465af6f21&#34; class=&#34;&#34;&gt;There are different kinds of blocks: a page, text, todo item, list item etc.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;458cd17b-4fb7-4da7-91df-fabab504a177&#34; class=&#34;&#34;&gt;Some blocks have properties specific to that block type. For example a page block has &lt;code&gt;title&lt;/code&gt; property.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;bcfbafb0-0560-48a1-b53d-c4d0281389e6&#34; class=&#34;&#34;&gt;To get the content of a page we start with its UUID which we can find out because it&amp;#x27;s last part of the URL of the page.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;31e8428b-e194-405f-bd01-018b0a7e4cb3&#34; class=&#34;&#34;&gt;We can issue &lt;code&gt;/api/v3/getRecordValues&lt;/code&gt; API to get list of blocks in the page and then &lt;code&gt;/api/v3/loadPageChunk&lt;/code&gt; to get content of those blocks.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;c9bf8931-34d5-4cc5-8aa2-f237537bde28&#34; class=&#34;&#34;&gt;Majority of work was figuring out what kinds of blocks there are, how are they represented in JSON and writing code to to retrieve the data and present it in a format that is easier to work with than the raw data returned by the server.&#xA;  &lt;/div&gt;&#xA;  &lt;h1 id=&#34;c2a2f4ab-1b09-4746-ba68-d42295380ca9&#34; class=&#34;&#34;&gt;Testing different kinds of blocks&#xA;  &lt;/h1&gt;&#xA;  &lt;div id=&#34;b61346c1-7d78-4f9b-b976-f5ce15288da0&#34; class=&#34;&#34;&gt;Notion page consist of different kinds of blocks and we need to know how each block is represented in JSON response.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;8567db60-4c5c-40dc-ad91-c01101537b13&#34; class=&#34;&#34;&gt;To investigate it systematically, I&amp;#x27;ve created a test page for each kind of block and used the request logging script to look at JSON returned by the server for that block.&#xA;  &lt;/div&gt;&#xA;  &lt;h1 id=&#34;03863875-d766-42f9-a8d3-47b77c5367a8&#34; class=&#34;&#34;&gt;Writing Go library&#xA;  &lt;/h1&gt;&#xA;  &lt;div id=&#34;f6301331-c8b0-4aca-a905-9a0e9e0bd242&#34; class=&#34;&#34;&gt;Next step was writing a Go library.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;f8d31404-caf4-43c6-80ae-eed44fdd3b7d&#34; class=&#34;&#34;&gt;I captured sample JSON responses from &lt;code&gt;getRecordValues&lt;/code&gt; and &lt;code&gt;loadPageChunk&lt;/code&gt; and used &lt;a href=&#34;https://app.quicktype.io/&#34;&gt;Quicktype&lt;/a&gt; to generate Go structures.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;fc1a9595-79e6-4f59-9796-410d0fb982b0&#34; class=&#34;&#34;&gt;I had to tweak them a bit to accommodate variations in JSON structure.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;cdbc0e3c-c4c2-4bcd-bc92-b4324f058908&#34; class=&#34;&#34;&gt;The rest of the effort was writing a helper function that abstracts the details of HTTP requests and returns an easy to use struct describing a notion page.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;4006f874-f8c5-45e9-a9d0-dbf8741be349&#34; class=&#34;&#34;&gt;There result of that work is &lt;a href=&#34;https://github.com/kjk/notionapi&#34;&gt;notionapi&lt;/a&gt; Go package.&#xA;  &lt;/div&gt;&#xA;  &lt;h1 id=&#34;0a9f2c11-56f5-42b3-9a1d-df3880e1a43e&#34; class=&#34;&#34;&gt;Using the library in practice&#xA;  &lt;/h1&gt;&#xA;  &lt;div id=&#34;3f5e9bd7-2334-4ce8-a349-2cfe652d85a0&#34; class=&#34;&#34;&gt;This was not just an academic exercise.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;950e8502-4677-4a98-8157-d2d67aa94389&#34; class=&#34;&#34;&gt;This blog was powered by markdown files I stored in GitHub repository.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;6cf58618-ecdd-4713-8f42-d4936150f8a9&#34; class=&#34;&#34;&gt;My goal was to move the content to Notion, so that I can edit it more easily, convert it to HTML and publish as my website/blog.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;a4f2ce69-1eaa-420a-8f66-6e90804d365c&#34; class=&#34;&#34;&gt;You can see the code &lt;a href=&#34;https://github.com/kjk/blog&#34;&gt;here&lt;/a&gt;.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;7ece10da-fabc-47b5-b96d-d2eb82a09be7&#34; class=&#34;&#34;&gt;The high-level structure of the code:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;87dba394-93a8-4e41-9199-58d55a82d34b&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;I use my Go &lt;a href=&#34;https://github.com/kjk/notionapi&#34;&gt;notionapi&lt;/a&gt; library to download the content from Notion&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;I cache downloaded data and store them in git repository. This is to make sure I have a copy of data even if Notion disappears, to make it faster to tweak publishing code (no need to re-download) and to be nicer to Notion server (no re-downloads unless I absolutely have to)&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;I convert Notion data to HTML, wrap it in templates for my pages and write HTML files to disk&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;I deploy to &lt;a href=&#34;https://www.netlify.com/&#34;&gt;Netlify&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;&lt;/div&gt;</content>
  </entry>
  <entry>
   <title>Advanced web spidering with Puppeteer</title>
   <link href="https://blog.kowalczyk.info/article/ea07db1b9bff415ab180b0525f3898f6/advanced-web-spidering-with-puppeteer.html" rel="alternate"></link>
   <updated>2018-07-18T00:00:00Z</updated>
   <id>tag:blog.kowalczyk.info,2018-07-18:/article/ea07db1b9bff415ab180b0525f3898f6/advanced-web-spidering-with-puppeteer.html</id>
   <content type="html">&lt;p&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;notion-page&#34; id=&#34;ea07db1b-9bff-415a-b180-b0525f3898f6&#34;&gt;&#xA;  &lt;div id=&#34;5a9abf2d-cc20-45a2-90ec-9c393e6985bf&#34; class=&#34;&#34;&gt;&lt;a href=&#34;https://pptr.dev/&#34;&gt;Puppeteer&lt;/a&gt; is a node.js library that makes it easy to do advanced web scraping and spidering.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;dac71655-33a0-450a-9ed2-110119ad6597&#34; class=&#34;&#34;&gt;Older generation of web scraping and spidering tools would grab and analyze HTML pages as returned by a web server.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;06482943-ee14-48cc-bc2e-41c01718fbfa&#34; class=&#34;&#34;&gt;It doesn&amp;#x27;t work well anymore because less and less website are static HTML pages. Today websites are often applications written in JavaScript that generate HTML on the client, not the server.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;fd67f3c2-9bda-460d-9aec-4dd575b8ec04&#34; class=&#34;&#34;&gt;To get the final HTML output your scraper needs to run that JavaScript.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;2a55c93b-6690-4f27-b26d-24ba79722e6b&#34; class=&#34;&#34;&gt;That used to be very difficult but Puppeteer makes it easy.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;19219644-0bea-411c-8424-02ff430fef7d&#34; class=&#34;&#34;&gt;Puppeteer uses Chrome to run web application and uses CDP (&lt;a href=&#34;https://chromedevtools.github.io/devtools-protocol/&#34;&gt;Chrome DevTools Protocol&lt;/a&gt;) to access the webpage.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;b0c7104e-8000-45d1-ad95-ab90bfaed89a&#34; class=&#34;&#34;&gt;This article describes some more advanced techniques but let&amp;#x27;s start with basic example first.&#xA;  &lt;/div&gt;&#xA;  &lt;h1 id=&#34;d2cce1a7-8e47-4888-bbea-a931d18880b2&#34; class=&#34;&#34;&gt;Save web page to a file&#xA;  &lt;/h1&gt;&#xA;  &lt;div id=&#34;254a0619-2a7f-4b30-8574-5e43b368880f&#34; class=&#34;&#34;&gt;First  install the library: &#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;3f0cd0d5-d164-43dc-8d6e-bcc99e464cb3&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;&lt;code&gt;yarn add puppeteer&lt;/code&gt; when using yarn&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;code&gt;npm --save puppeteer&lt;/code&gt; when using npm&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;div id=&#34;a740a0f9-42f4-4171-9b37-7f59260d5589&#34; class=&#34;&#34;&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;82df135e-5732-4825-98bd-e44d47ab1235&#34; class=&#34;&#34;&gt;This is the simplest possible usage of Puppeteer:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;21cc7652-5dfd-4980-a6f8-96baf2050ad3&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;navigate to a page of interest&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;get content of the webpage as HTML and save it to a file&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;puppeteer&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;require&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;puppeteer&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;fs&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;require&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;fs&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;run&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;browser&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;puppeteer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;launch&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;page&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;browser&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;newPage&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;kr&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;page&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;kr&#34;&gt;goto&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;https://www.google.com/&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;waitUntil&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;networkidle2&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;c1&#34;&gt;// hacky defensive move but I don&amp;#39;t know a better way:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&#x9;&lt;span class=&#34;c1&#34;&gt;// wait a bit so that the browser finishes executing JavaScript&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&#x9;&lt;span class=&#34;kr&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;page&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;waitFor&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;html&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;page&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;content&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;fs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;writeFileSync&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;index.html&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;html&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;kr&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;browser&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;close&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;run&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;e2dcab14-6b4e-43ef-a933-c51863855232&#34; class=&#34;&#34;&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;h1 id=&#34;b05a44b6-88b0-4870-92ca-6e594da81154&#34; class=&#34;&#34;&gt;Handling failures&#xA;  &lt;/h1&gt;&#xA;  &lt;div id=&#34;f37a6eb0-b394-41a2-9de2-4a0a17240ec4&#34; class=&#34;&#34;&gt;What if a url you tried to load didn&amp;#x27;t exist?&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;57cbf164-b1f5-4562-8fe4-3e1cc4b29bf4&#34; class=&#34;&#34;&gt;The web server will return the &amp;#x27;Not Found&amp;#x27; page with HTTP status code &lt;code&gt;404&lt;/code&gt; in the response. The above script would treat such page as a perfectly valid response.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;4d6ce728-6a69-432e-9b32-acbefd055e6c&#34; class=&#34;&#34;&gt;Most times you want to handle this as an error case. &#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;73978465-01c7-4df5-8aa9-a4866f3bd0f3&#34; class=&#34;&#34;&gt;For example, if you&amp;#x27;re writing a bot that checks for broken links, you want to distinguish &lt;code&gt;404&lt;/code&gt; NotFound response from &lt;code&gt;200&lt;/code&gt; Ok response.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;953b93ce-7b17-4604-840e-04985f470cee&#34; class=&#34;&#34;&gt;In HTTP protocol status codes &lt;code&gt;4xx&lt;/code&gt; and &lt;code&gt;5xx&lt;/code&gt; indicate errors. &lt;code&gt;2xx&lt;/code&gt; indicate success and &lt;code&gt;3xx&lt;/code&gt; indicate successful redirection.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;7db4c093-9a4d-4e5f-92b5-d28f2bfcfacc&#34; class=&#34;&#34;&gt;Puppeteer provides &lt;code&gt;Page.setRequestInterception(true)&lt;/code&gt; hook for intercepting HTTP requests before they happen as well as inspecting completed HTTP responses.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;38261b6e-2296-4e56-8698-4477ba00333f&#34; class=&#34;&#34;&gt;Here&amp;#x27;s a program that prints information about all HTTP requests and responses:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;puppeteer&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;require&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;puppeteer&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;run&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;browser&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;puppeteer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;launch&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;page&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;browser&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;newPage&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;mainUrl&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;https://blog.kowalczyk.info/pas&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;mainUrlStatus&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;kr&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;page&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;setRequestInterception&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;page&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;on&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;request&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;request&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;url&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;request&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;url&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;request url:&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;url&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;request&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;continue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;page&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;on&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;requestfailed&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;request&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;url&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;request&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;url&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;request failed url:&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;url&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;page&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;on&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;response&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;response&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;request&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;request&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;url&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;request&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;url&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;status&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;status&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;response url:&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;url&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;status:&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;status&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;url&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;===&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;mainUrl&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&#x9;&lt;span class=&#34;nx&#34;&gt;mainUrlStatus&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;status&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;kr&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;page&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;kr&#34;&gt;goto&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;mainUrl&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;status for main url:&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;mainUrlStatus&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;html&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;page&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;content&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;kr&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;browser&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;close&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;run&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;fcad7702-251b-49a3-ba57-9353cd7b9dff&#34; class=&#34;&#34;&gt;Here&amp;#x27;s what it&amp;#x27;ll print:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ node test.js&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;request url: https://blog.kowalczyk.info/pas&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;response url: https://blog.kowalczyk.info/pas status: 404&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;request url: https://fonts.googleapis.com/css?family=Roboto:400,700&amp;amp;subset=latin,latin-ext&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;response url: https://fonts.googleapis.com/css?family=Roboto:400,700&amp;amp;subset=latin,latin-ext status: 200&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;request url: https://fonts.gstatic.com/s/roboto/v18/KFOlCnqEu92Fr1MmWUlfChc9AMP6lQ.ttf&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;request url: https://fonts.gstatic.com/s/roboto/v18/KFOmCnqEu92Fr1Mu7GxPKTU1Kg.ttf&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;response url: https://fonts.gstatic.com/s/roboto/v18/KFOmCnqEu92Fr1Mu7GxPKTU1Kg.ttf status: 200&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;response url: https://fonts.gstatic.com/s/roboto/v18/KFOlCnqEu92Fr1MmWUlfChc9AMP6lQ.ttf status: 200&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;status for main url: 404&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;fada363f-259b-4bf2-97c0-2176a00108b0&#34; class=&#34;&#34;&gt;Notice that fetching a page also fetches all resources used by that page, just like in a web browser. For that reason to find out status code for the url we requested, we have to remember it in a variable in &lt;code&gt;response&lt;/code&gt; hook.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;03da57cd-70fd-432c-82c8-5007e579d14f&#34; class=&#34;&#34;&gt;&lt;code&gt;requestfailed&lt;/code&gt; hook is for errors on network connection level e.g. DNS resolution failed, there&amp;#x27;s not network at all, network connection got interrupted etc.&#xA;  &lt;/div&gt;&#xA;  &lt;h1 id=&#34;ef8e1cdc-c3e7-4ddf-973b-c075b7f91f7c&#34; class=&#34;&#34;&gt;See &lt;code&gt;console.log&lt;/code&gt; from inside the browser&#xA;  &lt;/h1&gt;&#xA;  &lt;div id=&#34;0c93383d-cfaf-47f5-8291-677ff8c5db9e&#34; class=&#34;&#34;&gt;Your JavaScript code is executed in two different contexts:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;6086e4a2-a5b2-40a6-84d3-d629c947c616&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;main script is executed in node.js. In that context &lt;code&gt;console.log(&amp;quot;foo&amp;quot;)&lt;/code&gt; prints to shell&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;scripts provided to &lt;code&gt;Page.evaluate&lt;/code&gt; method are serialized to text, sent to the browser via Chrome DevTools Protocol and executed inside the browser. In that context &lt;code&gt;console.log(&amp;quot;foo&amp;quot;)&lt;/code&gt; prints to browser console, which you can&amp;#x27;t see.&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;div id=&#34;9dcc85b2-365b-4bc9-a0b4-6c8e58a18486&#34; class=&#34;&#34;&gt;To see what &lt;code&gt;console.log&lt;/code&gt; prints in the browser, you can hook it and re-log to shell:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;browser&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;puppeteer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;launch&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;page&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;browser&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;newPage&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;url&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;https://blog.kowalczyk.info/&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// this hooks `console.log()` in the browser&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;page&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;on&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;console&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;msg&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;The whole message:&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;msg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;text&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;());&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;\nEach argument:&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;arg&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;of&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;msg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// arg is a Promise returning value of type JSHandle&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// https://pptr.dev/#?product=Puppeteer&amp;amp;show=api-class-jshandle&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;arg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;jsonValue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;then&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;v&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;v&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;page&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;kr&#34;&gt;goto&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;url&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;page&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;evaluate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;c1&#34;&gt;// This is executed inside the browser so not visible in our script&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;// unless we hook &amp;#39;console&amp;#39; events&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;  &lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;Message from the browser&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;browser&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;close&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;h1 id=&#34;3dc8e96a-c91f-4afe-9896-a19ad4599274&#34; class=&#34;&#34;&gt;Quickly testing &lt;code&gt;evaluate &lt;/code&gt;scripts&#xA;  &lt;/h1&gt;&#xA;  &lt;div id=&#34;ae4bef4a-fff8-4a0a-a62d-14947e1c5f13&#34; class=&#34;&#34;&gt;It&amp;#x27;s slow to test browser script executed via &lt;code&gt;Page.evaluate&lt;/code&gt; because you have to start the browser, load the page etc.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;1e0c2be3-546a-4fec-8f58-9cdb52bda43e&#34; class=&#34;&#34;&gt;To test scripts faster I test them directly in the browser, using excellent Chrome dev tools.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;afe39340-0aa7-458d-8481-e498aed18d43&#34; class=&#34;&#34;&gt;My process is:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;ced1175b-0ce4-4de6-a27e-8d5d201845ed&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;prepare the script, in IIFE form, in the editor&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;copy &amp;amp; paste in console window in Chrome dev tools&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;div id=&#34;5f6c18c2-db50-4dfa-812b-3b96a79b1651&#34; class=&#34;&#34;&gt;What is IIFE form? To avoid conflicts with JavaScripts state from previous runs I wrap the code inside Immediately Invoked Function Expression:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;c1&#34;&gt;// code here is isolated from things outside this function&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;  &lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;My script&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;c1&#34;&gt;// ... my script&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;c1&#34;&gt;// when debugging I can trigger JavaScript debugger from inside the script&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&#x9;&lt;span class=&#34;c1&#34;&gt;// with debugger statement:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&#x9;&lt;span class=&#34;kr&#34;&gt;debugger&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}()&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// immediately invoke the function&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;09151562-7b76-4981-a9d2-70682e1ce21a&#34; class=&#34;&#34;&gt;It&amp;#x27;s faster to iterate on code this way. You can also use browser&amp;#x27;s JavaScript debugger.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;747563e7-2a30-4807-a8cc-a795bb9156f9&#34; class=&#34;&#34;&gt;As shown in the snippet, I can also trigger the debugger for single-stepping through the code with &lt;code&gt;debugger;&lt;/code&gt; statement.&#xA;  &lt;/div&gt;&#xA;  &lt;h1 id=&#34;a4c56859-53e6-4fc0-bd30-1fac3cea28dc&#34; class=&#34;&#34;&gt;Study Puppeteer API&#xA;  &lt;/h1&gt;&#xA;  &lt;div id=&#34;8ca7b6f8-ff41-4819-bd8e-b65cefcb5422&#34; class=&#34;&#34;&gt;Now that you&amp;#x27;ve seen a few advanced uses of Puppeteer, you should study its API a bit to learn what else is possible. CDP is very powerful:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;9331d690-308e-4620-9be4-a68232593824&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://pptr.dev/#?product=Puppeteer&amp;amp;show=api-class-page&#34;&gt;Page class&lt;/a&gt; allows hooking many events, reading and setting cookies, simulating interaction like mouse clicks etc.&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://pptr.dev/#?product=Puppeteer&amp;amp;show=api-class-tracing&#34;&gt;Tracing class&lt;/a&gt; allows creating a trace file for future inspection in Chrome DevTools&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://pptr.dev/#?product=Puppeteer&amp;amp;show=api-class-worker&#34;&gt;Worker class&lt;/a&gt; allows interacting with Web Workers&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://pptr.dev/#?product=Puppeteer&amp;amp;show=api-class-coverage&#34;&gt;Coverage class&lt;/a&gt; allows measuring JavaScript and CSS coverage&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://pptr.dev/#?product=Puppeteer&amp;amp;show=api-class-keyboard&#34;&gt;Keyboard class&lt;/a&gt; allows simulating keyboard events&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;h1 id=&#34;19c134ee-ab83-4a4b-b5aa-fcb520f9e47e&#34; class=&#34;&#34;&gt;Other CDP tools and libraries&#xA;  &lt;/h1&gt;&#xA;  &lt;div id=&#34;27b61601-7c87-439f-86ea-69caf5541700&#34; class=&#34;&#34;&gt;Puppeteer is not the only tool that takes advantage of Chrome DevTools protocol. A bunch of them is listed in &lt;a href=&#34;https://github.com/ChromeDevTools/awesome-chrome-devtools&#34;&gt;Awesome Chrome DevTools&lt;/a&gt;.&#xA;  &lt;/div&gt;&#xA;&lt;/div&gt;</content>
  </entry>
  <entry>
   <title>Fuzzing Markdown parser written in Go</title>
   <link href="https://blog.kowalczyk.info/article/n/fuzzing-markdown-parser-written-in-go.html" rel="alternate"></link>
   <updated>2018-01-31T15:32:55-08:00</updated>
   <id>tag:blog.kowalczyk.info,2018-01-31:/article/n/fuzzing-markdown-parser-written-in-go.html</id>
   <content type="html">&lt;p&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;notion-page&#34; id=&#34;ac7e26d8-7292-40f2-adea-5a9c63c00394&#34;&gt;&#xA;  &lt;div id=&#34;e0d12990-e40d-4608-be2e-60cf44ed7aa5&#34; class=&#34;&#34;&gt;I finished working on &lt;a href=&#34;https://github.com/gomarkdown/markdown&#34;&gt;Markdown parser&lt;/a&gt; for Go.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;19e70f52-03ef-462a-8e3c-60aed1bf96e9&#34; class=&#34;&#34;&gt;To make sure it’s robust (no crashes, no hangs) I decided to fuzz it.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;355410ff-cf06-4aa7-a50f-e5e825b083e4&#34; class=&#34;&#34;&gt;What is fuzzing?&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;fdccf734-3265-4ef8-b939-4a831dfd9eb0&#34; class=&#34;&#34;&gt;Parsing text or binary formats is tricky. It’s easy to make mistakes that can lead to security exploits.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;6bd2cbb6-7c99-4290-94f2-542534c54462&#34; class=&#34;&#34;&gt;&lt;a href=&#34;https://security.googleblog.com/2016/08/guided-in-process-fuzzing-of-chrome.html&#34;&gt;Fuzzing&lt;/a&gt; helps to combat this.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;3333e025-1dde-4187-8e8d-92ae507d28aa&#34; class=&#34;&#34;&gt;The idea is simple:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;d697728f-4030-4a2d-8b7c-61ed34c589f2&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;magic algorithm generates randomized input data&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;we feed this data to parsing code&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;code instrumentation detects if parsing code crashes or overwrites memory&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;h2 id=&#34;e97cd1ee-64f0-4cff-8d13-f335a0bab318&#34; class=&#34;&#34;&gt;Fuzzing in Go is simple&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;160d2e2c-13df-4e65-9445-aeaf0bda9d13&#34; class=&#34;&#34;&gt;Writing a fuzzer for C or C++ code is hard.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;f84a814a-e775-4338-a68c-ce30e1d74545&#34; class=&#34;&#34;&gt;It’s trivial for Go thanks to &lt;a href=&#34;https://github.com/dvyukov/go-fuzz&#34;&gt;go-fuzz&lt;/a&gt;.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;f53d4d09-212b-49ee-9f88-217aba841e1c&#34; class=&#34;&#34;&gt;It took me about an hour to write a fuzzer for my markdown parser.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;ac825fd1-5464-4125-b522-6ba8a38ff95a&#34; class=&#34;&#34;&gt;Fuzzing step-by-step&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;fe9e3206-d147-4729-9567-55292ade872d&#34; class=&#34;&#34;&gt;First we need to get the tools:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ go get github.com/dvyukov/go-fuzz/go-fuzz&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ go get github.com/dvyukov/go-fuzz/go-fuzz-build&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;bf937375-8f95-4ab9-becf-aae60c693cf4&#34; class=&#34;&#34;&gt;My library has &lt;code&gt;ast := markdown.Parse(markdown []byte, p *parser.Parser)&lt;/code&gt; function which takes markdown text as input and parses it into an abstract syntax tree.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;5ebb110d-d7bd-468a-a3bb-7877cf7162e2&#34; class=&#34;&#34;&gt;I wrote &lt;code&gt;fuzz.go&lt;/code&gt;:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// +build gofuzz&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;package&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;markdown&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Fuzz function to be used by https://github.com/dvyukov/go-fuzz&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;Fuzz&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;data&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;byte&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;Parse&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;b6f69c97-5e8d-463f-bf56-18000233d1f6&#34; class=&#34;&#34;&gt;This implements &lt;code&gt;Fuzz(data []byte)&lt;/code&gt; function, which is an API expected by go-fuzz.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;f685ce1e-6a96-4f6c-a165-c1e206707baa&#34; class=&#34;&#34;&gt;Notice &lt;code&gt;// +build gofuzz&lt;/code&gt; line. It means that this code will not be compiled by default but only with &lt;code&gt;gofuzz&lt;/code&gt; build tag.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;fd837fbc-b637-43cf-803b-d31970861f0c&#34; class=&#34;&#34;&gt;Having the fuzzer generate completely random data is better than nothing but it’s much better if fuzzer can be seeded with sample valid content.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;59760f7e-0e24-4ff7-8f1a-63fdb463aec0&#34; class=&#34;&#34;&gt;As it happens, I already had markdown files used for tests in &lt;code&gt;testdata&lt;/code&gt; directory.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;a8cd6af5-917a-4171-a2b8-89c6fc858f04&#34; class=&#34;&#34;&gt;I used those to initialize the fuzzer:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# create a working directory for the fuzzer&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ mkdir -p fuzz-workdir&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# copy the files that seed fuzzing to corpus sub-directory of working directory&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ mkdir -p fuzz-workdir/corpus&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ cp testdata/*.text fuzz-workdir/corpus&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# generate the fuzzing program. This compiles fuzz.go we wrote earlier&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# generates fuzzer executable and markdown-fuzz.zip that packages&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# data to drive fuzzing process&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ go-fuzz-build github.com/gomarkdown/markdown&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;2f0dd7cc-fffa-4955-a2f7-67f820dfa642&#34; class=&#34;&#34;&gt;The last &lt;code&gt;go-fuzz-build&lt;/code&gt; step can take a while.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;4ea9affa-2721-4b58-bfa5-0ad74131260e&#34; class=&#34;&#34;&gt;Finally we start fuzzing process:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ go-fuzz -bin&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;./markdown-fuzz.zip -workdir&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;fuzz-workdir&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;7c740fd5-d704-4686-b00e-e119412387b5&#34; class=&#34;&#34;&gt;This runs for as long as you let it. Fuzzing works by generating random input data so it can go forever.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;4a3bbddc-a0bd-44f3-af49-e9c410f1026a&#34; class=&#34;&#34;&gt;As long as you don’t delete working directory, you can stop and re-start &lt;code&gt;go-fuzz&lt;/code&gt; and it’ll pick up where it left off.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;cb0ec3f3-5083-4142-aa54-a1fb35c79749&#34; class=&#34;&#34;&gt;Found crashes will be logged in &lt;code&gt;fuzz-workdir/crashers&lt;/code&gt; directory.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;a9b0f6f0-1cb6-4c74-b482-63f526f0b7ea&#34; class=&#34;&#34;&gt;I’m a fan of automation so I wrote &lt;a href=&#34;https://github.com/gomarkdown/markdown/blob/master/s/fuzz.sh&#34;&gt;&lt;code&gt;fuzz.sh&lt;/code&gt;&lt;/a&gt; script.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;e0328cd3-afbd-441c-8008-67607d038751&#34; class=&#34;&#34;&gt;That way I can re-run it easily after making changes to the parser to verify I didn’t introduce bugs.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;d641045c-91a1-4708-9877-db17e9f214e7&#34; class=&#34;&#34;&gt;The fuzzer has advanced options like fuzzing using multiple machines.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;5395ca89-ff39-443f-bf69-678d97bd6ad4&#34; class=&#34;&#34;&gt;The results&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;36003c51-85b6-442b-b2d4-b252bf6ea33d&#34; class=&#34;&#34;&gt;Fuzzing is magic. My library is derived from very popular and widely used blackfriday library (which in turn was derived from a popular C library) and yet the fuzzer found 3 separate issues that either crashed the parser or caused infinite loop.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;5e78ff31-58e8-4853-9d72-ad5bb637a61f&#34; class=&#34;&#34;&gt;They &lt;a href=&#34;https://github.com/gomarkdown/markdown/commit/5d96569c5a0d3cd46d961eddbb61e936e627774c&#34;&gt;are&lt;/a&gt; &lt;a href=&#34;https://github.com/gomarkdown/markdown/commit/e0fc813169b926a2182bc6554888eb37d12261f7&#34;&gt;now&lt;/a&gt; &lt;a href=&#34;https://github.com/gomarkdown/markdown/commit/5dd4b50fe81eda60f173e242ece05f24c5cc5cec&#34;&gt;fixed&lt;/a&gt; and covered by unit tests.&#xA;  &lt;/div&gt;&#xA;&lt;/div&gt;</content>
  </entry>
  <entry>
   <title>57 MicroConf videos for self-funded software businesses</title>
   <link href="https://blog.kowalczyk.info/article/l/57-microconf-videos-for-self-funded-software-businesses.html" rel="alternate"></link>
   <updated>2017-12-24T20:09:15-08:00</updated>
   <id>tag:blog.kowalczyk.info,2017-12-24:/article/l/57-microconf-videos-for-self-funded-software-businesses.html</id>
   <content type="html">&lt;p&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;notion-page&#34; id=&#34;0c896ea2-efd2-4ec7-be1d-1f6e3b22d254&#34;&gt;&#xA;  &lt;div id=&#34;8b57a69d-4ec7-4ac6-9cdc-e6f3b916e66a&#34; class=&#34;&#34;&gt;&lt;a href=&#34;http://www.microconf.com/&#34;&gt;MicroConf&lt;/a&gt; is a conference for small/indie/self-funded software businesses. Many of their talks are &lt;a href=&#34;https://vimeo.com/user12790628&#34;&gt;available on Vimeo&lt;/a&gt; but not well indexed. They have a &lt;a href=&#34;http://www.microconf.com/past-videos/&#34;&gt;better index&lt;/a&gt; (and &lt;a href=&#34;http://www.microconf.com/growth/past-videos/&#34;&gt;another here&lt;/a&gt;) on their website, but also not great.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;2fec78dc-1dfa-4ee7-8750-595d19c7dca5&#34; class=&#34;&#34;&gt;This is a list of videos and a bit of info about each video. I hope this will help you find a video useful for you.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;4aa01e34-b2a9-4c95-8deb-02bd708cfcc4&#34; class=&#34;&#34;&gt;1. Lizards Thru Doorways: Proven ways to Widen Your Funnel Using Just Your CTAs&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;f7b11759-377b-40b1-a860-6a108adc0079&#34; class=&#34;&#34;&gt;Video &lt;a href=&#34;https://vimeo.com/132932415&#34;&gt;https://vimeo.com/132932415&lt;/a&gt; by &lt;a href=&#34;https://copyhackers.com/&#34;&gt;Joanna Wiebe&lt;/a&gt;, 39 min.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;b9331829-7c6c-4d34-bf2c-7f16838a935d&#34; class=&#34;&#34;&gt;Joanna is a copywriter for hire i.e. she writes the text of emails, sales pages etc. for other businesses.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;b4e1ec94-559e-407e-82b6-ebf323c6130d&#34; class=&#34;&#34;&gt;Her talk is about writing better copy. Tips and case studies. She also has a lot of free (and paid) tutorials on copywriting on her website &lt;a href=&#34;https://copyhackers.com/&#34;&gt;https://copyhackers.com/&lt;/a&gt;. Recap: &lt;a href=&#34;https://kaidavis.com/microconf-2015/joanna/&#34;&gt;https://kaidavis.com/microconf-2015/joanna/&lt;/a&gt; &#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;7d25bc45-f1cf-41f4-857c-782e00f62c42&#34; class=&#34;&#34;&gt;2. An Inside Story of Self-Funded SaaS Growth&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;5b07719c-d08b-41a8-8f0a-b7d6f4052260&#34; class=&#34;&#34;&gt;Video &lt;a href=&#34;https://vimeo.com/132932414&#34;&gt;https://vimeo.com/132932414&lt;/a&gt; by &lt;a href=&#34;https://www.softwarebyrob.com/&#34;&gt;Rob Walling&lt;/a&gt;, 51 min.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;144d5e95-53ca-42b3-878e-38bbb41d3716&#34; class=&#34;&#34;&gt;This is a history of Rob’s SaaS startup &lt;a href=&#34;https://www.drip.com/&#34;&gt;https://www.drip.com&lt;/a&gt;(email marketing software). He also wrote about it online: &lt;a href=&#34;https://wpcurve.com/bootstrapped-drip-into-a-7-figure-saas-business/&#34;&gt;https://wpcurve.com/bootstrapped-drip-into-a-7-figure-saas-business/&lt;/a&gt;.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;67e06069-3b4c-4cf1-9aca-f6ef690f2194&#34; class=&#34;&#34;&gt;Recap: &lt;a href=&#34;https://kaidavis.com/microconf-2015/rob/&#34;&gt;https://kaidavis.com/microconf-2015/rob/&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;283e6c99-324a-4116-ba9b-dfe9637966ad&#34; class=&#34;&#34;&gt;3. Accelerating Growth: Grow Faster Without Working Yourself to Death&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;f1d3e0f9-ae7e-4e8f-a257-8c4b45ee07f2&#34; class=&#34;&#34;&gt;Video &lt;a href=&#34;https://vimeo.com/132139313&#34;&gt;https://vimeo.com/132139313&lt;/a&gt; by &lt;a href=&#34;https://hitenism.com/&#34;&gt;Hiten Shah&lt;/a&gt;, 54 min&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;e9a1e4d2-03e1-4a1c-bacb-d29a38358a34&#34; class=&#34;&#34;&gt;Recap: &lt;a href=&#34;https://kaidavis.com/microconf-2015/hiten/&#34;&gt;https://kaidavis.com/microconf-2015/hiten/&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;6d21f452-cd5c-49b5-8eb3-159269f9706a&#34; class=&#34;&#34;&gt;4. Amplification - Content Marketing That Works&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;b6942f32-e1db-470b-9cb0-509107381005&#34; class=&#34;&#34;&gt;Video &lt;a href=&#34;https://vimeo.com/132139312&#34;&gt;https://vimeo.com/132139312&lt;/a&gt; by Justin Jackson, 11 min.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;c53cfbe5-1cb6-484a-a1bc-275bc3c001d1&#34; class=&#34;&#34;&gt;Content marketing is about writing blog posts, doing podcasts and videos to attract people to your website so that you can pitch them on your products.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;7a7c6e54-1ba8-4616-a21d-0e440956cb7e&#34; class=&#34;&#34;&gt;5. How to Aggressively Acquire Customers for your SaaS with an Efficient Outbound Sales Process&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;a015b8c4-5e0e-4d11-a887-9bf8a4d54f9b&#34; class=&#34;&#34;&gt;Video &lt;a href=&#34;https://vimeo.com/132139308&#34;&gt;https://vimeo.com/132139308&lt;/a&gt; by &lt;a href=&#34;https://twitter.com/JordanGal&#34;&gt;Jordan Gal&lt;/a&gt;, 12 min.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;4c844188-d798-49fd-953f-387d1c376a85&#34; class=&#34;&#34;&gt;Outbound sales process is using email and phone calls (cold-calling) to find customers.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;72676117-15dc-4f6d-91aa-70d6bdd77d0a&#34; class=&#34;&#34;&gt;6. How I Designed Our (High-Touch) Sales &amp;amp; Onboarding to Run Without Me (2014)&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;010ddd8d-ce8b-4d61-ab92-c7eb6ba5067a&#34; class=&#34;&#34;&gt;Video &lt;a href=&#34;https://vimeo.com/132139307&#34;&gt;https://vimeo.com/132139307&lt;/a&gt; by &lt;a href=&#34;http://casjam.com/&#34;&gt;Brian Casel&lt;/a&gt;, 11 min.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;9ec1a9d5-afe2-4f57-abd9-cb4f79fc25aa&#34; class=&#34;&#34;&gt;Based on experience from his startup Restaurant Engine, talks about converting leads (from his inbound traffic) into customers and how to automate it.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;67612ec4-0d24-4cfd-b92f-4f0d8f3a821e&#34; class=&#34;&#34;&gt;Recap: &lt;a href=&#34;https://www.phraseexpander.com/microconf-2014/brian-casel-automated-content-marketing-machine-microconf-2014/&#34;&gt;https://www.phraseexpander.com/microconf-2014/brian-casel-automated-content-marketing-machine-microconf-2014/&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;aa16e7f2-d1e2-4d57-b92c-f54749280fd5&#34; class=&#34;&#34;&gt;7. Creating an Explosive Email Course&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;878209f6-ddff-40f7-bf8d-909389b2e40a&#34; class=&#34;&#34;&gt;Video &lt;a href=&#34;https://vimeo.com/132139306&#34;&gt;https://vimeo.com/132139306&lt;/a&gt; by &lt;a href=&#34;http://kadavy.net/&#34;&gt;David Kadavy&lt;/a&gt;, 13 min.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;335f0396-d134-4c17-872d-38c1c2f62da3&#34; class=&#34;&#34;&gt;Talks about creating email course in order to get more leads (build email list).&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;d6b75a39-4849-49ed-ad2b-4807a782e37f&#34; class=&#34;&#34;&gt;8. Do This, Not That: Creating an Exceptional Customer Support Experience from Day 1 (2015)&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;da7c6997-5d2c-431d-b42c-e9eead7aaa17&#34; class=&#34;&#34;&gt;Video &lt;a href=&#34;https://vimeo.com/131466750&#34;&gt;https://vimeo.com/131466750&lt;/a&gt; by &lt;a href=&#34;https://twitter.com/sh&#34;&gt;Sarah Hattter&lt;/a&gt;, 29 min.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;758ca814-a831-4c64-907f-b86d324e7019&#34; class=&#34;&#34;&gt;Sarah runs &lt;a href=&#34;http://cosupport.com/&#34;&gt;CoSupport&lt;/a&gt;, which teaches companies how to create good customer support.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;9a8bf176-302b-438f-8cc6-dfb13bc9bdd6&#34; class=&#34;&#34;&gt;Recap: &lt;a href=&#34;https://kaidavis.com/microconf-2015/sarah/&#34;&gt;https://kaidavis.com/microconf-2015/sarah/&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;97034315-70e6-4488-8ef0-b6e40e342788&#34; class=&#34;&#34;&gt;9. How to Build a Solo SaaS Sales Machine (2015)&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;b79d7faa-6758-4bf1-8413-ee47fe66d46b&#34; class=&#34;&#34;&gt;Video &lt;a href=&#34;https://vimeo.com/131441010&#34;&gt;https://vimeo.com/131441010&lt;/a&gt; by &lt;a href=&#34;https://twitter.com/Steli&#34;&gt;Steli Efti&lt;/a&gt;, 56 min.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;ba2ee9f4-4075-44e4-a3f0-74ef9443fc30&#34; class=&#34;&#34;&gt;Recap: &lt;a href=&#34;https://kaidavis.com/microconf-2015/steli/&#34;&gt;https://kaidavis.com/microconf-2015/steli/&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;c4654ba0-3532-4173-a00a-fe49512f94b9&#34; class=&#34;&#34;&gt;10. Lessons Learned Building a WordPress Plugin Business to $10k/month&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;7ef35f7e-0915-4f86-951d-e396d9021552&#34; class=&#34;&#34;&gt;Video &lt;a href=&#34;https://vimeo.com/130984501&#34;&gt;https://vimeo.com/130984501&lt;/a&gt; by &lt;a href=&#34;https://philderksen.com/&#34;&gt;Phil Derksen&lt;/a&gt;, 15 min.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;0671d70e-b17f-44e3-8231-584111fbabe0&#34; class=&#34;&#34;&gt;11. How To Systematically Fight SaaS Churn And Win (2015)&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;be51cf5e-15e2-4603-a33e-21d6cfd98200&#34; class=&#34;&#34;&gt;Video &lt;a href=&#34;https://vimeo.com/130984497&#34;&gt;https://vimeo.com/130984497&lt;/a&gt; by &lt;a href=&#34;https://keepify.com/&#34;&gt;Robert Graham&lt;/a&gt;, 13 min.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;f3100ada-8f65-4dfe-8cbb-e4aaea1b1724&#34; class=&#34;&#34;&gt;Recap: &lt;a href=&#34;https://bootstrapping.io/microconf-2015/robert/&#34;&gt;https://bootstrapping.io/microconf-2015/robert/&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;e186923a-81b1-428d-997b-1e6fb64e7aa9&#34; class=&#34;&#34;&gt;12. How Bookkeeping Tripled My Revenue in Two Years (and Other Unexpected Cash Flow Advice) (2015)&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;824a70fe-5063-448c-b12e-c8a8cb150cf5&#34; class=&#34;&#34;&gt;Video &lt;a href=&#34;https://vimeo.com/130984492&#34;&gt;https://vimeo.com/130984492&lt;/a&gt; by &lt;a href=&#34;https://twitter.com/jessemecham&#34;&gt;Jesse Mecham&lt;/a&gt;, 48 min.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;e93fd240-868b-4d11-af5d-74d255e31caa&#34; class=&#34;&#34;&gt;Recap: &lt;a href=&#34;https://kaidavis.com/microconf-2015/jesse/&#34;&gt;https://kaidavis.com/microconf-2015/jesse/&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;7740e4b3-ef79-4cad-bb67-895e577022e2&#34; class=&#34;&#34;&gt;13. Growing Your Userbase with Better Onboarding (2015)&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;29785f0f-e152-4362-8a7c-7e0fbf29300a&#34; class=&#34;&#34;&gt;Video &lt;a href=&#34;https://vimeo.com/130797721&#34;&gt;https://vimeo.com/130797721&lt;/a&gt; by &lt;a href=&#34;http://www.samuelhulick.com/&#34;&gt;Samuel Hulick&lt;/a&gt;, 30 min.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;31844425-fdbf-43cd-bfc7-fe13fc1348f6&#34; class=&#34;&#34;&gt;Recap: &lt;a href=&#34;https://kaidavis.com/microconf-2015/samuel/&#34;&gt;https://kaidavis.com/microconf-2015/samuel/&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;65f49f42-a93a-45e4-bd81-3d5e179f3688&#34; class=&#34;&#34;&gt;14. Q&amp;amp;A and Smart Bear Live (2015)&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;558c49ba-6af7-4058-bd0c-3dc4203d9a37&#34; class=&#34;&#34;&gt;Video &lt;a href=&#34;https://vimeo.com/130797720&#34;&gt;https://vimeo.com/130797720&lt;/a&gt; by &lt;a href=&#34;https://twitter.com/asmartbear&#34;&gt;Jason Cohen&lt;/a&gt;, 1hr 7 min.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;2b6077bb-6c7f-4069-ab29-c77e8ade685e&#34; class=&#34;&#34;&gt;Recap: &lt;a href=&#34;https://kaidavis.com/microconf-2015/jason/&#34;&gt;https://kaidavis.com/microconf-2015/jason/&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;950f886e-fdc6-4e6f-bd95-3edf22d29304&#34; class=&#34;&#34;&gt;15. Micro-ISV to Micro-acquisition: Selling my 11-year one-man software business (2015)&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;3a4aec49-fee3-474d-b851-e1bd525eb9de&#34; class=&#34;&#34;&gt;Video &lt;a href=&#34;https://vimeo.com/130797718&#34;&gt;https://vimeo.com/130797718&lt;/a&gt; by &lt;a href=&#34;https://twitter.com/jacobthurman&#34;&gt;Jacob Thurman&lt;/a&gt;. 11 min.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;a501e867-2fbc-4764-9c62-21f911e85abf&#34; class=&#34;&#34;&gt;Recap: &lt;a href=&#34;https://kaidavis.com/microconf-2015/jacob/&#34;&gt;https://kaidavis.com/microconf-2015/jacob/&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;557be8ea-6f68-45f3-954f-bd46d34d864b&#34; class=&#34;&#34;&gt;16. How to start a SaaS business in any market with no idea or connections, using only excel, email &amp;amp; phone (2015)&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;38cc2fa1-3e3e-4f07-bb10-14a9bc5a4ffd&#34; class=&#34;&#34;&gt;Video &lt;a href=&#34;https://vimeo.com/130797716&#34;&gt;https://vimeo.com/130797716&lt;/a&gt; by &lt;a href=&#34;https://twitter.com/pawelwb&#34;&gt;Pawel Brzeminski&lt;/a&gt;, 12 min.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;5f0499cb-9d02-4503-a847-6ba382612f3c&#34; class=&#34;&#34;&gt;Recap: &lt;a href=&#34;https://kaidavis.com/microconf-2015/pawel/&#34;&gt;https://kaidavis.com/microconf-2015/pawel/&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;846af29b-6f69-46e2-a63f-6aa1fc67c182&#34; class=&#34;&#34;&gt;17. How I Grew My Productized Consulting Offering To $100K YRR In 12 Months (2015)&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;a4a2ea35-2482-4c14-9a3d-8103ec955968&#34; class=&#34;&#34;&gt;Video &lt;a href=&#34;https://vimeo.com/130797714&#34;&gt;https://vimeo.com/130797714&lt;/a&gt; by &lt;a href=&#34;http://www.appaftercare.com/&#34;&gt;Einer Vollset&lt;/a&gt;, 9 min.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;0e5f82f3-0b02-4ef6-abc9-6e2f646652e8&#34; class=&#34;&#34;&gt;Recap: https://kaidavis.com/microconf-2015/einar/. A story of building &lt;a href=&#34;http://www.appaftercare.com/&#34;&gt;http://www.appaftercare.com/&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;af794b5b-8336-4fb9-9f5e-6b68b5182908&#34; class=&#34;&#34;&gt;18. The 3 Week Startup (2015)&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;f19f9e01-02d7-467b-83d8-92d9c591e6b7&#34; class=&#34;&#34;&gt;Video &lt;a href=&#34;https://vimeo.com/130499701&#34;&gt;https://vimeo.com/130499701&lt;/a&gt; by &lt;a href=&#34;https://twitter.com/harisenbon79&#34;&gt;Keith Perhac&lt;/a&gt;, 10 min.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;2581b677-b053-4768-b10d-ca230559c81d&#34; class=&#34;&#34;&gt;Recap: &lt;a href=&#34;https://kaidavis.com/microconf-2015/keith/&#34;&gt;https://kaidavis.com/microconf-2015/keith/&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;ad0fcf42-79f6-461c-982a-3e13fe6ce896&#34; class=&#34;&#34;&gt;Tactical tips about how (and why) to build SaaS quickly (in 1 week).&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;ae447717-adea-498b-8024-5fb2757e1051&#34; class=&#34;&#34;&gt;19. Leveling Up (2015)&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;658aa91b-e43e-4379-8ad9-bcc329758964&#34; class=&#34;&#34;&gt;Video &lt;a href=&#34;https://vimeo.com/129913527&#34;&gt;https://vimeo.com/129913527&lt;/a&gt; by &lt;a href=&#34;http://www.kalzumeus.com/&#34;&gt;Patrick McKenzie&lt;/a&gt;, 1 hr.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;d858c5da-1494-4ad7-a304-d2b66486f398&#34; class=&#34;&#34;&gt;Recap: &lt;a href=&#34;https://kaidavis.com/microconf-2015/patio11/&#34;&gt;https://kaidavis.com/microconf-2015/patio11/&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;b724e4c7-c85b-44de-96a3-b3656ff7a23b&#34; class=&#34;&#34;&gt;20. How to Validate Your Idea and Launch to $7k in Recurring Revenue (2014)&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;b614ba07-b111-40e3-be1d-f4ae34d001a7&#34; class=&#34;&#34;&gt;Video &lt;a href=&#34;https://vimeo.com/96267945&#34;&gt;https://vimeo.com/96267945&lt;/a&gt; by &lt;a href=&#34;https://www.softwarebyrob.com/&#34;&gt;Rob Walling&lt;/a&gt;, 58 min.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;e0a01e50-e383-4ac0-9260-4d43413a7ee8&#34; class=&#34;&#34;&gt;Recap: &lt;a href=&#34;http://www.christophengelhardt.com/rob-walling-validate-idea-launch-7k-recurring-revenue-microconf-europe-2014/&#34;&gt;http://www.christophengelhardt.com/rob-walling-validate-idea-launch-7k-recurring-revenue-microconf-europe-2014/&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;de3167e1-41d0-4cba-b5f6-eaca9767420a&#34; class=&#34;&#34;&gt;Recap: &lt;a href=&#34;https://www.phraseexpander.com/microconf-2014/rob-walling-validate-idea-launch7k-microconf-2014/&#34;&gt;https://www.phraseexpander.com/microconf-2014/rob-walling-validate-idea-launch7k-microconf-2014/&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;9072f846-93ef-460a-aff6-0c9b9f22fd2f&#34; class=&#34;&#34;&gt;21. 6 Tricks That Helped Me Triple My SaaS’ Growth Rate (2014)&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;c94b008c-6dcb-440c-a4c6-fa2022bf3a0d&#34; class=&#34;&#34;&gt;Video &lt;a href=&#34;https://vimeo.com/95680318&#34;&gt;https://vimeo.com/95680318&lt;/a&gt; by &lt;a href=&#34;https://twitter.com/brennandunn&#34;&gt;Brennan Dunn&lt;/a&gt;, 41 min.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;b2231756-9d06-41ff-85d9-9eef370be122&#34; class=&#34;&#34;&gt;Recap: &lt;a href=&#34;http://www.christophengelhardt.com/brennan-dunn-6-tricks-helped-triple-saas-growth-rate-microconf-europe-2014/&#34;&gt;http://www.christophengelhardt.com/brennan-dunn-6-tricks-helped-triple-saas-growth-rate-microconf-europe-2014/&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;0b7907d6-3df2-4321-a3a0-b181aaf46d92&#34; class=&#34;&#34;&gt;Recap: &lt;a href=&#34;https://www.phraseexpander.com/microconf-2014/brennan-dunn-triple-saas-growth-rate-microconf-2014/&#34;&gt;https://www.phraseexpander.com/microconf-2014/brennan-dunn-triple-saas-growth-rate-microconf-2014/&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;e59c37de-83fa-47c1-a66c-401b15282460&#34; class=&#34;&#34;&gt;22. Lifting the Veil: The Data Behind Successful Product Launches (2014)&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;e81d8af1-3016-4f47-8d6b-27fddab57c24&#34; class=&#34;&#34;&gt;Video &lt;a href=&#34;https://vimeo.com/95680316&#34;&gt;https://vimeo.com/95680316&lt;/a&gt; by &lt;a href=&#34;https://twitter.com/delk&#34;&gt;Ryan Delk&lt;/a&gt;, 13 min.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;034fbb05-fdee-4811-a0aa-5423b080cb22&#34; class=&#34;&#34;&gt;Recap: &lt;a href=&#34;https://www.phraseexpander.com/microconf-2014/ryan-delk-data-behind-successful-product-launches-microconf-2014/&#34;&gt;https://www.phraseexpander.com/microconf-2014/ryan-delk-data-behind-successful-product-launches-microconf-2014/&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;a9a12fa5-a93d-4a81-921f-e4c4210ff504&#34; class=&#34;&#34;&gt;23. 3 Habits for Building (and Growing) a Product Empire (2014)&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;7baf2d9c-ca24-44f7-b084-b54d60587920&#34; class=&#34;&#34;&gt;Video &lt;a href=&#34;https://vimeo.com/95680313&#34;&gt;https://vimeo.com/95680313&lt;/a&gt; by &lt;a href=&#34;http://nathanbarry.com/&#34;&gt;Nathan Barry&lt;/a&gt;, 38 min.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;9298d107-ddc2-4803-b76d-f822f629e7ef&#34; class=&#34;&#34;&gt;Recap: &lt;a href=&#34;https://www.phraseexpander.com/microconf-2014/nathan-barry-3-habits-grow-product-empire-microconf-2014/&#34;&gt;https://www.phraseexpander.com/microconf-2014/nathan-barry-3-habits-grow-product-empire-microconf-2014/&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;d090526f-6b10-4f5d-adb5-10ce228f3406&#34; class=&#34;&#34;&gt;24. From Zero to $4M/year Without Quora, Hacker News, or Mixergy (2014)&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;80a9c0a3-8518-4a39-80d8-f356354f3e6c&#34; class=&#34;&#34;&gt;Video &lt;a href=&#34;https://vimeo.com/95653848&#34;&gt;https://vimeo.com/95653848&lt;/a&gt; by &lt;a href=&#34;https://twitter.com/jessemecham&#34;&gt;Jesse Mecham&lt;/a&gt;, 50 min.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;11642f4c-e05c-46d1-97da-37691144dc05&#34; class=&#34;&#34;&gt;Recap: &lt;a href=&#34;https://www.phraseexpander.com/microconf-2014/jesse-mecham-from-zero-4m-microconf-2014/&#34;&gt;https://www.phraseexpander.com/microconf-2014/jesse-mecham-from-zero-4m-microconf-2014/&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;5caf098a-0a5f-47ca-8c46-3352a34337ad&#34; class=&#34;&#34;&gt;25. 10 Business Questions Every Entrepreneur Needs to Ask Their Analytics (And Where to Find the Answers) (2014)&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;4a80b00f-eb27-40f7-8a6d-7980779f7e1f&#34; class=&#34;&#34;&gt;Video &lt;a href=&#34;https://vimeo.com/95653743&#34;&gt;https://vimeo.com/95653743&lt;/a&gt; by &lt;a href=&#34;https://twitter.com/anniecushing&#34;&gt;Annie Cushing&lt;/a&gt;, 46 min.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;ad8e35b8-dbe3-4615-97a0-f37c7846e1c2&#34; class=&#34;&#34;&gt;Recap: &lt;a href=&#34;https://www.phraseexpander.com/microconf-2014/annie-cushing-10-business-questions-analytics-microconf-2014/&#34;&gt;https://www.phraseexpander.com/microconf-2014/annie-cushing-10-business-questions-analytics-microconf-2014/&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;7292410a-d9d9-49d1-9126-4ffbb23ec78f&#34; class=&#34;&#34;&gt;26. How To Slay the Customer Support Beast (2014)&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;6ed605a9-ea09-4372-82d0-e8fce5930f58&#34; class=&#34;&#34;&gt;Video &lt;a href=&#34;https://vimeo.com/95052087&#34;&gt;https://vimeo.com/95052087&lt;/a&gt; by &lt;a href=&#34;https://twitter.com/ianlandsman&#34;&gt;Ian Landsman&lt;/a&gt;, 44 min.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;efbb6d3f-8229-47fe-9e9c-d7f3f04b6018&#34; class=&#34;&#34;&gt;Recap: &lt;a href=&#34;https://www.phraseexpander.com/microconf-2014/ian-landsman-slay-customer-support-beast-microconf-2014/&#34;&gt;https://www.phraseexpander.com/microconf-2014/ian-landsman-slay-customer-support-beast-microconf-2014/&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;7cc391eb-efc1-49eb-b6e1-a8f60c302c63&#34; class=&#34;&#34;&gt;27. Don’t Burn-up in the Launch: Staying Emotionally and Relationally Healthy While Launching Your Startup (2013)&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;3fc0101c-faed-4fba-a60b-d41237b48331&#34; class=&#34;&#34;&gt;Video &lt;a href=&#34;https://vimeo.com/72211933&#34;&gt;https://vimeo.com/72211933&lt;/a&gt; by &lt;a href=&#34;http://sherrywalling.com/&#34;&gt;Sherry Walling&lt;/a&gt;, 19 min.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;00e94219-6188-4ea7-a0e4-2bd28e409a6b&#34; class=&#34;&#34;&gt;Recap: &lt;a href=&#34;http://www.christophengelhardt.com/sherry-walling-dont-burn-up-in-the-launch-microconf-2013/&#34;&gt;http://www.christophengelhardt.com/sherry-walling-dont-burn-up-in-the-launch-microconf-2013/&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;5f3b3de2-5e2f-4a4a-ba85-ff54a39747d6&#34; class=&#34;&#34;&gt;Recap: &lt;a href=&#34;http://www.christophengelhardt.com/sherry-walling-dont-burn-up-in-the-launch-staying-emotionally-and-relationally-healthy-while-launching-your-startup-microconf-europe-2013/&#34;&gt;http://www.christophengelhardt.com/sherry-walling-dont-burn-up-in-the-launch-staying-emotionally-and-relationally-healthy-while-launching-your-startup-microconf-europe-2013/&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;cdc4ae60-a87d-461c-85dc-76fbf6317881&#34; class=&#34;&#34;&gt;28. Playing the Long Game: Making Entrepreneurship a Sustainable Life (2014)&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;6a90c66e-5634-4cd0-9b66-071562058421&#34; class=&#34;&#34;&gt;Video &lt;a href=&#34;https://vimeo.com/95052086&#34;&gt;https://vimeo.com/95052086&lt;/a&gt; by &lt;a href=&#34;http://sherrywalling.com/&#34;&gt;Sherry Walling&lt;/a&gt;, 56 min.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;69576059-afc0-4039-9c71-147a2e9c3e60&#34; class=&#34;&#34;&gt;Recap: &lt;a href=&#34;https://www.phraseexpander.com/microconf-2014/sherry-walling-entrepreneurship-sustainable-life-microconf-2014/&#34;&gt;https://www.phraseexpander.com/microconf-2014/sherry-walling-entrepreneurship-sustainable-life-microconf-2014/&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;9cb51253-35aa-4ff5-b4b6-9a9160052541&#34; class=&#34;&#34;&gt;29. From Idea to $5k/mo in 5 Months (2014)&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;9231ce27-9131-4ebe-a923-888b23ae4604&#34; class=&#34;&#34;&gt;Video &lt;a href=&#34;https://vimeo.com/94623532&#34;&gt;https://vimeo.com/94623532&lt;/a&gt; by &lt;a href=&#34;https://twitter.com/Shpigford&#34;&gt;Josh Pigford&lt;/a&gt;, 12 min.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;e362e0f0-fda7-44d0-886a-71d1f721c665&#34; class=&#34;&#34;&gt;History of building &lt;a href=&#34;https://baremetrics.com/&#34;&gt;https://baremetrics.com/&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;f26b9c91-07e6-4c8c-a1d6-b470892f7acf&#34; class=&#34;&#34;&gt;Recap: &lt;a href=&#34;https://www.phraseexpander.com/microconf-2014/josh-pigford-idea-5k-5months-microconf-2014/&#34;&gt;https://www.phraseexpander.com/microconf-2014/josh-pigford-idea-5k-5months-microconf-2014/&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;7a08d5f4-b50d-4cb9-b081-9ed97f77b0f0&#34; class=&#34;&#34;&gt;30. UX Basics That Convert Users into Customers (2014)&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;f112e9b3-e750-41d8-9f61-0c6e65e77474&#34; class=&#34;&#34;&gt;Video &lt;a href=&#34;https://vimeo.com/94623531&#34;&gt;https://vimeo.com/94623531&lt;/a&gt; by &lt;a href=&#34;http://www.samuelhulick.com/&#34;&gt;Samuel Hulick&lt;/a&gt;, 7 min.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;471a6de8-a125-46d0-aa64-1e84eef8ad21&#34; class=&#34;&#34;&gt;Recap: &lt;a href=&#34;https://www.phraseexpander.com/microconf-2014/samuel-hulick-uxbasics-convert-user-into-customers-microconf-2014/&#34;&gt;https://www.phraseexpander.com/microconf-2014/samuel-hulick-uxbasics-convert-user-into-customers-microconf-2014/&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;b1eb8965-2a3a-494c-8e55-9c2f936901d2&#34; class=&#34;&#34;&gt;31. Business Hacks &amp;amp; Epic Wins (2014)&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;a8c959b9-8699-40e4-8d48-a05808638571&#34; class=&#34;&#34;&gt;Video &lt;a href=&#34;https://vimeo.com/94623529&#34;&gt;https://vimeo.com/94623529&lt;/a&gt; by &lt;a href=&#34;https://twitter.com/SingleFounder&#34;&gt;Mike Taber&lt;/a&gt;, 1 hr 4 min.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;97d9e791-7e94-499a-9b1b-692e2d05802a&#34; class=&#34;&#34;&gt;Recap: &lt;a href=&#34;https://www.phraseexpander.com/microconf-2014/mike-taber-business-hacks-epic-wins-microconf-2014/&#34;&gt;https://www.phraseexpander.com/microconf-2014/mike-taber-business-hacks-epic-wins-microconf-2014/&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;6939025d-bb5c-4ce0-bafd-b61207bc80af&#34; class=&#34;&#34;&gt;32. How to Grow Your Self-Funded Business Faster (2014)&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;72a7f9a5-781d-408c-9ccb-79b99dffdc53&#34; class=&#34;&#34;&gt;Video &lt;a href=&#34;https://vimeo.com/94187473&#34;&gt;https://vimeo.com/94187473&lt;/a&gt; by &lt;a href=&#34;https://hitenism.com/&#34;&gt;Hiten Shah&lt;/a&gt;, 43 min.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;9d76098b-b1e5-4b1f-ae37-68f9e58a7b8a&#34; class=&#34;&#34;&gt;Recap: &lt;a href=&#34;https://www.phraseexpander.com/microconf-2014/hiten-shah-grow-your-self-funded-business-faster-microconf-2014/&#34;&gt;https://www.phraseexpander.com/microconf-2014/hiten-shah-grow-your-self-funded-business-faster-microconf-2014/&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;8d9c5a2d-ebab-40b7-892d-9c102f9b9fcd&#34; class=&#34;&#34;&gt;33. Designing the Ideal Bootstrapped Business (2013)&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;167a5bea-fa61-4441-b5ef-336297e0e604&#34; class=&#34;&#34;&gt;Video &lt;a href=&#34;https://vimeo.com/74338272&#34;&gt;https://vimeo.com/74338272&lt;/a&gt; by &lt;a href=&#34;https://twitter.com/asmartbear&#34;&gt;Jason Cohen&lt;/a&gt;, 1hr 5 min.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;81d7e756-3cfe-4f0d-9050-14d2a18fc26a&#34; class=&#34;&#34;&gt;Recap: &lt;a href=&#34;http://www.christophengelhardt.com/jason-cohen-microconf-2013/&#34;&gt;http://www.christophengelhardt.com/jason-cohen-microconf-2013/&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;38cb56c6-d059-46bb-8023-015d88e68155&#34; class=&#34;&#34;&gt;34. Dude. Marketing is not your thing (2013)&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;01dadad8-3302-4ccc-8121-dc9341aae986&#34; class=&#34;&#34;&gt;Video &lt;a href=&#34;https://vimeo.com/72461554&#34;&gt;https://vimeo.com/72461554&lt;/a&gt; by Jody Burgess, 13 min.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;d72b134d-1f25-4565-8cac-2954db4e5685&#34; class=&#34;&#34;&gt;Recap: &lt;a href=&#34;http://www.christophengelhardt.com/dude-marketing-is-not-your-thing-microconf-2013/&#34;&gt;http://www.christophengelhardt.com/dude-marketing-is-not-your-thing-microconf-2013/&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;aec4e8af-b1f2-42b7-864b-69429c49f0ce&#34; class=&#34;&#34;&gt;35. How a Non-Technical Founder Built a 6 Figure Saas App Using Only Free Public Data Sources (2013)&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;a932c4da-7e7d-4d11-ba91-58fe1b237d27&#34; class=&#34;&#34;&gt;Video &lt;a href=&#34;https://vimeo.com/72461551&#34;&gt;https://vimeo.com/72461551&lt;/a&gt; by &lt;a href=&#34;https://twitter.com/distressedpro&#34;&gt;Brecht Palombo&lt;/a&gt;, 15 min.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;8cfa5692-bef9-4535-a246-f444489aa14b&#34; class=&#34;&#34;&gt;Recap: &lt;a href=&#34;http://www.christophengelhardt.com/brecht-palombo-how-a-non-technical-founder-built-a-6-figure-saas-app-using-only-free-public-data-sources-microconf-2013/&#34;&gt;http://www.christophengelhardt.com/brecht-palombo-how-a-non-technical-founder-built-a-6-figure-saas-app-using-only-free-public-data-sources-microconf-2013/&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;d9e1206f-1dc9-4e73-bcf8-28c29ecd636a&#34; class=&#34;&#34;&gt;36. Finding Customers Who Are 100x More Valuable Without 100x the Effort (2013)&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;742dd50c-39c7-4320-ae46-54bed03fc629&#34; class=&#34;&#34;&gt;Video &lt;a href=&#34;https://vimeo.com/72456666&#34;&gt;https://vimeo.com/72456666&lt;/a&gt; by &lt;a href=&#34;https://twitter.com/ericabiz&#34;&gt;Erica Douglass&lt;/a&gt;, 48 min.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;efda256d-6efe-4afc-8420-bf319950beaf&#34; class=&#34;&#34;&gt;Recap: &lt;a href=&#34;http://www.christophengelhardt.com/erica-douglass-how-to-measurably-move-the-needle-with-your-software-company-microconf-2013/&#34;&gt;http://www.christophengelhardt.com/erica-douglass-how-to-measurably-move-the-needle-with-your-software-company-microconf-2013/&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;17ec4906-1cf9-4cc0-8fbd-f60f42420211&#34; class=&#34;&#34;&gt;37. Bootstrapping an App Business (2013)&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;b8471184-8bd4-49f9-bf73-ffcfa980c9c7&#34; class=&#34;&#34;&gt;Video &lt;a href=&#34;https://vimeo.com/72260021&#34;&gt;https://vimeo.com/72260021&lt;/a&gt; by &lt;a href=&#34;https://twitter.com/pthompson&#34;&gt;Patrick Thompson&lt;/a&gt;, 16 min.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;635ce315-82e2-4747-9b38-7c4dade2a329&#34; class=&#34;&#34;&gt;Recap: &lt;a href=&#34;http://www.christophengelhardt.com/patrick-thompson-bootstraping-an-app-business-microconf-2013/&#34;&gt;http://www.christophengelhardt.com/patrick-thompson-bootstraping-an-app-business-microconf-2013/&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;f5a25287-61ed-4fa5-a5ed-49d74c30aeab&#34; class=&#34;&#34;&gt;38. Building Things To Help Sell The Things You Build (2013)&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;faf1aa2e-c2cd-4ab1-a7cb-c34ef1aa5c9a&#34; class=&#34;&#34;&gt;Video &lt;a href=&#34;https://vimeo.com/72140534&#34;&gt;https://vimeo.com/72140534&lt;/a&gt; by &lt;a href=&#34;http://www.kalzumeus.com/&#34;&gt;Patrick McKenzie&lt;/a&gt;, 1 hr 13 min.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;0e111972-6798-4e07-aaee-a5e4f23d9997&#34; class=&#34;&#34;&gt;Recap: &lt;a href=&#34;http://www.christophengelhardt.com/patrick-mckenzie-building-things-to-help-sell-the-things-you-build-microconf-2013/&#34;&gt;http://www.christophengelhardt.com/patrick-mckenzie-building-things-to-help-sell-the-things-you-build-microconf-2013/&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;35a91e42-a943-4df8-aee7-f693485d1ced&#34; class=&#34;&#34;&gt;39. Killer Content Marketing (2013)&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;5ee5e921-b741-40cc-bb1f-c1f58d536fff&#34; class=&#34;&#34;&gt;Video &lt;a href=&#34;https://vimeo.com/72081980&#34;&gt;https://vimeo.com/72081980&lt;/a&gt; by &lt;a href=&#34;https://hitenism.com/&#34;&gt;Hiten Shah&lt;/a&gt;, 44 min.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;b1046a01-8acb-4fa3-8fc3-e8eb26782a27&#34; class=&#34;&#34;&gt;Recap: &lt;a href=&#34;http://www.christophengelhardt.com/hiten-shah-killer-content-marketing-microconf-2013/&#34;&gt;http://www.christophengelhardt.com/hiten-shah-killer-content-marketing-microconf-2013/&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;172298ee-a1a3-40df-82d5-82c71364c16e&#34; class=&#34;&#34;&gt;40. SEO Demystified: Practical Techniques That Produce Astonishing Results (2013)&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;626a7f1b-69c4-43f8-94f6-4defb386013d&#34; class=&#34;&#34;&gt;Video &lt;a href=&#34;https://vimeo.com/71547333&#34;&gt;https://vimeo.com/71547333&lt;/a&gt; by &lt;a href=&#34;https://twitter.com/TheDaveCollins&#34;&gt;Dave Collins&lt;/a&gt;, 57 min.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;3db63731-c71b-4412-b828-74d032a74b38&#34; class=&#34;&#34;&gt;Recap: &lt;a href=&#34;http://www.christophengelhardt.com/dave-collins-seo-demystified-practical-techniques-that-produce-astonishing-results-microconf-europe-2013/&#34;&gt;http://www.christophengelhardt.com/dave-collins-seo-demystified-practical-techniques-that-produce-astonishing-results-microconf-europe-2013/&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;b639df39-d86c-4f47-bea2-d6910d47baed&#34; class=&#34;&#34;&gt;41. Shut Up and Take My Money: How to Find Business Ideas Customers Want (2013)&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;b6567fca-8c9d-4f5c-ae85-077beea2bf3f&#34; class=&#34;&#34;&gt;Video &lt;a href=&#34;https://vimeo.com/71250239&#34;&gt;https://vimeo.com/71250239&lt;/a&gt; by &lt;a href=&#34;https://twitter.com/joshkaufman&#34;&gt;Josh Kaufman&lt;/a&gt;, 42 min.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;89c164b1-b104-4da9-883d-5813d6309e8f&#34; class=&#34;&#34;&gt;Recap: &lt;a href=&#34;http://www.christophengelhardt.com/josh-kaufman-shut-up-and-take-my-money-how-to-find-business-ideas-customers-want-microconf-2013/&#34;&gt;http://www.christophengelhardt.com/josh-kaufman-shut-up-and-take-my-money-how-to-find-business-ideas-customers-want-microconf-2013/&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;3268f6ac-1755-45e4-aa14-81b21865eeb7&#34; class=&#34;&#34;&gt;42. Copywriting that Converts: How to Sell Without Selling Your Soul (2013)&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;d98d537d-934e-489a-93d6-954499365f53&#34; class=&#34;&#34;&gt;Video &lt;a href=&#34;https://vimeo.com/71008640&#34;&gt;https://vimeo.com/71008640&lt;/a&gt; by &lt;a href=&#34;https://copyhackers.com/&#34;&gt;Joanna Wiebe&lt;/a&gt;, 50 min.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;aa3c1fdc-7602-4cb6-9cb8-8ca663a2edbc&#34; class=&#34;&#34;&gt;Recap: &lt;a href=&#34;http://www.christophengelhardt.com/joanna-wiebe-copywriting-that-converts-microconf-2013/&#34;&gt;http://www.christophengelhardt.com/joanna-wiebe-copywriting-that-converts-microconf-2013/&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;22288876-bbd4-4770-8a8f-73024b9b4da7&#34; class=&#34;&#34;&gt;Another: &lt;a href=&#34;http://www.workhappy.net/2013/05/how-you-can-drastically-improve-the-copy-on-your-site-even-if-you-only-have-5-minutes.html&#34;&gt;http://www.workhappy.net/2013/05/how-you-can-drastically-improve-the-copy-on-your-site-even-if-you-only-have-5-minutes.html&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;0536749c-6048-4f32-9cee-69af187c782d&#34; class=&#34;&#34;&gt;43. Lean Analytics: How to Focus on What Matters (2013)&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;ca581de5-2c6f-4eed-abb6-1c69bf1e1080&#34; class=&#34;&#34;&gt;Video &lt;a href=&#34;https://vimeo.com/70981922&#34;&gt;https://vimeo.com/70981922&lt;/a&gt; by &lt;a href=&#34;https://twitter.com/byosko&#34;&gt;Ben Yoskovitz&lt;/a&gt;, 43 min.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;ca521029-15e0-4a76-aa64-e86f073f1dc4&#34; class=&#34;&#34;&gt;Recap: &lt;a href=&#34;http://www.christophengelhardt.com/ben-yoskovitz-measure-what-matters-microconf-2013/&#34;&gt;http://www.christophengelhardt.com/ben-yoskovitz-measure-what-matters-microconf-2013/&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;26363ea5-9961-4b9f-95df-929a82998fdc&#34; class=&#34;&#34;&gt;44. How to Sell Anything to Anyone (2013)&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;9aebc3f4-7f09-47b8-9998-b3f842f4ac08&#34; class=&#34;&#34;&gt;Video &lt;a href=&#34;https://vimeo.com/70901902&#34;&gt;https://vimeo.com/70901902&lt;/a&gt; by &lt;a href=&#34;https://twitter.com/SingleFounder&#34;&gt;Mike Taber&lt;/a&gt;, 41 min.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;8d5ca801-128b-4fb9-8e89-6a4372cff415&#34; class=&#34;&#34;&gt;Recap: &lt;a href=&#34;http://www.christophengelhardt.com/mike-taber-microconf-2013-a-k-a-the-liquor-fairy/&#34;&gt;http://www.christophengelhardt.com/mike-taber-microconf-2013-a-k-a-the-liquor-fairy/&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;b62b4779-c2b4-49c7-a03c-a21fa48aefab&#34; class=&#34;&#34;&gt;45. How to 10x in 15 months (2013)&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;462b56cc-b8f5-4777-9034-9d07c72ff615&#34; class=&#34;&#34;&gt;Video &lt;a href=&#34;https://vimeo.com/70901901&#34;&gt;https://vimeo.com/70901901&lt;/a&gt; by &lt;a href=&#34;https://www.softwarebyrob.com/&#34;&gt;Rob Walling&lt;/a&gt;, 51 min.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;c4563a3e-67c2-4a44-8899-887515a80d30&#34; class=&#34;&#34;&gt;Recap: &lt;a href=&#34;http://www.christophengelhardt.com/rob-walling-how-to-10x-in-15-months-microconf-2013/&#34;&gt;http://www.christophengelhardt.com/rob-walling-how-to-10x-in-15-months-microconf-2013/&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;496cfea6-e79c-408c-9e43-f99987cfc72d&#34; class=&#34;&#34;&gt;46. Cheap and Easy Customer Support (2012)&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;8e46c58c-2b45-4057-9f19-cd183cb2a176&#34; class=&#34;&#34;&gt;Video &lt;a href=&#34;https://vimeo.com/51306662&#34;&gt;https://vimeo.com/51306662&lt;/a&gt; by &lt;a href=&#34;https://twitter.com/sh&#34;&gt;Sarah Hattter&lt;/a&gt;, 40 min.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;e5b21586-2ade-48b0-afa0-4d0d0696ee54&#34; class=&#34;&#34;&gt;47. Google AdWords: Stop Losing &amp;amp; Start Exploiting (Really) (2012)&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;175a4aa2-ae07-4db7-ae01-31d3bc66afae&#34; class=&#34;&#34;&gt;Video &lt;a href=&#34;https://vimeo.com/51187193&#34;&gt;https://vimeo.com/51187193&lt;/a&gt; by &lt;a href=&#34;https://twitter.com/TheDaveCollins&#34;&gt;Dave Collins&lt;/a&gt;, 58 min.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;53163303-d74b-42d0-8cf2-a01a6ea80825&#34; class=&#34;&#34;&gt;48. How I Bootstrapped and Sold My Software Company By Maxing Out My Credit Cards (2012)&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;030b6b70-66c8-4c23-a320-ef4baa625d61&#34; class=&#34;&#34;&gt;Video &lt;a href=&#34;https://vimeo.com/50372726&#34;&gt;https://vimeo.com/50372726&lt;/a&gt; by Bill Bither, 46 min.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;eb90355d-5cc5-46a2-91f7-00eccc2583e8&#34; class=&#34;&#34;&gt;49. From Idea to 7 Figures in 2 years: The Story of Woothemes&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;faf43262-6126-4fc3-9dba-df3df97f0054&#34; class=&#34;&#34;&gt;Video &lt;a href=&#34;https://vimeo.com/50209990&#34;&gt;https://vimeo.com/50209990&lt;/a&gt; by &lt;a href=&#34;https://twitter.com/adii&#34;&gt;Adii Pienaar&lt;/a&gt;, 45 min.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;670e91c3-7251-4be5-8284-2fc41fd65ca8&#34; class=&#34;&#34;&gt;50. Ask Me Anything (2012)&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;4573a72d-976d-4564-8b57-b1d531a68451&#34; class=&#34;&#34;&gt;Video &lt;a href=&#34;https://vimeo.com/49023444&#34;&gt;https://vimeo.com/49023444&lt;/a&gt; by &lt;a href=&#34;https://twitter.com/peldi&#34;&gt;Peldi Guilizzoni&lt;/a&gt;, 48 min.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;99c4cd34-45e4-4950-97e6-17a4aa8e1208&#34; class=&#34;&#34;&gt;51. If You Don’t Like Drunk Frat Boys, Don’t Open an Irish Pub… (2012)&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;c54370a9-872f-485b-8241-d61f2a4814c0&#34; class=&#34;&#34;&gt;Video &lt;a href=&#34;https://vimeo.com/48962410&#34;&gt;https://vimeo.com/48962410&lt;/a&gt; by &lt;a href=&#34;https://stackingthebricks.com/&#34;&gt;Amy Hoy&lt;/a&gt;, 43 min.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;c8e74ffd-e213-4b44-922b-2c48e11552a1&#34; class=&#34;&#34;&gt;Transcript: &lt;a href=&#34;https://stackingthebricks.com/a-customer-is-your-mvp-a-video-talk-on-making-products-that-sell/&#34;&gt;https://stackingthebricks.com/a-customer-is-your-mvp-a-video-talk-on-making-products-that-sell/&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;9b112608-2251-41d3-9ec7-950397384f01&#34; class=&#34;&#34;&gt;52. Growth Hacking (2012)&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;357c1fb0-4679-4e3a-a238-e7058e35c71c&#34; class=&#34;&#34;&gt;Video &lt;a href=&#34;https://vimeo.com/48592609&#34;&gt;https://vimeo.com/48592609&lt;/a&gt; by Dan Martell, 39 min.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;58aa5bc8-1be6-45ba-b3c4-d9744df52d4d&#34; class=&#34;&#34;&gt;53. Losers Have Goals, Winners Have Systems (2012)&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;72192951-a286-4b73-aef6-b9f0bf3c04dd&#34; class=&#34;&#34;&gt;Video &lt;a href=&#34;https://vimeo.com/48571431&#34;&gt;https://vimeo.com/48571431&lt;/a&gt; by &lt;a href=&#34;https://twitter.com/SingleFounder&#34;&gt;Mike Taber&lt;/a&gt;, 27 min.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;f9b3af85-4473-43c6-b774-325fabd2ef3c&#34; class=&#34;&#34;&gt;Related article: &lt;a href=&#34;http://www.singlefounder.com/losers-have-goals-winners-have-systems/&#34;&gt;http://www.singlefounder.com/losers-have-goals-winners-have-systems/&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;78c6eec5-049f-476c-bf8a-bc62e865f1d3&#34; class=&#34;&#34;&gt;54. Naked Business: How Honesty Makes You More Money (2012)&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;aecadff9-b62d-46a7-817f-abdde6b7e98f&#34; class=&#34;&#34;&gt;Video &lt;a href=&#34;https://vimeo.com/48549019&#34;&gt;https://vimeo.com/48549019&lt;/a&gt; by &lt;a href=&#34;https://twitter.com/asmartbear&#34;&gt;Jason Cohen&lt;/a&gt;, 55 min.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;6c7b6a66-2c1f-4a30-b84f-0a85a0036769&#34; class=&#34;&#34;&gt;55. Finding Your Flywheel (2012)&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;5754a045-99cc-4cd8-b9fc-3dfecb2a45c4&#34; class=&#34;&#34;&gt;Video &lt;a href=&#34;https://vimeo.com/47465229&#34;&gt;https://vimeo.com/47465229&lt;/a&gt; by &lt;a href=&#34;https://www.softwarebyrob.com/&#34;&gt;Rob Walling&lt;/a&gt;, 1 hr 04 min.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;51604a06-378e-4c09-9f6b-f7c5fec23dd6&#34; class=&#34;&#34;&gt;56. How to Engineer Marketing Success (2012)&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;c6e95e7f-98b4-4cb1-80be-e20a7de8f023&#34; class=&#34;&#34;&gt;&lt;a href=&#34;https://vimeo.com/47311461&#34;&gt;https://vimeo.com/47311461&lt;/a&gt; by &lt;a href=&#34;http://www.kalzumeus.com/&#34;&gt;Patrick McKenzie&lt;/a&gt;, 51 min.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;66dfa645-b37f-45ef-8b72-7d76e8c53a44&#34; class=&#34;&#34;&gt;57. More Lessons I’ve Learned as a Serial Entrepreneur (2012)&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;73e9b613-7710-48ac-8e9f-90ef1b087e65&#34; class=&#34;&#34;&gt;&lt;a href=&#34;https://vimeo.com/46893380&#34;&gt;https://vimeo.com/46893380&lt;/a&gt; by &lt;a href=&#34;https://hitenism.com/&#34;&gt;Hiten Shah&lt;/a&gt;, 48 min.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;a8f6227f-3252-4d8c-91bc-6f28ea20027e&#34; class=&#34;&#34;&gt;Related resources:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;c8af2327-e3a5-422b-b9d3-ae6b7c4ce286&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://bootstrapping.io/microconf-2015/&#34;&gt;https://bootstrapping.io/microconf-2015/&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://www.phraseexpander.com/microconf-2014/&#34;&gt;https://www.phraseexpander.com/microconf-2014/&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;http://www.christophengelhardt.com/microconf-europe-2014-hub/&#34;&gt;http://www.christophengelhardt.com/microconf-europe-2014-hub/&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;http://www.christophengelhardt.com/microconf-europe-2013-hub-page/&#34;&gt;http://www.christophengelhardt.com/microconf-europe-2013-hub-page/&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;http://www.christophengelhardt.com/microconf-2013-hub-page/&#34;&gt;http://www.christophengelhardt.com/microconf-2013-hub-page/&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://accidentaltechnologist.com/entrepreneurship/microconf-2013-was-freakin-awesome/&#34;&gt;https://accidentaltechnologist.com/entrepreneurship/microconf-2013-was-freakin-awesome/&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://vimeo.com/theblnbusinessofsoftware&#34;&gt;https://vimeo.com/theblnbusinessofsoftware&lt;/a&gt; : talks from Business Of Software conference&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;/article/wjRD/solo-founders-with-profitable-businesses-collected-stories.html&#34;&gt;solo founders with profitable businesses&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;&lt;/div&gt;</content>
  </entry>
  <entry>
   <title>How to install latest clang (6.0) on Ubuntu 16.04 (xenial) / WSL</title>
   <link href="https://blog.kowalczyk.info/article/k/how-to-install-latest-clang-6.0-on-ubuntu-16.04-xenial-wsl.html" rel="alternate"></link>
   <updated>2017-11-20T20:37:02-08:00</updated>
   <id>tag:blog.kowalczyk.info,2017-11-20:/article/k/how-to-install-latest-clang-6.0-on-ubuntu-16.04-xenial-wsl.html</id>
   <content type="html">&lt;p&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;notion-page&#34; id=&#34;75f1199f-2c17-4fb9-87bc-9b1fb4989375&#34;&gt;&#xA;  &lt;div id=&#34;e196657f-8b5d-4e2d-986a-b5c202107231&#34; class=&#34;&#34;&gt;This article describes installing latest clang (llvm) on Ubuntu 16.04 (Xenial), which is also the default distro for &lt;a href=&#34;https://msdn.microsoft.com/en-us/commandline/wsl/about&#34;&gt;Windows Subsystem for Linux&lt;/a&gt; (WSL).&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;5484a575-a4ba-476f-a138-a36753302ef1&#34; class=&#34;&#34;&gt;Ubuntu 16.04 ships with relatively old clang. Running &lt;code&gt;sudo apt-get install clang&lt;/code&gt; installs version 3.8.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;3c852935-52d9-43fa-b240-1aaf3e48438c&#34; class=&#34;&#34;&gt;To install latest version (currently 6.0) run:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; sudo apt-key add -&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt-add-repository &lt;span class=&#34;s2&#34;&gt;&amp;#34;deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-6.0 main&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt-get update&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt-get install -y clang-6.0&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;57cc2ffa-9c6c-4bfc-bbc0-83e70e2a664c&#34; class=&#34;&#34;&gt;&lt;code&gt;clang-6.0&lt;/code&gt; is the name of the executable (and so is &lt;code&gt;lldb-6.0&lt;/code&gt; etc.).&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;df10ac60-a2ef-4690-9b9b-7bb466979f4c&#34; class=&#34;&#34;&gt;How it works&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;7d6573f6-0394-4392-9590-f05d647cd64e&#34; class=&#34;&#34;&gt;Let’s deconstruct those commands so that you know what’s happening.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;bf291411-b73f-4e67-be60-004464302e49&#34; class=&#34;&#34;&gt;How does apt know what packages are available?&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;ce617085-2be7-4358-9b4c-433ff1df15c9&#34; class=&#34;&#34;&gt;Apt queries package servers to get a list of available deb packages. Default Ubuntu installation knows about official Ubuntu servers but you can run your own server to provide additional packages.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;25b42e2d-4577-4cd4-aa84-486908153c50&#34; class=&#34;&#34;&gt;List of servers is in &lt;code&gt;/etc/apt/sources.list&lt;/code&gt;. Here’s how it looks by default on Ubuntu 16.04:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ cat /etc/apt/sources.list&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;deb http://archive.ubuntu.com/ubuntu/ xenial main restricted universe multiverse&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;deb http://archive.ubuntu.com/ubuntu/ xenial-updates main restricted universe multiverse&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;deb http://security.ubuntu.com/ubuntu/ xenial-security main restricted universe multiverse&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;0ee18d46-9577-4f6d-ad43-4472aa9c669e&#34; class=&#34;&#34;&gt;Ubuntu only provides a relatively old clang 3.8. Luckily, Apple creates deb packages and maintains a server for all llvm/clang releases and most Ubuntu distros.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;b9ce976e-a1f9-4c61-aa97-3f09bccbca97&#34; class=&#34;&#34;&gt;&lt;code&gt;sudo apt-add-repository &amp;quot;deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-6.0 main&amp;quot;&lt;/code&gt; adds llvm’s server for Ubuntu 16.04 to &lt;code&gt;/etc/apt/sources.list&lt;/code&gt;.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;7306e452-1e12-4438-8ab2-3082b51e912a&#34; class=&#34;&#34;&gt;&lt;code&gt;sudo apt-get update&lt;/code&gt; downloads the latest list of packages from all servers, including the one we just added&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;1bc236d1-c16e-419c-b998-58f5c772d889&#34; class=&#34;&#34;&gt;For security, packages are signed with private keys. You need public key to verify package signature.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;8966276b-4bb9-4c0b-be41-f263d5a6c381&#34; class=&#34;&#34;&gt;&lt;code&gt;wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -&lt;/code&gt; downloads llvm’s server public key.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;aea3d3ff-eb1a-4bd5-9b67-78196a4f106c&#34; class=&#34;&#34;&gt;&lt;code&gt;sudo apt-get -y install clang-6.0&lt;/code&gt; installs newly available package &lt;code&gt;clang-6.0&lt;/code&gt;.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;0bb555a8-24ac-46fa-94a4-dd9a99e478ab&#34; class=&#34;&#34;&gt;Flag &lt;code&gt;-y&lt;/code&gt; disables confirmation prompt.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;460f9d33-150f-44ae-9bf5-bb739349201c&#34; class=&#34;&#34;&gt;What if there’s a newer version of clang or a different version of Ubuntu?&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;3c28a100-750c-4bcb-b645-3aef754a6164&#34; class=&#34;&#34;&gt;There is a new llvm/clang release every 6 months. What to do for newer version?&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;464c0d5c-51ba-43e5-a8df-d42b3abc9ec4&#34; class=&#34;&#34;&gt;Visit https://apt.llvm.org/ and locate the equivalent of &lt;code&gt;deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-6.0 main&lt;/code&gt; for desired combo of clang/Ubuntu and correspondingly update &lt;code&gt;apt-add-repository ...&lt;/code&gt; line in the above instructions.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;7b76c7b9-62d7-4f0c-92b0-c475c0e0703c&#34; class=&#34;&#34;&gt;Making latest version available as clang&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;2ab29c74-9a68-4cae-8144-372f01fe5a45&#34; class=&#34;&#34;&gt;This version is installed side-by-side with the default clang 3.8. To run it, you have to explicitly say &lt;code&gt;clang-6.0&lt;/code&gt;. &lt;code&gt;clang&lt;/code&gt; will either refer to 3.8 (if you’ve installed it) or nothing at all.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;e02f9b78-53da-4e4a-9a42-d55a35c1bfa6&#34; class=&#34;&#34;&gt;You can reconfigure the system so that &lt;code&gt;clang&lt;/code&gt; refers to clang 6:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-3.8 &lt;span class=&#34;m&#34;&gt;100&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-6.0 &lt;span class=&#34;m&#34;&gt;1000&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;update-alternatives --install /usr/bin/clang++ clang /usr/bin/clang-3.8 &lt;span class=&#34;m&#34;&gt;100&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;update-alternatives --install /usr/bin/clang clang /usr/bin/clang-3.8 &lt;span class=&#34;m&#34;&gt;100&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;update-alternatives --install /usr/bin/clang clang /usr/bin/clang-6.0 &lt;span class=&#34;m&#34;&gt;1000&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;update-alternatives --config clang&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;update-alternatives --config clang++&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;f51b0284-bd3a-4096-ad5f-01e08a73ddb3&#34; class=&#34;&#34;&gt;This might help if you have scripts that hard-code name of the compiler and don’t allow over-writing it.&#xA;  &lt;/div&gt;&#xA;&lt;/div&gt;</content>
  </entry>
  <entry>
   <title>Guide to predefined macros in C++ compilers (gcc, clang, msvc etc.)</title>
   <link href="https://blog.kowalczyk.info/article/j/guide-to-predefined-macros-in-c-compilers-gcc-clang-msvc-etc..html" rel="alternate"></link>
   <updated>2017-11-07T17:07:45-08:00</updated>
   <id>tag:blog.kowalczyk.info,2017-11-07:/article/j/guide-to-predefined-macros-in-c-compilers-gcc-clang-msvc-etc..html</id>
   <content type="html">&lt;p&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;notion-page&#34; id=&#34;578cb0cc-6552-48ce-85a1-74ab9eda7ef3&#34;&gt;&#xA;  &lt;div id=&#34;199ec0ef-8b27-447d-aa8a-6390e0c7f508&#34; class=&#34;&#34;&gt;When writing portable C++ code you need to write conditional code that depends on compiler used or the OS for which the code is written.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;46d9167f-75af-4bd8-af86-07cdb4d5a79c&#34; class=&#34;&#34;&gt;Here’s a typical case:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;#if defined (_MSC_VER)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;// code specific to Visual Studio compiler&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;#endif&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;ec91f105-678b-4272-8785-5cb9b8b02a6b&#34; class=&#34;&#34;&gt;To perform those checks you need to check pre-processor macros that various compilers set.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;962994ba-13a7-4caa-9e82-2b670e56d498&#34; class=&#34;&#34;&gt;It can either be binary is defined vs. is not defined check (e.g. &lt;code&gt;__APPLE__&lt;/code&gt;) or checking a value of the macro (e.g. &lt;code&gt;_MSC_VER&lt;/code&gt; defines version of Visual Studio compiler).&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;dd18fc1b-e14d-4ed7-8d58-4113047ba527&#34; class=&#34;&#34;&gt;This document describes macros set by various compilers.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;db5540f7-09fd-4768-bb5f-376f0339672f&#34; class=&#34;&#34;&gt;Other documentations: &#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;a27f7e00-8574-4991-8d96-6b2354e2662c&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;predefined macros in &lt;a href=&#34;https://msdn.microsoft.com/en-us/library/b0084kay.aspx&#34;&gt;Visual Studio&lt;/a&gt; &#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://sourceforge.net/p/predef/wiki/Compilers/&#34;&gt;https://sourceforge.net/p/predef/wiki/Compilers/&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;to list clang’s pre-defined macros: &lt;code&gt;clang -x c /dev/null -dM -E&lt;/code&gt;&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;to list gcc’s pre-defined macros: &lt;code&gt;gcc -x c /dev/null -dM -E&lt;/code&gt; (not that on mac gcc is actually clang that ships with XCode)&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;the &lt;code&gt;-x c /dev/null -dM -E&lt;/code&gt; also works for mingw (which is based on gcc)&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;listing predefined macros &lt;a href=&#34;http://nadeausoftware.com/articles/2011/12/c_c_tip_how_list_compiler_predefined_macros&#34;&gt;for other compilers&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;h2 id=&#34;08721030-d4ef-4664-a93b-505825c203e8&#34; class=&#34;&#34;&gt;Checking for OS (platform)&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;1f8729f1-1416-4aa1-b93c-526eda5f4418&#34; class=&#34;&#34;&gt;To check for which OS the code is compiled:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Linux and Linux-derived           __linux__&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Android                           __ANDROID__ (implies __linux__)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Linux (non-Android)               __linux__ &amp;amp;&amp;amp; !__ANDROID__&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Darwin (Mac OS X and iOS)         __APPLE__&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Akaros (http://akaros.org)        __ros__&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Windows                           _WIN32&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Windows 64 bit                    _WIN64 (implies _WIN32)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;NaCL                              __native_client__&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;AsmJS                             __asmjs__&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Fuschia                           __Fuchsia__&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;h2 id=&#34;66690e2b-da63-4deb-89d0-8529a4403f2f&#34; class=&#34;&#34;&gt;Checking the compiler:&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;af501a3d-e3f9-4a32-8b98-4ceea569512b&#34; class=&#34;&#34;&gt;To check which compiler is used:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Visual Studio       _MSC_VER&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;gcc                 __GNUC__&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;clang               __clang__&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;emscripten          __EMSCRIPTEN__ (for asm.js and webassembly)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;MinGW 32            __MINGW32__&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;MinGW-w64 32bit     __MINGW32__&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;MinGW-w64 64bit     __MINGW64__&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;h2 id=&#34;0927dc3c-de57-4ea2-9216-9b29341f39a4&#34; class=&#34;&#34;&gt;Checking compiler version&#xA;  &lt;/h2&gt;&#xA;  &lt;h2 id=&#34;b4c2c248-7461-458d-9d6f-f19223575538&#34; class=&#34;&#34;&gt;gcc&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;d19c3bac-810a-4a10-bf12-58732b664607&#34; class=&#34;&#34;&gt;&lt;code&gt;__GNUC__&lt;/code&gt; (e.g. 5) and &lt;code&gt;__GNUC_MINOR__&lt;/code&gt; (e.g. 1).&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;ed3d2ee1-94ea-411b-9d86-eef5d5adae3d&#34; class=&#34;&#34;&gt;To check that this is gcc compiler version 5.1 or greater:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;#if defined(__GNUC__) &amp;amp;&amp;amp; (__GNUC___ &amp;gt; 5 || (__GNUC__ == 5 &amp;amp;&amp;amp; __GNUC_MINOR__ &amp;gt;= 1))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;// this is gcc 5.1 or greater&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;#endif&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;845bb4d8-0fc3-4160-8f4c-9e8f075f0b90&#34; class=&#34;&#34;&gt;Notice the chack has to be: &lt;code&gt;major &amp;gt; 5 || (major == 5 &amp;amp;&amp;amp; minor &amp;gt;= 1)&lt;/code&gt;. If you only do &lt;code&gt;major == 5 &amp;amp;&amp;amp; minor &amp;gt;= 1&lt;/code&gt;, it won’t work for version 6.0.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;a2a2398a-5ab0-4ef5-9ac9-0517a1dfe32f&#34; class=&#34;&#34;&gt;clang&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;b0292f8a-c3f4-40ad-932b-4272a41407e7&#34; class=&#34;&#34;&gt;&lt;code&gt;__clang_major__&lt;/code&gt;, &lt;code&gt;__clang_minor__&lt;/code&gt;, &lt;code&gt;__clang_patchlevel__&lt;/code&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;28562010-854c-4412-a704-7b0ab0b5e36d&#34; class=&#34;&#34;&gt;Visual Studio&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;459354c1-8163-49bf-b13d-3946e50f2131&#34; class=&#34;&#34;&gt;&lt;code&gt;_MSC_VER&lt;/code&gt; and &lt;code&gt;_MSC_FULL_VER&lt;/code&gt;:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;VS                        _MSC_VER   _MSC_FULL_VER&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;1                         800&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;3                         900&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;4                         1000&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;4                         1020&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;5                         1100&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;6                         1200&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;6 SP6                     1200    12008804&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;7                         1300    13009466&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;7.1 (2003)                1310    13103077&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;8 (2005)                  1400    140050727&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;9 (2008)                  1500    150021022&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;9 SP1                     1500    150030729&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;10 (2010)                 1600    160030319&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;10 (2010) SP1             1600    160040219&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;11 (2012)                 1700    170050727&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;12 (2013)                 1800    180021005&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;14 (2015)                 1900    190023026&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;14 (2015 Update 1)        1900    190023506&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;14 (2015 Update 2)        1900    190023918&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;14 (2015 Update 3)        1900    190024210&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;15 (2017 Update 1 &amp;amp; 2)    1910    191025017&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;15 (2017 Update 3 &amp;amp; 4)    1911&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;15 (2017 Update 5)        1912&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;a544baf4-80f5-43ce-b7f9-5aa84e2cac9a&#34; class=&#34;&#34;&gt;More information:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;0d19c7d4-e536-4f18-8085-d9b271145094&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://blogs.msdn.microsoft.com/vcblog/2016/10/05/visual-c-compiler-version/&#34;&gt;https://blogs.msdn.microsoft.com/vcblog/2016/10/05/visual-c-compiler-version/&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://blogs.msdn.microsoft.com/vcblog/2017/11/15/side-by-side-minor-version-msvc-toolsets-in-visual-studio-2017/#comment-467525&#34;&gt;https://blogs.msdn.microsoft.com/vcblog/2017/11/15/side-by-side-minor-version-msvc-toolsets-in-visual-studio-2017/#comment-467525&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;h2 id=&#34;a4d5821c-3c1d-455f-8481-60142fbde4b9&#34; class=&#34;&#34;&gt;MinGW&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;81df19cd-221d-4aa0-9960-c4e871b0c8f5&#34; class=&#34;&#34;&gt;MinGW (aka MinGW32) and MinGW-w64 32bit: &lt;code&gt;__MINGW32_MAJOR_VERSION&lt;/code&gt; and &lt;code&gt;__MINGW32_MINOR_VERSION&lt;/code&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;1c717946-2e00-48f0-ab23-8831c66e03df&#34; class=&#34;&#34;&gt;MinGW-w64 64bit: &lt;code&gt;__MINGW64_VERSION_MAJOR&lt;/code&gt; and &lt;code&gt;__MINGW64_VERSION_MINOR&lt;/code&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;b4e20182-726a-4366-bd1b-8a2732711cad&#34; class=&#34;&#34;&gt;Checking processor architecture&#xA;  &lt;/h2&gt;&#xA;  &lt;h2 id=&#34;2a45f270-cbf8-46b4-b2e0-2a3d1d437ae5&#34; class=&#34;&#34;&gt;gcc&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;4de8ee53-b69d-405e-9059-9a27d6c307f2&#34; class=&#34;&#34;&gt;The meaning of those should be self-evident:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;f0f45ca8-c88a-46c2-9630-87bd30573e4c&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;&lt;code&gt;__i386__&lt;/code&gt;&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;code&gt;__x86_64__&lt;/code&gt;&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;code&gt;__arm__&lt;/code&gt;. If defined, you can further check:&#xA;      &lt;ul id=&#34;71c3fd2d-01ed-49aa-84f3-861e2c615e9e&#34; class=&#34;bulleted-list&#34;&gt;&#xA;        &lt;li&gt;&lt;code&gt;__ARM_ARCH_5T__&lt;/code&gt;&#xA;        &lt;/li&gt;&#xA;        &lt;li&gt;&lt;code&gt;__ARM_ARCH_7A__&lt;/code&gt;&#xA;        &lt;/li&gt;&#xA;      &lt;/ul&gt;&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;code&gt;__powerpc64__&lt;/code&gt;&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;code&gt;__aarch64__&lt;/code&gt;&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;&lt;/div&gt;</content>
  </entry>
  <entry>
   <title>Tutorial for github.com/kjk/flex Go package (implementation of CSS flexbox algorithm)</title>
   <link href="https://blog.kowalczyk.info/article/9/tutorial-for-github.comkjkflex-go-package-implementation-of-css-flexbox-algorithm.html" rel="alternate"></link>
   <updated>2017-08-04T18:32:51-07:00</updated>
   <id>tag:blog.kowalczyk.info,2017-08-04:/article/9/tutorial-for-github.comkjkflex-go-package-implementation-of-css-flexbox-algorithm.html</id>
   <content type="html">&lt;p&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;notion-page&#34; id=&#34;693ee625-7f3c-428f-a8e1-15607c79260e&#34;&gt;&#xA;  &lt;div id=&#34;1b5adf18-4298-4538-9a4c-8ddd93a6919e&#34; class=&#34;&#34;&gt;Package &lt;a href=&#34;https://github.com/kjk/flex&#34;&gt;github.com/kjk/flex&lt;/a&gt; implements &lt;a href=&#34;https://www.w3.org/TR/css-flexbox-1/&#34;&gt;CSS flexbox&lt;/a&gt; layout algorithm in Go.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;e72aa12e-e2cd-4324-9bdd-ca7e9538d64d&#34; class=&#34;&#34;&gt;It’s a pure Go &lt;a href=&#34;/article/wN9R/experience-porting-4.5k-loc-of-c-to-go-facebooks-css-flexbox-implementation-yoga.html&#34;&gt;port&lt;/a&gt; of Facebook’s &lt;a href=&#34;https://github.com/facebook/yoga&#34;&gt;Yoga&lt;/a&gt; C library.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;ba073990-f885-4be6-9d70-43fe0c39ce25&#34; class=&#34;&#34;&gt;High-level API overview&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;63469f44-ecd1-4302-84df-56fea75dc5aa&#34; class=&#34;&#34;&gt;Despite implementing CSS flexbox spec, it isn’t tied to CSS/HTML in any way. Yoga, for example, can be integrated with iOS app and used to layout UIView hierarchy.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;08ac030b-7324-4061-8ef7-644edba13e72&#34; class=&#34;&#34;&gt;The library works on abstract tree of nodes. In HTML a node would correspond to a block element like a &lt;code&gt;div&lt;/code&gt;. When used in Cocoa app, a node could represent &lt;code&gt;UIView&lt;/code&gt; or &lt;code&gt;NSView&lt;/code&gt;.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;f081459d-802f-4432-89d7-f30d60fb2705&#34; class=&#34;&#34;&gt;When used on windows, it could represent a HWND-based control.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;9e3740a3-eb97-4177-95e4-456fa16d1999&#34; class=&#34;&#34;&gt;The high-level use is: &#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;a83cb376-7619-4217-a2b8-0e7787d34f3c&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;create a tree of nodes that represents a layout you want to represent&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;set desired flexbox properties on each node using &lt;code&gt;node.StyleSet*()&lt;/code&gt; &lt;a href=&#34;https://github.com/kjk/flex/blob/master/yoga_props.go#L58&#34;&gt;functions&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;call &lt;code&gt;flex.CalculateLayout(rootNode, parentWidth, parentHeight, direction)&lt;/code&gt;&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;each node is now measured and positioned so you can e.g. size and position widgets associated with each node. You can get the size on position of nodes with &lt;code&gt;node.LayoutGet*()&lt;/code&gt; &lt;a href=&#34;https://github.com/kjk/flex/blob/master/yoga_props.go#L531&#34;&gt;functions&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;when layout hierachy changes (e.g. the node represents a label and you’ve changed its text, which changes it’s intrinsic size), call &lt;code&gt;node.MarkDirty()&lt;/code&gt; and &lt;code&gt;CalculateLayout()&lt;/code&gt; to re-calculate new size/position of the nodes&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;h2 id=&#34;204b4f07-5190-4f98-9e7e-393ad6bcf52a&#34; class=&#34;&#34;&gt;An exmple&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;34d3e1b5-b88e-43fb-b62e-622102d5522c&#34; class=&#34;&#34;&gt;Let’s assume that we want to re-create the following HTML layout:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;percentage_multiple_nested_with_padding_margin_and_percentage_values&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;style&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;width: 200px; height: 200px; flex-direction: column;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;style&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;flex-grow: 1; flex-basis: 10%; min-width: 60%; margin: 5px; padding: 3px;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;style&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;width: 50%; margin: 5px; padding: 3%;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;style&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;width: 45%; margin: 5%; padding: 3px;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;style&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;flex-grow: 4; flex-basis: 15%; min-width: 20%;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;eef7f1aa-234a-4526-a5ed-26f229c7e37c&#34; class=&#34;&#34;&gt;The equivalent Go code is:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;config&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;flex&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;NewConfig&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;root&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;flex&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;NewNodeWithConfig&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;config&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;root&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;StyleSetWidth&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;200&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;root&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;StyleSetHeight&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;200&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;rootChild0&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;flex&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;NewNodeWithConfig&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;config&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;rootChild0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;StyleSetFlexGrow&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;rootChild0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;StyleSetFlexBasisPercent&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;rootChild0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;StyleSetMargin&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;EdgeLeft&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;rootChild0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;StyleSetMargin&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;EdgeTop&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;rootChild0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;StyleSetMargin&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;EdgeRight&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;rootChild0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;StyleSetMargin&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;EdgeBottom&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;rootChild0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;StyleSetPadding&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;EdgeLeft&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;rootChild0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;StyleSetPadding&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;EdgeTop&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;rootChild0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;StyleSetPadding&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;EdgeRight&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;rootChild0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;StyleSetPadding&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;EdgeBottom&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;rootChild0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;StyleSetMinWidthPercent&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;60&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;root&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;InsertChild&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;rootChild0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;rootChild0Child0&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;flex&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;NewNodeWithConfig&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;config&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;rootChild0Child0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;StyleSetMargin&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;EdgeLeft&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;rootChild0Child0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;StyleSetMargin&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;EdgeTop&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;rootChild0Child0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;StyleSetMargin&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;EdgeRight&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;rootChild0Child0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;StyleSetMargin&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;EdgeBottom&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;rootChild0Child0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;StyleSetPaddingPercent&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;EdgeLeft&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;rootChild0Child0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;StyleSetPaddingPercent&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;EdgeTop&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;rootChild0Child0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;StyleSetPaddingPercent&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;EdgeRight&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;rootChild0Child0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;StyleSetPaddingPercent&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;EdgeBottom&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;rootChild0Child0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;StyleSetWidthPercent&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;50&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;rootChild0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;InsertChild&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;rootChild0Child0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;rootChild0Child0Child0&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;flex&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;NewNodeWithConfig&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;config&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;rootChild0Child0Child0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;StyleSetMarginPercent&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;EdgeLeft&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;rootChild0Child0Child0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;StyleSetMarginPercent&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;EdgeTop&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;rootChild0Child0Child0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;StyleSetMarginPercent&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;EdgeRight&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;rootChild0Child0Child0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;StyleSetMarginPercent&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;EdgeBottom&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;rootChild0Child0Child0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;StyleSetPadding&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;EdgeLeft&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;rootChild0Child0Child0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;StyleSetPadding&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;EdgeTop&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;rootChild0Child0Child0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;StyleSetPadding&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;EdgeRight&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;rootChild0Child0Child0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;StyleSetPadding&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;EdgeBottom&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;rootChild0Child0Child0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;StyleSetWidthPercent&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;45&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;rootChild0Child0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;InsertChild&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;rootChild0Child0Child0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;rootChild1&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;flex&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;NewNodeWithConfig&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;config&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;rootChild1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;StyleSetFlexGrow&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;rootChild1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;StyleSetFlexBasisPercent&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;15&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;rootChild1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;StyleSetMinWidthPercent&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;20&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;root&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;InsertChild&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;rootChild1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;flex&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;CalculateLayout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;root&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;flex&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Undefined&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;flex&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Undefined&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;DirectionLTR&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;c1281790-dba7-4f15-8f35-335c0a3a0455&#34; class=&#34;&#34;&gt;After &lt;code&gt;CalculateLayout&lt;/code&gt; we can see the position of each node e.g.:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;fmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Printf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;root left: %f\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;root&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;LayoutGetLeft&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 0&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Printf&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;root top: %f\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;root&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;LayoutGetTop&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 0&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Printf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;root width: %f\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;root&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;LayoutGetWidth&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 200&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Printf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;root height: %f\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;root&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;LayoutGetHeight&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 200&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;a381fc6b-f882-4880-9e6d-d089b103489b&#34; class=&#34;&#34;&gt;To see example for every flexbox property, look into &lt;a href=&#34;https://github.com/facebook/yoga/tree/master/gentest/fixtures&#34;&gt;github.com/facebook/yoga/gentest/fixtures&lt;/a&gt;. Their names hint at which properties are being used.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;90fb4141-a8de-46a3-8b0d-d447859f6679&#34; class=&#34;&#34;&gt;Each file there has corresponding &lt;code&gt;*_test.go&lt;/code&gt; file in &lt;a href=&#34;https://github.com/kjk/flex&#34;&gt;github.com/kjk/flex&lt;/a&gt; directory which shows how to express it in Go.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;c38daa0b-4206-41de-8f56-c9039720efef&#34; class=&#34;&#34;&gt;Size of root’s parent&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;6eda708a-3df2-4264-807e-1fe79b54951a&#34; class=&#34;&#34;&gt;Notice that in this particular example we used &lt;code&gt;flex.Undefined&lt;/code&gt; as both height and width of the parent container.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;ef0aa016-3b05-4428-a8f1-f442c1a474af&#34; class=&#34;&#34;&gt;Imagine you’re using &lt;code&gt;flex&lt;/code&gt; to implment layout for a dekstop application where each &lt;code&gt;flex.Node&lt;/code&gt; represents a control inside the window.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;e86e53d3-1f93-4a41-80bd-03e1fcef2e95&#34; class=&#34;&#34;&gt;Window is the parent of root node.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;f8e2e969-c800-4b19-b989-f35fe194a8dd&#34; class=&#34;&#34;&gt;In response to user resizing the window, you want to pass width/height of the window to &lt;code&gt;flex.CalculateLayout()&lt;/code&gt;.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;9271d23e-1311-44e5-94b1-ece05645af2f&#34; class=&#34;&#34;&gt;When you create the window initially, you might do the reverse: pass &lt;code&gt;flex.Undefined&lt;/code&gt; as width/height of parent container and then use the size of root node as the size of the window, to size it to its content.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;e0e9eece-4d42-4ba8-ad78-4ae7d77bec27&#34; class=&#34;&#34;&gt;Measure function&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;3147e337-9699-47bd-a050-eced6ec38b22&#34; class=&#34;&#34;&gt;Imagine that a node represents an OS button. The button has some intrisic size dictated by its text.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;1bf7b130-5800-4945-8f0b-0d34127a0e78&#34; class=&#34;&#34;&gt;To represent that size &lt;code&gt;flex&lt;/code&gt; allows setting a measuring function with &lt;code&gt;node.SetMeasureFunc(measureFunc MeasureFunc)&lt;/code&gt;. It’s definition is:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;type&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;MeasureFunc&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;node&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Node&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;width&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;float32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;widthMode&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;MeasureMode&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;height&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;float32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;heightMode&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;MeasureMode&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Size&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;bcb3afeb-71a5-4212-82fe-06e6aea6a6e2&#34; class=&#34;&#34;&gt;The functions takes a hint width/height which is the size of parent container and returns intrinsic size of node.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;19cf2cdc-806f-4102-882d-efa7bf58f05f&#34; class=&#34;&#34;&gt;This is usefule e.g. when a node represents a paragraph of text. When you know width of the parent container, you can break it into multi-line text.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;a2afd968-cda5-4027-9aa7-7dc250a0eeb0&#34; class=&#34;&#34;&gt;If measuring function needs some state, you can use &lt;code&gt;node.Context&lt;/code&gt; to store it.&#xA;  &lt;/div&gt;&#xA;&lt;/div&gt;</content>
  </entry>
  <entry>
   <title>Experience porting 4.5k loc of C to Go (Facebook&#39;s CSS flexbox implementation Yoga)</title>
   <link href="https://blog.kowalczyk.info/article/wN9R/experience-porting-4.5k-loc-of-c-to-go-facebooks-css-flexbox-implementation-yoga.html" rel="alternate"></link>
   <updated>2017-08-02T00:00:00Z</updated>
   <id>tag:blog.kowalczyk.info,2017-08-02:/article/wN9R/experience-porting-4.5k-loc-of-c-to-go-facebooks-css-flexbox-implementation-yoga.html</id>
   <content type="html">&lt;p&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;notion-page&#34; id=&#34;4a5192a1-8aeb-40da-a8f5-6b1236435340&#34;&gt;&#xA;  &lt;div id=&#34;ba2841ac-1229-4f81-b58a-6e84cc0b6bed&#34; class=&#34;&#34;&gt;I’m working on desktop GUI apps for Windows in Go.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;664a17e6-2395-4395-a075-2483d31d032b&#34; class=&#34;&#34;&gt;As it uses native win32 controls I don’t have to implement my own buttons, list boxes etc.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;fa1fdedc-c466-4fc1-9475-f2e747c71ae1&#34; class=&#34;&#34;&gt;However, win32 doesn’t have anything to help you layout things on the screen. Manual positioning is painful.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;99574d97-c6b9-4167-850f-c7684b9ffb0e&#34; class=&#34;&#34;&gt;I started looking into a writing a layout engine. There are many systems to be inspired by.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;b123dd05-c4d1-4447-b762-f121e2932e96&#34; class=&#34;&#34;&gt;I did some work in Winforms so I could copy their ideas.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;289d1a6c-e9d9-4b44-b8ef-9f499fa5192b&#34; class=&#34;&#34;&gt;I could look into copying Android layout logic.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;067ee5d5-aaf7-420e-84e4-516d56d4138d&#34; class=&#34;&#34;&gt;I could look into implementing constraint-based layout like in iOS.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;6ab52119-8125-4f88-b573-e369aea3693d&#34; class=&#34;&#34;&gt;The option I liked the best is CSS flexbox. It’s not the simplest, it’s not the most complicated, but the biggest win is that many people, including myself, are already familiar with it.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;069965fe-0ad8-413a-8a5a-e32c4ea7179d&#34; class=&#34;&#34;&gt;I started looking for an implementation in Go. I found one in &lt;a href=&#34;https://github.com/golang/exp/tree/master/shiny/widget/flex&#34;&gt;shiny&lt;/a&gt; project, but it’s simplified implementation.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;f691e2aa-d3de-4b7e-820c-2d74735a5c62&#34; class=&#34;&#34;&gt;The most complete implementation is Facebook’s &lt;a href=&#34;https://github.com/facebook/yoga&#34;&gt;Yoga&lt;/a&gt;, but it’s written in C (with bindings to many languages).&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;ef7c3289-8b54-4979-b203-38591f542cf4&#34; class=&#34;&#34;&gt;I could write cgo bindings but I prefer my Go pure so I decided to manually port C code to Go, all 4.5k lines of it.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;57637a6f-8564-4e31-9ca0-7f7b7df13772&#34; class=&#34;&#34;&gt;The result is &lt;a href=&#34;https://github.com/kjk/flex&#34;&gt;flex&lt;/a&gt; package.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;54b86abb-42c4-4f55-b3d4-1157569408d2&#34; class=&#34;&#34;&gt;Here’s how porting happened.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;0687c0d0-74ee-42fc-a518-e989a489ecef&#34; class=&#34;&#34;&gt;Phase zero - picking a name for the package&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;d13dc207-4583-4bc3-a53e-9b6c6ca69b95&#34; class=&#34;&#34;&gt;It was tempting to pick a name that would be some combination of &lt;code&gt;yoga&lt;/code&gt; and &lt;code&gt;go&lt;/code&gt; e.g. &lt;code&gt;goyoga&lt;/code&gt; or &lt;code&gt;yoga-go&lt;/code&gt;.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;a94e3357-0b3a-49c8-967c-03ec15e49b03&#34; class=&#34;&#34;&gt;That would be a bad idea. Including &lt;code&gt;go&lt;/code&gt; in package or repository name is bad (but unfortunately happens too often).&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;03b7c0e5-359b-44ee-81f7-5c362f387738&#34; class=&#34;&#34;&gt;My rules for a good name for a Go library are:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;c8e04814-1266-4d50-81ac-2cf4384ae902&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;it’s short&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;it’s descriptive&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;package name matches repository name&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;div id=&#34;5ae0281b-7e4d-4197-8616-b388635a9be2&#34; class=&#34;&#34;&gt;Therefore &lt;a href=&#34;https://github.com/kjk/flex&#34;&gt;github.com/kjk/flex&lt;/a&gt; was born.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;472bf0ea-e476-460d-b5e9-53b80bf421c8&#34; class=&#34;&#34;&gt;I could use &lt;code&gt;yoga&lt;/code&gt;, to better higlight connection with original project, but it’s not descriptive.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;5a22f5eb-3bf0-4487-8467-f02950b4d972&#34; class=&#34;&#34;&gt;I could use &lt;code&gt;flexbox&lt;/code&gt; to better higlight connection with &lt;a href=&#34;https://www.w3.org/TR/css-flexbox-1/&#34;&gt;CSS flexbox&lt;/a&gt;, but it’s a bit long.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;a5e21569-49a2-4345-a5c3-29ee65171047&#34; class=&#34;&#34;&gt;Phase one - getting Go port to compile&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;3891546b-74ee-480d-b5ae-4c5cbca71e84&#34; class=&#34;&#34;&gt;You can’t port 4.5k lines of code in one sitting so it was important to have a strategy that allows for making incremental progress.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;b1e60036-de3b-419e-8137-6c0cf536743f&#34; class=&#34;&#34;&gt;The important part was keeping track of porting progress. For that I cloned Yoga’s repository. After converting a piece of C code to Go I would delete it from my fork. That way I would keep track of code that still needs to be ported.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;179bf810-ffe9-42c7-a6f7-f60fad00fb40&#34; class=&#34;&#34;&gt;It’s important to get an early boost so I started with &lt;a href=&#34;https://github.com/kjk/flex/commit/4dabacad1ba37403e8b7380b2ccb6c2ed6c8586a&#34;&gt;the simplest parts&lt;/a&gt;: structures, enums.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;4bde7d6e-c854-4c61-b9cf-7e7c17ed542e&#34; class=&#34;&#34;&gt;Porting process was boring but relatively uneventful.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;4f915ee4-4830-4c08-8574-24d5d0ad9317&#34; class=&#34;&#34;&gt;Some porting comments:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;3aa74288-37e2-4243-8c46-c207d04f198d&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;Go and C have reversed order of declarations. Reversing it manually is boring and error prone. There were many repeated declarations that I could do with a simple search-and-replace, e.g &lt;code&gt;YGNodeRef node&lt;/code&gt; =&amp;gt; &lt;code&gt;node *YGNode&lt;/code&gt;&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;search-and-replace &lt;code&gt;-&amp;gt;&lt;/code&gt; to &lt;code&gt;.&lt;/code&gt; as Go uses &lt;code&gt;.&lt;/code&gt; for both cases (something that C++-28 should adopt)&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;Go doesn’t support ternary operator and Yoga’s developers are infatuated with it. That was one part where I had to be extra careful as it was more than mechanical change&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;Go doesn’t support &lt;code&gt;xor&lt;/code&gt; (^) and &lt;code&gt;or&lt;/code&gt; (|) operator on bool. As it turns out, they are not neccessary. &lt;code&gt;x ^ y&lt;/code&gt; is the same as &lt;code&gt;x != y&lt;/code&gt;. &lt;code&gt;a = a | b&lt;/code&gt; can be written as: &lt;code&gt;if !a &amp;amp;&amp;amp; b { a = true }&lt;/code&gt;&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;code&gt;switch&lt;/code&gt; statement needs attention as in C &lt;code&gt;case&lt;/code&gt; falls-through by default&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;Yoga uses &lt;code&gt;float&lt;/code&gt; for coordinates and uses a few functions like &lt;code&gt;fmaxf&lt;/code&gt; etc. Go only implements &lt;code&gt;float64&lt;/code&gt; versions of math functions. Missing functions were easy to write.&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;Yoga code was relatively easy to port partly because of lack of string handling, which is where Go and C differ a lot&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;div id=&#34;ba0a1d1c-f632-41f1-835e-c731ac854961&#34; class=&#34;&#34;&gt;It took me 2 days to port C code to Go, get it compiling and passing some tests. But some tests were failing.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;6e5cdf61-1842-42f3-8620-e61f41f163aa&#34; class=&#34;&#34;&gt;Phase two - fixing bugs&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;2f173225-d6b1-4329-9776-a121e1498b35&#34; class=&#34;&#34;&gt;Yoga has a lot of tests which gave me confidence that when the port is done, I’ll be able to verify its correctness by porting the tests.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;c77eab2a-20fc-40eb-931a-ae760c3d2a29&#34; class=&#34;&#34;&gt;I ported the code, I started porting the tests and they were failing.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;5639ce27-0a9f-47c2-aeed-dabd3a65a347&#34; class=&#34;&#34;&gt;I was in a pickle.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;06c6d847-79ee-44a5-9255-243f7b2f1dfc&#34; class=&#34;&#34;&gt;2 days was enough to mechanically port the code but not even close enough to understand it.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;6ee25a1b-c132-47c9-9dfa-5042e29e032b&#34; class=&#34;&#34;&gt;As far as I can tell you can’t step through tests in the debugger, so I converted a failing test into a test program.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;ca9411f3-d76c-4bb3-89c8-922b9b30f4aa&#34; class=&#34;&#34;&gt;I was pleasently surprised that debugger in Visual Studio Code works decently.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;168369aa-5886-41c2-8ecd-950575518bf4&#34; class=&#34;&#34;&gt;I found one porting mistake by stepping through the code but fixing it didn’t fix test failures.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;90782d2e-6856-4522-94f6-0275e75888cc&#34; class=&#34;&#34;&gt;I spent a bit more time stepping through the code but not knowing what results to expect I decided that it’s not a promising approach.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;8276b851-3987-4a25-ba0a-23e88ea0370d&#34; class=&#34;&#34;&gt;I decided to re-check every line of code.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;9f13d1bd-5cfc-4957-a033-ebab2ad0418c&#34; class=&#34;&#34;&gt;Initially I ported the code in random order so I used re-checking time to also re-arrange the code in the same order as C code. That will help re-porting Yoga improvments in the future.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;9c916e96-8dbc-442e-95e7-723af6507dd9&#34; class=&#34;&#34;&gt;It was predictably boring. It took another day, I found 2 more porting bugs.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;cd42188b-3263-4d7d-b4ac-3ad1f47c5594&#34; class=&#34;&#34;&gt;The tests were still failing and I was a bit stuck.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;1ecdcbb2-f8c6-4cf0-834b-cbd192d360a7&#34; class=&#34;&#34;&gt;I did another pass in the debugger and fortunately an inspiration struck.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;5051739c-2bba-4fa3-a282-33b1ba9ed2c7&#34; class=&#34;&#34;&gt;Yoga uses &lt;code&gt;NaN&lt;/code&gt; value as &lt;code&gt;undefined&lt;/code&gt;. I noticed that &lt;code&gt;fmaxf&lt;/code&gt; function that I wrote returned &lt;code&gt;NaN&lt;/code&gt; when any argument was &lt;code&gt;NaN&lt;/code&gt;.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;e9e4879b-617e-4408-9f57-5c37fbaa2c1f&#34; class=&#34;&#34;&gt;I compared my &lt;code&gt;fmaxf&lt;/code&gt; implementation with C implmentation and it turns out that if one of the arguments is &lt;code&gt;NaN&lt;/code&gt; but the other one isn’t, it should return non-NaN value.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;df362b95-c6cb-48d0-b039-1f7ee65f6503&#34; class=&#34;&#34;&gt;It was easy to fix and that fixed all the tests.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;35646aa9-1b43-40eb-b8eb-a7fde6aa2513&#34; class=&#34;&#34;&gt;Phase three - Go-ifying the API&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;c92e1298-721c-4bc1-a2c9-bf7cf5c25b46&#34; class=&#34;&#34;&gt;The code was working but it was far from idiomatic Go code.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;ed34e7d9-7f13-4857-89ee-57e15d5936f1&#34; class=&#34;&#34;&gt;Next phase was tweaking the API to be more Go-like.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;98326193-19ac-44d6-8ab8-490c03218b89&#34; class=&#34;&#34;&gt;The majority of changes were:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;8e4f0bf3-c9f4-44c2-83ff-8d0c165dd7ea&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;making struct fields public&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;removing &lt;code&gt;YG&lt;/code&gt; prefix as packages obviate the need for that&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;renaming accessor functions to methods (from &lt;code&gt;func NodeFoo(node *Node)&lt;/code&gt; =&amp;gt; &lt;code&gt;func (node *Node) Foo()&lt;/code&gt;&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;div id=&#34;d01f4168-8a99-41ee-8e0a-34a62eb5c17d&#34; class=&#34;&#34;&gt;It’s mechanical, boring process.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;2c9a3fc1-8aa2-4b65-b44f-35b4c8a6aace&#34; class=&#34;&#34;&gt;Some transformations could be done with regex search &amp;amp; replace. The rest with manual labor.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;1ccd6194-d65d-4e96-8a2e-f2e18ea434ce&#34; class=&#34;&#34;&gt;Having lots of tests really helped in making sure that the code is still correct.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;0410f0da-3fcf-44c6-be5f-04e8772d221d&#34; class=&#34;&#34;&gt;The status&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;8384528c-70f7-4eea-b287-a66393e450e1&#34; class=&#34;&#34;&gt;The port is finished. It works and passes all Yoga tests.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;27e5af75-e6a0-43a9-9adc-99842b98f569&#34; class=&#34;&#34;&gt;The API is still a bit awkward by Go standards. Too many methods, not enough direct access to variables.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;dae95e9f-944a-4cef-8165-5e38c2d526a7&#34; class=&#34;&#34;&gt;Partly it’s because I wanted to stay close to C code so that future changes to Yoga can be ported easily.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;3a816359-300b-4db5-beb0-451f4c27477a&#34; class=&#34;&#34;&gt;Partly it’s because of implementation choices and the nature of the problem.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;2c9f5e22-7f73-4304-b454-ec4d30db29da&#34; class=&#34;&#34;&gt;There are many flexbox properties, only some are given for any given element. The rest is given default values, which might be undefined.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;d17997e4-ebb2-4874-ac85-7c0f1c73fdba&#34; class=&#34;&#34;&gt;How to represent that in Go? Ideally, the zero value of the type would represent default value so that a style definition can be constructed as &lt;code&gt;&amp;amp;Style{}&lt;/code&gt; and then we can set the properties that are non-default.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;0b324eb1-13c7-4b50-8af7-44fd059deee0&#34; class=&#34;&#34;&gt;Unfortunately, that doesn’t work well for properties that are numbers because zero might be a valid value.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;5a9ef6ea-3962-4f9e-9a80-a8b0ca27d10d&#34; class=&#34;&#34;&gt;Yoga uses &lt;code&gt;float32&lt;/code&gt; for numbers and &lt;code&gt;NaN&lt;/code&gt; for undefined value of a CSS property.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;83b740e6-ea3d-4d9f-b0a0-2ec7e72f2dfd&#34; class=&#34;&#34;&gt;Another option is used in &lt;code&gt;sql&lt;/code&gt; package for types like &lt;code&gt;sql.NullString&lt;/code&gt; that need to indicate null-abilibility. They are represented as a struct that combines a value and bool field &lt;code&gt;Valid&lt;/code&gt;. Zero value of bool is false, so by default values start as invalid (undefined).&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;2b69f640-5599-46ba-9cbe-9bf0d4a7ab41&#34; class=&#34;&#34;&gt;This isn’t a great API either since it makes setting and reading values more awkward.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;3ff8a575-c1d2-410b-a809-ca006b78ea3a&#34; class=&#34;&#34;&gt;At the end of the day it’s better to have solid, working code with awkward API than not having the code at all. Full flexbox implementation is not trivial to write.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;bb0cce98-80b8-4e0f-b5fa-f38918779c2e&#34; class=&#34;&#34;&gt;Unless someone writes a pure Go implementation designed from scratch with Go idioms in mind, &lt;a href=&#34;https://github.com/kjk/flex&#34;&gt;flex&lt;/a&gt; is the best option.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;d2dbc2d1-8b5d-46ed-9a5e-84cbafb33f6b&#34; class=&#34;&#34;&gt;Notes on automatic translation&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;2355d04f-1559-4a26-b3f5-5cf5ccb16a28&#34; class=&#34;&#34;&gt;Go is so close to C. Wouldn’t it be great if there was a program that could take C code and turn it into Go code?&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;f0a669a8-eb19-4197-99fc-92fb5a9d905d&#34; class=&#34;&#34;&gt;There are few attempts to do that:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;9b7705d8-2b4d-4690-b7a3-0a17f7c40c6b&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://github.com/rsc/c2go&#34;&gt;https://github.com/rsc/c2go&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://github.com/elliotchance/c2go&#34;&gt;https://github.com/elliotchance/c2go&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://github.com/cznic/ccgo&#34;&gt;https://github.com/cznic/ccgo&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;div id=&#34;0a8a7d44-6600-4d38-90d8-7efa39f401a5&#34; class=&#34;&#34;&gt;&lt;a href=&#34;https://github.com/elliotchance/c2go&#34;&gt;elliotchance/c2go&lt;/a&gt; seems to be the most promising.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;310e21ef-2c89-4c08-89fa-ce99278ceb29&#34; class=&#34;&#34;&gt;I didn’t try any of them as neither seems to be usable yet.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;130e3582-88c5-4d13-9c90-68120cab2069&#34; class=&#34;&#34;&gt;It’s an approach worth keeping an eye on for the future but not yet viable today.&#xA;  &lt;/div&gt;&#xA;&lt;/div&gt;</content>
  </entry>
  <entry>
   <title>Using MySQL in Docker for local testing In Go</title>
   <link href="https://blog.kowalczyk.info/article/w4re/using-mysql-in-docker-for-local-testing-in-go.html" rel="alternate"></link>
   <updated>2017-07-23T00:00:00Z</updated>
   <id>tag:blog.kowalczyk.info,2017-07-23:/article/w4re/using-mysql-in-docker-for-local-testing-in-go.html</id>
   <content type="html">&lt;p&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;notion-page&#34; id=&#34;1c001c20-740f-492d-9ca4-79ae9334a67f&#34;&gt;&#xA;  &lt;div id=&#34;d2c9557a-11cd-4633-914a-ca80b4666474&#34; class=&#34;&#34;&gt;Imagine you’re writing a web application that uses MySQL. You &lt;a href=&#34;/article/5/blueprint-for-deploying-web-apps-on-coreos.html&#34;&gt;deploy on Linux&lt;/a&gt; but code and test on Mac.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;c5efdeae-ed37-44aa-958e-3f6c86240101&#34; class=&#34;&#34;&gt;What is a good way to setup MySQL database for local developement and testing on Mac?&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;09140a34-3133-4c49-82d6-672550f97ec2&#34; class=&#34;&#34;&gt;You can install MySQL on Mac using &lt;a href=&#34;https://dev.mysql.com/downloads/mysql/&#34;&gt;official MySQL installer&lt;/a&gt; or via &lt;a href=&#34;https://brew.sh/&#34;&gt;Homebrew&lt;/a&gt; but my favorite way is to use &lt;a href=&#34;https://store.docker.com/editions/community/docker-ce-desktop-mac&#34;&gt;docker&lt;/a&gt;.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;b49c9c4e-7677-421a-aad1-d025e8364954&#34; class=&#34;&#34;&gt;Docker better isolates MySQL from the rest of the system, which has a couple of advantages:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;f8aac4f3-5c8d-4684-9906-2c01ae9966cf&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;it’s easier to install the exact version of MySQL that is running in production as there is a docker image for every version&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;you don’t have to worry that &lt;code&gt;brew upgrade&lt;/code&gt; will upgrade MySQL. Auto-upgrade is desired for most software but not a database. You need to remember to use &lt;code&gt;brew pin&lt;/code&gt; to disable that&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;you can run several different versions of MySQL for different projects&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;since it’s running on Linux, it’s closer to the code running in production&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;div id=&#34;f32567aa-43d7-432a-8d27-608268451b14&#34; class=&#34;&#34;&gt;There is a downside to using Docker:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;18237c60-7772-4856-a836-9d815fd64f4f&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;you have to make sure that the database container is running&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;you need to know the ip address of docker vm running the container&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;div id=&#34;4fa3598c-6ecd-457a-9063-16491dadde28&#34; class=&#34;&#34;&gt;Doing this manually would be annoying and I like to automate so I wrote re-usable bit of Go code to do that.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;b3e1c820-565e-4d6b-bc1b-bc3acd3da4fd&#34; class=&#34;&#34;&gt;It’s just a matter of running docker commands and parsing their outputs but it’s difficult enough to worth sharing the complete solution.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;c612a505-a09d-4ad8-a771-abd5ee8117f6&#34; class=&#34;&#34;&gt;Conceptually, what we do is:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;255b021c-e008-484f-99c3-a56947650ffe&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;run &lt;code&gt;docker ps -a&lt;/code&gt; and parse the output&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;if the container is not running at all, start it with &lt;code&gt;docker run&lt;/code&gt;&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;if the container is stopped, re-start it with &lt;code&gt;docker start&lt;/code&gt;&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;if the container is already running, extract ip address/port from the output&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;div id=&#34;33bcbdbc-2bf7-4957-99c1-fb00cbd5a7ea&#34; class=&#34;&#34;&gt;MySQL database is stored in a local directory mounted by the container. That way data persists even if the container is stopped.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;3a74c405-ed4c-451d-a722-f84e650e9a58&#34; class=&#34;&#34;&gt;The code is re-usable. You can customize it by changing:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;0ab00628-f5ab-4743-a294-77ca1b4e4343&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;the base MySQL container. In my case it’s &lt;a href=&#34;https://hub.docker.com/_/mysql/&#34;&gt;&lt;code&gt;mysql:5.6&lt;/code&gt;&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;where MySQL data is stored&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;name of the container, which should by unique to the project&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;port on which the database is exposed locally. In the container MySQL listens on standard port 3306. It must be exposed locally on a unique port&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;div id=&#34;28585436-0fe4-4340-9884-244bae878c25&#34; class=&#34;&#34;&gt;It can be adapted for other databases, like PostgreSQL.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;3e78878f-0b08-49ff-8974-094ebda25cd0&#34; class=&#34;&#34;&gt;We should only start docker when running locally. In my software I use cmd-line flag &lt;code&gt;-production&lt;/code&gt; to distinguish between running in production and locally.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;1fd212ee-39cf-4e13-b648-828c8d4799e6&#34; class=&#34;&#34;&gt;In production I would use the hard-coded host/ip of MySQL server. Locally I would call &lt;code&gt;startLocalDockerDbMust()&lt;/code&gt; go get them.&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;dockerStatusExited&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;exited&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;dockerStatusRunning&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;running&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// using https://hub.docker.com/_/mysql/&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// to use the latest mysql, use mysql:8&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;dockerImageName&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;mysql:5.6&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// name must be unique across containers runing on this computer&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;dockerContainerName&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;mysql-db-multi&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// where mysql stores databases. Must be on local disk so that&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// database outlives the container&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;dockerDbDir&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;~/data/db-multi&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 3306 is standard MySQL port, I use a unique port to be able&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// to run multiple mysql instances for different projects&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;dockerDbLocalPort&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;7200&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;type&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;containerInfo&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt;       &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;name&lt;/span&gt;     &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;mappings&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;status&lt;/span&gt;   &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;quoteIfNeeded&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;strings&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Contains&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;strings&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Contains&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;\&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;strings&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Replace&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;`&amp;#34;`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;`\&amp;#34;`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;`&amp;#34;`&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;`&amp;#34;`&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;cmdString&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;cmd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;exec&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Cmd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;n&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;cmd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;make&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;([]&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;++&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;quoteIfNeeded&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;cmd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;strings&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Join&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;runCmdWithLogging&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;cmd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;exec&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Cmd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;error&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;cmd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Stdout&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Stdout&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;cmd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Stderr&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Stderr&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;cmd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Run&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;decodeContainerStaus&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;status&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// convert &amp;#34;Exited (0) 2 days ago&amp;#34; into statusExited&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;strings&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;HasPrefix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;status&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;Exited&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;dockerStatusExited&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// convert &amp;#34;Up &amp;lt;time&amp;gt;&amp;#34; into statusRunning&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;strings&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;HasPrefix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;status&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;Up &amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;dockerStatusRunning&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;strings&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;ToLower&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;status&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// given:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 0.0.0.0:7200-&amp;gt;3306/tcp&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// return (0.0.0.0, 7200) or None if doesn&amp;#39;t match&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;decodeIPPortMust&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;mappings&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;parts&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;strings&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Split&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;mappings&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;-&amp;gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;panicIf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;parts&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;invalid mappings string: &amp;#39;%s&amp;#39;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;mappings&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;parts&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;strings&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Split&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;parts&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;:&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;panicIf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;parts&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;invalid mappints string: &amp;#39;%s&amp;#39;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;mappings&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;parts&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;parts&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;dockerContainerInfoMust&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;containerName&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;containerInfo&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;cmd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;exec&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Command&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;docker&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;ps&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;-a&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;--format&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;{{.ID}}|{{.Status}}|{{.Ports}}|{{.Names}}&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;outBytes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;cmd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;CombinedOutput&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;panicIfErr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;cmd.CombinedOutput() for &amp;#39;%s&amp;#39; failed with %s&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;cmdString&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;cmd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;outBytes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// this returns a line like:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// 6c5a934e00fb|Exited (0) 3 months ago|0.0.0.0:7200-&amp;gt;3306/tcp|mysql-db-multi&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;strings&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;TrimSpace&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;lines&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;strings&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Split&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;_&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;line&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;range&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;lines&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;parts&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;strings&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Split&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;line&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;|&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nf&#34;&gt;panicIf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;parts&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;Unexpected output from docker ps:\n%s\n. Expected 4 parts, got %d (%v)\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;line&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;parts&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;parts&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;status&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;mappings&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;parts&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;parts&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;parts&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;parts&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;containerName&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;containerInfo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;       &lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;nx&#34;&gt;status&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;   &lt;span class=&#34;nf&#34;&gt;decodeContainerStaus&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;status&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;nx&#34;&gt;mappings&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;mappings&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;nx&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;     &lt;span class=&#34;nx&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// returns host and port on which database accepts connection&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;startLocalDockerDbMust&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// docker must be running&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;cmd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;exec&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Command&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;docker&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;ps&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;cmd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Run&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;panicIfErr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;docker must be running! Error: %s&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// ensure directory for database files exists&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;dbDir&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;expandTildeInPath&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;dockerDbDir&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;MkdirAll&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;dbDir&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mo&#34;&gt;0755&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;panicIfErr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;failed to create dir &amp;#39;%s&amp;#39;. Error: %s&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;info&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;dockerContainerInfoMust&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;dockerContainerName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;info&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;info&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;status&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;dockerStatusRunning&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;decodeIPPortMust&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;info&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;mappings&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// start or resume container&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;info&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;// start new container&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;        &lt;span class=&#34;nx&#34;&gt;volumeMapping&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;dockerDbDir&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;s:/var/lib/mysql&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;dockerPortMapping&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;dockerDbLocalPort&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;:3306&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;cmd&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;exec&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Command&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;docker&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;run&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;-d&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;--name&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;dockerContainerName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;-p&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;dockerPortMapping&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;-v&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;volumeMapping&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;-e&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;MYSQL_ALLOW_EMPTY_PASSWORD=yes&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;-e&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;MYSQL_INITDB_SKIP_TZINFO=yes&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;dockerImageName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;// start stopped container&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;        &lt;span class=&#34;nx&#34;&gt;cmd&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;exec&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Command&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;docker&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;start&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;info&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;runCmdWithLogging&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;cmd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// wait max 8 seconds for the container to start&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;8&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;++&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;info&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;dockerContainerInfoMust&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;dockerContainerName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;info&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;info&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;status&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;dockerStatusRunning&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;decodeIPPortMust&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;info&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;mappings&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Sleep&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Second&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;panicIf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;docker container &amp;#39;%s&amp;#39; didn&amp;#39;t start in time&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;dockerContainerName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;7091d0ba-d42e-40ea-9507-467e05f92864&#34; class=&#34;&#34;&gt;Code for this chapter: &lt;a href=&#34;https://github.com/kjk/go-cookbook/blob/master/start-mysql-in-docker-go&#34;&gt;https://github.com/kjk/go-cookbook/blob/master/start-mysql-in-docker-go&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;&lt;/div&gt;</content>
  </entry>
  <entry>
   <title>Rotate log files daily in Go</title>
   <link href="https://blog.kowalczyk.info/article/1Ll7/rotate-log-files-daily-in-go.html" rel="alternate"></link>
   <updated>2017-07-20T00:00:00Z</updated>
   <id>tag:blog.kowalczyk.info,2017-07-20:/article/1Ll7/rotate-log-files-daily-in-go.html</id>
   <content type="html">&lt;p&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;notion-page&#34; id=&#34;15b069ab-c983-4dce-b2a4-32d05c46e2e2&#34;&gt;&#xA;  &lt;div id=&#34;1076737e-68d2-42ab-91c4-a137983d386e&#34; class=&#34;&#34;&gt;If you log to a file, it’s a good idea to rotate logs. That way they won’t become too large.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;4d5e7e79-6462-4e9f-90e6-67aa88ed3522&#34; class=&#34;&#34;&gt;After rotation you can backup them up to online storage or delete them.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;24c423d4-d7d7-4b19-923f-17bb58a0040b&#34; class=&#34;&#34;&gt;Rotating daily has a good balance of how often to rotate vs. how large the log file can become.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;5293b470-5405-423d-9891-eb4a0c2467e0&#34; class=&#34;&#34;&gt;You can write logs to stdout and use external program, like &lt;a href=&#34;https://www.cyberciti.biz/faq/how-do-i-rotate-log-files/&#34;&gt;logrotate&lt;/a&gt;, to do the rotation.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;a78e1242-530e-4bfa-b2e3-13eb1996eef9&#34; class=&#34;&#34;&gt;I prefer the simplicity of handling that in my own code and Go’s &lt;code&gt;io.Writer&lt;/code&gt; interface makes it easy to implement a re-usable file ration code.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;eab61b74-e011-487f-a20a-5496abffdeae&#34; class=&#34;&#34;&gt;I did just that in package &lt;a href=&#34;https://github.com/kjk/dailyrotate&#34;&gt;dailyrotate&lt;/a&gt; (&lt;a href=&#34;https://godoc.org/github.com/kjk/dailyrotate&#34;&gt;documentation&lt;/a&gt;).&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;6224baca-33b3-47fe-8a33-c3f49e2e0d97&#34; class=&#34;&#34;&gt;Here’s basic use:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s&#34;&gt;&amp;#34;github.com/kjk/dailyrotate&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;rotatedFile&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;dailyrotate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;File&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// called when file is closed. If didRotate is true, it was closed due to rotation&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// at UTC midnight. Otherwise it was closed due to regular Close()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;onCloseHappened&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;path&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;didRotate&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;bool&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;fmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Printf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;we just closed a file &amp;#39;%s&amp;#39;, didRotate: %v\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;didRotate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;didRotate&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// we block writes until this returns so expensive processing should be done in&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// background goroutine&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;k&#34;&gt;go&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;// here you can implement things like:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;        &lt;span class=&#34;c1&#34;&gt;// - compressing rotated file&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;        &lt;span class=&#34;c1&#34;&gt;// - deleting old files to free up disk space&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;        &lt;span class=&#34;c1&#34;&gt;// - upload rotated file to backblaze/google storage/s3 for backup&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;        &lt;span class=&#34;c1&#34;&gt;// - analyze the content of the file&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;p&#34;&gt;}()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;initRotatedFileMust&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;pathFormat&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;filepath&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Join&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;dir&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;2006-01-02.log&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;w&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;dailyrotate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;NewFile&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;pathFormat&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;onCloseHappened&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;panicIfErr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;logString&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;error&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;_&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;io&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;WriteString&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;rotatedFile&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;shutdownLogging&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;rotatedFile&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Close&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;1a359af5-3ae5-443f-9403-9a64f1223123&#34; class=&#34;&#34;&gt;Here’s a &lt;a href=&#34;https://github.com/kjk/blog/blob/ee30c22379c90642880c8fae33fa3b767a22cb64/visitor_analytics.go#L229&#34;&gt;real-life example&lt;/a&gt; of processing rotated file:&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;4a6fdc61-c99f-4c5a-898c-04ecce8c64c3&#34; class=&#34;&#34;&gt;&lt;code&gt;dailyrotate.File&lt;/code&gt; is &lt;code&gt;io.Writer&lt;/code&gt; and safe to use from multiple goroutines.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;c0a7ea11-f896-403a-a146-39bd7efbb30f&#34; class=&#34;&#34;&gt;In addition to &lt;code&gt;Write(d []byte)&lt;/code&gt; it also implements a &lt;code&gt;Write2(d []byte, flush bool) (string, int64, int, error)&lt;/code&gt;. It has 2 improvements over &lt;code&gt;Write&lt;/code&gt;:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;67e30634-5c88-46f1-b5b9-ae9d8aa44bb1&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;allows to flush in a single call. Flushing after each write is slower but less likely to loose data or corrupt the file when program crashes&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;it returns file path and offset in the file at which the data was written. This is important for building a random-access index to records written to &lt;code&gt;dailyrotate.File&lt;/code&gt;&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;h2 id=&#34;75cebf7e-5a6e-42fa-b798-8e986a490ad3&#34; class=&#34;&#34;&gt;Other real-world uses&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;8138bb09-c0f5-46f9-bab2-cc4f5fef0d32&#34; class=&#34;&#34;&gt;Rotation is not limited to log files. I use it as part of poor-man’s &lt;a href=&#34;https://github.com/kjk/blog/blob/master/visitor_analytics.go&#34;&gt;web server analytics system&lt;/a&gt;.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;1a4fc7b1-0030-4360-b7e3-a5a062745e70&#34; class=&#34;&#34;&gt;I log info about web requests to &lt;code&gt;dailyrotate.File&lt;/code&gt; using my &lt;a href=&#34;/article/vkeR/simple-serialization-for-logging-and-analytics-in-go.html&#34;&gt;siser&lt;/a&gt; simple serialization format.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;05fa67f8-8e67-4504-8847-c21bf888f7e0&#34; class=&#34;&#34;&gt;When a file is rotated, I compress it, upload to &lt;a href=&#34;https://www.backblaze.com/b2/cloud-storage.html&#34;&gt;backblaze&lt;/a&gt; for backup and delete local files older than 7 days to free up space.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;2a0fcd2e-6259-402a-9893-efa7882ac952&#34; class=&#34;&#34;&gt;I also calculate basic daily statistics and e-mail a summary to myself.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;ab4e958e-bb46-4713-aaf6-145a9f5a9d6e&#34; class=&#34;&#34;&gt;I know, I could just use Google Analytics, and I do. My little system has advantages: * it tells me about missing pages (404). That alerts me if break something or if others link incorrectly to my website. Knowing bad links, I can add re-directs for them * by getting daily summaries via e-mail, I keep an eye on things with minimal effort&#xA;  &lt;/div&gt;&#xA;&lt;/div&gt;</content>
  </entry>
  <entry>
   <title>Simple serialization format for logging and analytics in Go</title>
   <link href="https://blog.kowalczyk.info/article/vkeR/simple-serialization-format-for-logging-and-analytics-in-go.html" rel="alternate"></link>
   <updated>2017-07-17T00:00:00Z</updated>
   <id>tag:blog.kowalczyk.info,2017-07-17:/article/vkeR/simple-serialization-format-for-logging-and-analytics-in-go.html</id>
   <content type="html">&lt;p&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;notion-page&#34; id=&#34;8e16d4f1-ebb9-4b4d-a935-3aed30369f5c&#34;&gt;&#xA;  &lt;div id=&#34;05453542-cc94-444a-a927-3cd32a8aa7ae&#34; class=&#34;&#34;&gt;I was looking to save multiple records with somewhat flexible schema to a file. Go has plenty of options but I couldn’t find anything that was just right.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;cbf7cbbb-cf4c-4da8-a0ad-5bc9f3a32c59&#34; class=&#34;&#34;&gt;My desired features were:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;656fbfe8-5f37-475e-86b7-f56e4c471201&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;human-readable so that I can use tools like &lt;code&gt;grep&lt;/code&gt; or &lt;code&gt;tail&lt;/code&gt; to look at the file&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;records are not fixed i.e. can have variable number of fields&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;no need to support nested records&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;allows for simple and efficient implementation&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;div id=&#34;cc657ece-a420-49de-bb8e-5799fb2e6b08&#34; class=&#34;&#34;&gt;Here are some most popular available options and why they don’t exactly fit the bill:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;0679422e-7135-4a9b-af45-6b4b95e1166c&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;csv - efficient but uses fixed records; not very readable if there are many fields on a single line&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;json - not very readable&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;protocol buffers - binary so unreadable; needs up-front scheme&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;div id=&#34;aa8a24f7-cab1-45ac-afa7-727a4dee0339&#34; class=&#34;&#34;&gt;I designed and implemented my own format in package &lt;a href=&#34;https://github.com/kjk/siser&#34;&gt;siser&lt;/a&gt; (which stands for Simple Serialization).&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;8aabbdd2-9087-43b6-973d-c73c3643bf50&#34; class=&#34;&#34;&gt;You’ll not be surprised by how it looks:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;url: http://blog.kowalczyk.info/index.html&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;code: 200&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;large field:+13789&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;this is large data, 13789 bytes in size...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;---&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;1c5c3baf-cacb-4c5d-aef7-19c6d3aca66c&#34; class=&#34;&#34;&gt;It’s a typical key/value serialization with one neat feature: support for large data (e.g. an image or long text).&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;63a30434-217a-486c-b853-71cd4ffd76e9&#34; class=&#34;&#34;&gt;The format is line oriented. Each line is &lt;code&gt;${key}: ${value}\n&lt;/code&gt;.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;993a2532-401b-4536-a6cd-bf1544b457e5&#34; class=&#34;&#34;&gt;If the value is larger than 120 bytes or is not ascii text (with bytes outside of 32-127 range), I serialize it as large value:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;${key}:+${value_length}\n&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;${value}\n&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;29a4d503-7cf6-4363-a4df-233f35d046fa&#34; class=&#34;&#34;&gt;To separate records I use &lt;code&gt;---\n&lt;/code&gt;.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;704df892-095c-4141-a79d-f218bfbca7bb&#34; class=&#34;&#34;&gt;I use this format for 2 main purposes:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;c099fac2-9b58-4d21-819a-ecf0d6a9e23a&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;structured logging to help in debugging&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;logging events for analytics&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;h2 id=&#34;dd0491e0-1e4c-407e-a4b3-2dbaac38ec4f&#34; class=&#34;&#34;&gt;Using the library&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;22841a07-57a1-4f0d-8e2d-d772a2831ea5&#34; class=&#34;&#34;&gt;When used for analytics, each record represents an event. I save the events to a file and later on process the whole file record-by-record and calculate desired statistics.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;b3fb4e58-5bff-4ab4-82a1-ed12859ed72d&#34; class=&#34;&#34;&gt;Here’s how we would log info about HTTP requests:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;logHTTPRequest&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;w&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;io&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Writer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;url&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ipAddr&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;statusCode&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;error&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;r&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;siser&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Record&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// you can append multiple key/value pairs at once&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Append&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;url&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;url&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;ipaddr&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ipAddr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// or assemble with multiple calls&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Append&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;code&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;strconv&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Itoa&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;statusCode&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;/// ... more fields&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;d&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Marshal&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;_&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;w&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Write&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;55898d5b-7bc4-4fdb-8370-9b9113422f6d&#34; class=&#34;&#34;&gt;Here’s a &lt;a href=&#34;https://github.com/kjk/blog/blob/b18317d3dbde1d21745aaea615d952f2c2e158c8/visitor_analytics.go#L309&#34;&gt;full example&lt;/a&gt; of logging HTTP requests.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;84c66327-7b7a-4b34-8c9e-6d7b7bd602d0&#34; class=&#34;&#34;&gt;Let’s say we wrote the data to &lt;code&gt;http_access.log&lt;/code&gt; file. Here’s how we would process the records:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Open&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;http_access.log&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;panicIfErr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;defer&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Close&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;r&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;siser&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;NewReader&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;ReadNext&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;_&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;record&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Record&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;code&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ok&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;code&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// get rest of values and do something with them&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;panicIfErr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;6dffec4d-474c-44e3-ad2f-62ff78315d3b&#34; class=&#34;&#34;&gt;Here’s a &lt;a href=&#34;https://github.com/kjk/blog/blob/b18317d3dbde1d21745aaea615d952f2c2e158c8/visitor_analytics.go#L108&#34;&gt;full example&lt;/a&gt; of calculating basic daily statistics on HTTP requests (most frequently visited pages, most frequent 404s, most frequent referrers).&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;3af560c7-43e2-42b1-a1c9-9cd87ded093c&#34; class=&#34;&#34;&gt;Annoyances&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;3ae82ecf-4a5d-451b-8bca-07b8b70ea001&#34; class=&#34;&#34;&gt;The library doesn’t offer marshaling directly to/from structs. Could be added with a bit of reflection.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;495bec76-3067-4300-aee8-942cf96df56a&#34; class=&#34;&#34;&gt;It doesn’t directly support non-string types like &lt;code&gt;int&lt;/code&gt; or &lt;code&gt;time.Time&lt;/code&gt;. You have to convert them to/from string yourself.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;125d9ec6-8b46-4901-ac01-5b5185684cea&#34; class=&#34;&#34;&gt;Implementation notes&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;f3dc4a90-04de-43af-af73-cddfe5b1e0d9&#34; class=&#34;&#34;&gt;Some implementation decisions were made with performance in mind.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;b8ead6a7-3a8f-4fc0-8593-45518939e8a8&#34; class=&#34;&#34;&gt;Given key/value nature of the record, an easy choice would be to use &lt;code&gt;map[string]string&lt;/code&gt; as argument to encode/decode functions.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;3a674b4e-cf39-4a51-8fb3-caaaae0ecae6&#34; class=&#34;&#34;&gt;However &lt;code&gt;[]string&lt;/code&gt; is more efficient than a &lt;code&gt;map&lt;/code&gt;. A slice can be reused across multiple records. We can clear by re-slicing as empty slice and reusing underlying array. A map would require allocating a new instance for each record, which would create a lot of work for garbage collector.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;3390d2a3-6923-4575-b904-86c5c0726a99&#34; class=&#34;&#34;&gt;When serializing, you need to use &lt;code&gt;Reset&lt;/code&gt; method to get the benefit of efficient re-use of the record.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;69a233ce-7f9b-497f-adfb-042d492f4293&#34; class=&#34;&#34;&gt;When reading and deserializing records, &lt;code&gt;siser.Reader&lt;/code&gt; uses this optimization internally.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;a3835cc1-dd54-448b-991c-943be62897e5&#34; class=&#34;&#34;&gt;The format avoids the need for escaping keys and values, which helps in making encoding/decoding fast.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;03b6a658-9e75-4c8d-b2de-ab20234fe8f8&#34; class=&#34;&#34;&gt;How does that play out in real life? I wrote a &lt;a href=&#34;https://github.com/kjk/siser/blob/6ffab5b5c5f0fa60f5f4b203a776af7930d2850b/serialize_test.go#L171&#34;&gt;benchmark&lt;/a&gt; comparing siser vs. &lt;code&gt;json.Marshal&lt;/code&gt;. It’s about 30% faster:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ go test -bench=.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;BenchmarkSiserMarshal-8          1000000          2108 ns/op&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;BenchmarkJSONMarshal-8            500000          3181 ns/op&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;BenchmarkSiserUnmarshal-8         500000          2908 ns/op&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;BenchmarkJSONUnmarshal-8          200000          7482 ns/op&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;6b15e52a-2ffb-4f2e-ad84-8b5d994b3536&#34; class=&#34;&#34;&gt;The format is binary-safe and works for serializing large values e.g. you can serialize png image&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;795608ca-20bf-48dc-ae8c-3391890797f7&#34; class=&#34;&#34;&gt;It’s also very easy to implement in any language.&#xA;  &lt;/div&gt;&#xA;&lt;/div&gt;</content>
  </entry>
  <entry>
   <title>Embedding build number in Go executable</title>
   <link href="https://blog.kowalczyk.info/article/vEja/embedding-build-number-in-go-executable.html" rel="alternate"></link>
   <updated>2017-07-13T00:00:00Z</updated>
   <id>tag:blog.kowalczyk.info,2017-07-13:/article/vEja/embedding-build-number-in-go-executable.html</id>
   <content type="html">&lt;p&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;notion-page&#34; id=&#34;39d17339-1739-4a9d-bf99-ffafa20c2a96&#34;&gt;&#xA;  &lt;div id=&#34;b33564e7-dfe0-4d50-916d-d7e8e0bc8f33&#34; class=&#34;&#34;&gt;So you’ve deployed your web application to production and it’s running on a server far, far away.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;9f4ae5f9-9e2f-46c3-971c-b201d0e32c4a&#34; class=&#34;&#34;&gt;When debugging problems it’s good to know what version of the code is running.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;08af8a14-96bf-4b3e-bdb1-4d09c11c12ad&#34; class=&#34;&#34;&gt;If you’re using git, that would be sha1 of the revision used to build the program.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;20ce03c5-689e-474d-bc2f-87ccd713356e&#34; class=&#34;&#34;&gt;We can embed that version in the executable during build thanks Go linker’s &lt;code&gt;-X&lt;/code&gt; option which allows to change any variable in the program.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;d69c8a07-84b4-4de0-aceb-48ebbcefaf05&#34; class=&#34;&#34;&gt;In our Go program we would have:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;package&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;main&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;sha1ver&lt;/span&gt;   &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// sha1 revision used to build the program&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;buildTime&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// when the executable was built&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;f1c163d5-98ac-4c80-87bf-81c66684398a&#34; class=&#34;&#34;&gt;We can set that variable to sha1 of the git revision in our build script &lt;code&gt;build.sh&lt;/code&gt;:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;#&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;bin&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;bash&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;#&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;notice&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;how&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;we&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;avoid&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;spaces&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;$&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;now&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;to&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;avoid&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;quotation&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;hell&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;go&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;build&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;command&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;now&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;$&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;date&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;%&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Y&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-%&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;m&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-%&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;d_&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;%&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;go&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;build&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ldflags&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;-X main.sha1ver=`git rev-parse HEAD` -X main.buildTime=$now&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;264ddced-b032-4a71-9815-7badf7206974&#34; class=&#34;&#34;&gt;Full example: &lt;a href=&#34;https://github.com/kjk/go-cookbook/blob/master/embed-build-number/build.sh&#34;&gt;embed-build-number/build.sh&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;567e43cb-28ad-42e4-9682-502181abcb36&#34; class=&#34;&#34;&gt;On Windows we would write powershell script &lt;code&gt;build.ps1&lt;/code&gt;:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;#&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;notice&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;how&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;we&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;avoid&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;spaces&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;$&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;now&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;to&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;avoid&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;quotation&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;hell&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;go&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;build&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;command&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;$&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;now&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Get&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Date&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;UFormat&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;%Y-%m-%d_%T&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;$&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;sha1&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;git&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;rev&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;parse&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;HEAD&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;).&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Trim&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;go&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;build&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ldflags&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;-X main.sha1ver=$sha1 -X main.buildTime=$now&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;4eaf2a38-eae7-4a8a-92c4-240bdec406ea&#34; class=&#34;&#34;&gt;Full example: &lt;a href=&#34;https://github.com/kjk/go-cookbook/blob/master/embed-build-number/build.ps1&#34;&gt;embed-build-number/build.ps1&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;892a570a-b983-43b4-8fb7-8de9809ebc5c&#34; class=&#34;&#34;&gt;Let’s deconstruct:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;15cba0fc-ac2e-48c5-a119-16a375451612&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;&lt;code&gt;git rev-parse HEAD&lt;/code&gt; returns sha1 of the current revision, e.g. &lt;code&gt;e5ce06c1f604efb1de91d515d5de865e7e164d59&lt;/code&gt;&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;code&gt;-X main.sha1ver=${foo}&lt;/code&gt; tells Go linker to set variable &lt;code&gt;sha1ver&lt;/code&gt; in package &lt;code&gt;main&lt;/code&gt; to &lt;code&gt;${foo}&lt;/code&gt;&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;code&gt;-ldflags &amp;quot;${flags}&amp;quot;&lt;/code&gt; tells Go build tool to pass &lt;code&gt;${flags}&lt;/code&gt; to go linker&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;div id=&#34;c02cbad1-ccac-4f61-acce-d6ebd90398bc&#34; class=&#34;&#34;&gt;We also need an easy way to see that version. We can add &lt;code&gt;-version&lt;/code&gt; cmd-line flag to &lt;a href=&#34;https://github.com/kjk/go-cookbook/blob/master/embed-build-number/main.go#L26&#34;&gt;print it out&lt;/a&gt;:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;flgVersion&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;bool&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;parseCmdLineFlags&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;flag&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;BoolVar&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;flgVersion&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;version&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;if true, print version and exit&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;flag&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Parse&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;flgVersion&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;fmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Printf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;Build on %s from sha1 %s\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;buildTime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;sha1ver&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Exit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;1f95f8f1-094e-4e98-9b97-593274d92df5&#34; class=&#34;&#34;&gt;If this is a web application, we can additionally add a debug page that would show the version. I often do it &lt;a href=&#34;https://github.com/kjk/go-cookbook/blob/master/embed-build-number/main.go#L43&#34;&gt;like that&lt;/a&gt;:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;servePlainText&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;w&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;http&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ResponseWriter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;w&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Header&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Set&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;Content-Type&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;text/plain&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;w&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Header&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Set&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;Content-Length&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;strconv&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Itoa&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)))&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;w&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;WriteHeader&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;http&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;StatusOK&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;w&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Write&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;([]&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;byte&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// /app/debug&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;handleDebug&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;w&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;http&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ResponseWriter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;r&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;http&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Request&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;fmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Sprintf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;url: %s %s&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Method&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;RequestURI&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;append&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;Headers:&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;k&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;v&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;range&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Header&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;v&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nx&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;append&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;k&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;v&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;fmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Sprintf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;  %s: %v&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;k&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;v&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nx&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;append&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nx&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;append&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;  &amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;k&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;:&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;_&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;v2&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;range&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;v&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;nx&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;append&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;    &amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;v2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;append&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;append&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;fmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Sprintf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;ver: https://github.com/kjk/go-cookbook/commit/%s&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;sha1ver&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;append&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;fmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Sprintf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;built on: %s&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;buildTime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;strings&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Join&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;servePlainText&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;w&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;makeHTTPServer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;http&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Server&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;mux&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;http&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ServeMux&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;mux&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;HandleFunc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;/app/debug&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;handleDebug&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;http&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Server&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;ReadTimeout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;  &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Second&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;WriteTimeout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Second&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;IdleTimeout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;  &lt;span class=&#34;mi&#34;&gt;120&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Second&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;Handler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;      &lt;span class=&#34;nx&#34;&gt;mux&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;startHTTPServer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;httpAddr&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;127.0.0.1:4040&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;httpSrv&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;makeHTTPServer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;httpSrv&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Addr&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;httpAddr&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;fmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Printf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;Visit http://%s/app/debug\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;httpAddr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;httpSrv&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;ListenAndServe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Fatalf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;httpSrv.ListendAndServe() failed with %s\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;f2624055-e427-48c5-b8e6-b18dfdd88b2e&#34; class=&#34;&#34;&gt;In addition to printing version of the code I also show HTTP headers. During debugging, the more information, the better.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;112805bf-cf9c-4c00-ab26-a21bca7c00e5&#34; class=&#34;&#34;&gt;Code for this chapter: &lt;a href=&#34;https://github.com/kjk/go-cookbook/tree/master/embed-build-number&#34;&gt;https://github.com/kjk/go-cookbook/tree/master/embed-build-number&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;&lt;/div&gt;</content>
  </entry>
  <entry>
   <title>3 ways to iterate in Go</title>
   <link href="https://blog.kowalczyk.info/article/1Bkr/3-ways-to-iterate-in-go.html" rel="alternate"></link>
   <updated>2017-07-09T00:00:00Z</updated>
   <id>tag:blog.kowalczyk.info,2017-07-09:/article/1Bkr/3-ways-to-iterate-in-go.html</id>
   <content type="html">&lt;p&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;notion-page&#34; id=&#34;da74af7a-3096-4880-8249-be7a79fc8156&#34;&gt;&#xA;  &lt;div id=&#34;03216ef2-f405-49e8-8d1b-156dea2e51ab&#34; class=&#34;&#34;&gt;Iteration is a frequent need, be it iterating over lines of a file, results or of &lt;code&gt;SELECT&lt;/code&gt; SQL query or files in a directory.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;e0949943-34d5-4527-aaad-90186cdfc10e&#34; class=&#34;&#34;&gt;There are 3 common iteration patterns in Go programs: * callbacks * an iterator object with &lt;code&gt;Next()&lt;/code&gt; method * channels&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;f3763724-66a1-453d-97f9-bc6eba7fcb05&#34; class=&#34;&#34;&gt;Iteration mixed with processing&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;d5c8cb8b-172f-4c00-b3ee-128778548074&#34; class=&#34;&#34;&gt;Before discussing different ways of designing iteration API, let’s see how we would iterate without encapsulating iteration logic.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;0f10e565-7d86-47c7-bdca-db4fddee49ae&#34; class=&#34;&#34;&gt;Our example is iterating over even numbers, starting with 2 up to a given &lt;code&gt;max&lt;/code&gt; number (inclusive).&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;d422fc8a-84ce-4ee5-91e9-328a00633af7&#34; class=&#34;&#34;&gt;To show handling of errors we’ll consider &lt;code&gt;max&lt;/code&gt; less than 0 to be invalid.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;10ac40d9-ceee-4665-9931-fe5a856f574b&#34; class=&#34;&#34;&gt;This is intentionally the simplest possible iterator so that we can focus on the implementation of the iterator API and not generating the values to iterate over.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;da9ea584-d9ef-49c7-abd0-27e427d944a7&#34; class=&#34;&#34;&gt;Our processing is simple as well: we print the number.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;b5ce4e98-8597-4570-9754-64a2f1635dd1&#34; class=&#34;&#34;&gt;Here’s an example of iteration intertwined with processing.&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;printEvenNumbers&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;max&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;max&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Fatalf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;&amp;#39;max&amp;#39; is %d, should be &amp;gt;= 0&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;max&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;max&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;fmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Printf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;%d\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;b753eb2b-154e-491b-ba85-61349ab99d93&#34; class=&#34;&#34;&gt;Full example: &lt;a href=&#34;https://github.com/kjk/go-cookbook/blob/master/3-ways-to-iterate/inlined.go&#34;&gt;3-ways-to-iterate/inlined.go&lt;/a&gt;.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;893b7411-952e-4296-b6c9-4ae1b061985e&#34; class=&#34;&#34;&gt;This is fine if iteration logic is simple. If our iteration was complex, like iterating over lines in a file, every time we needed to do different processing of the lines, we would end up copy &amp;amp; pasteing a lot of code.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;d0afe73a-90ae-47aa-aeb3-ba642851fc3b&#34; class=&#34;&#34;&gt;For easy reuse we want to encapsulate complex iteration logic and provide simple API to callers.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;0b27f433-13eb-4270-bfea-83114387e927&#34; class=&#34;&#34;&gt;Iterating via callback&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;2d94790d-6012-4f52-be52-93ba7cbf2964&#34; class=&#34;&#34;&gt;The caller provides callback function to be called with each value.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;5f12114f-06a3-4cac-8699-11cc10f84d03&#34; class=&#34;&#34;&gt;Client side of iteration:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;printEvenNumbers&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;max&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;iterateEvenNumbers&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;max&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;n&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;error&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;fmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Printf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;%d\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Fatalf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;error: %s\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;cfab79ca-4172-42a1-93b3-01302e898689&#34; class=&#34;&#34;&gt;We need a way to stop iteration from within the callback which is why the callback returns an &lt;code&gt;error&lt;/code&gt;. Returning non-nil &lt;code&gt;error&lt;/code&gt; from callback stops iteration.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;a957be6e-c3c7-40ac-bf52-7fa42a0c84b3&#34; class=&#34;&#34;&gt;Implementation of iterator:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;iterateEvenNumbers&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;max&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;cb&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;n&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;error&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;max&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;fmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Errorf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;&amp;#39;max&amp;#39; is %d, must be &amp;gt;= 0&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;max&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;max&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;cb&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;dc03a541-d477-4b09-9846-d0a4ab4a3485&#34; class=&#34;&#34;&gt;Full example: &lt;a href=&#34;https://github.com/kjk/go-cookbook/blob/master/3-ways-to-iterate/callback.go&#34;&gt;3-ways-to-iterate/callback.go&lt;/a&gt;.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;7258c037-37ad-4355-a28f-557334e062c0&#34; class=&#34;&#34;&gt;This pattern is used in &lt;a href=&#34;https://golang.org/pkg/path/filepath/#Walk&#34;&gt;&lt;code&gt;filepath.Walk&lt;/code&gt;&lt;/a&gt; API in standard library.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;46aa9e0c-2226-4f89-bfa3-28d4720d63dc&#34; class=&#34;&#34;&gt;Iterating with &lt;code&gt;Next()&lt;/code&gt;&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;e3bfd44f-e175-4c38-9933-03e258c649fc&#34; class=&#34;&#34;&gt;Another pattern is to implement iterator struct with 3 functions: * &lt;code&gt;Next()&lt;/code&gt; advances iterator to next value. It returns false to indicate end of iteration (which can be due to error) * &lt;code&gt;Value()&lt;/code&gt; to get the current value of the iterator. The name depends on the kind of value we retrieve * optional &lt;code&gt;Err()&lt;/code&gt; function which returns iteration error&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;185bc3d8-0d0b-4835-97f9-9c40e32891d7&#34; class=&#34;&#34;&gt;Client code:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;printEvenNumbers&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;max&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;iter&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;NewEvenNumberIterator&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;max&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;iter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Next&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;fmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Printf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;n: %d\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;iter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;iter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Fatalf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;error: %s\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;iter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;ba751519-e980-467f-8183-f94fab325ae0&#34; class=&#34;&#34;&gt;Notice how &lt;code&gt;Next()&lt;/code&gt; fits nicely with &lt;code&gt;for&lt;/code&gt; loop thanks to returning bool and indicating end of iteration with &lt;code&gt;false&lt;/code&gt;.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;9e80f68a-3ffb-4eaa-a266-c9b581807e85&#34; class=&#34;&#34;&gt;Unfortunately, the nice API on the caller side requires complicated implementation of the iterator.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;985cb1dd-6f3e-44e4-97e5-d8f0f16ea0a6&#34; class=&#34;&#34;&gt;We need to carry state across &lt;code&gt;Next()&lt;/code&gt; calls and remember iteration errors:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// EvenNumberIterator generates even numbers&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;type&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;EvenNumberIterator&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;max&lt;/span&gt;       &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;currValue&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;       &lt;span class=&#34;kt&#34;&gt;error&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// NewEvenNumberIterator creates new number iterator&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;NewEvenNumberIterator&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;max&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;EvenNumberIterator&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;error&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;max&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;fmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Errorf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;&amp;#39;max&amp;#39; is %d, should be &amp;gt;= 0&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;max&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;EvenNumberIterator&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;max&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;       &lt;span class=&#34;nx&#34;&gt;max&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;currValue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;       &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Next advances to next even number. Returns false on end of iteration.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;EvenNumberIterator&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;Next&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;bool&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;currValue&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;currValue&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;max&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Value returns current even number&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;EvenNumberIterator&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;Value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;currValue&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;max&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nb&#34;&gt;panic&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;Value is not valid after iterator finished&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;currValue&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Err returns iteration error.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;EvenNumberIterator&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;Err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;error&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;1d84b14b-903c-4226-976f-20d8b34ab503&#34; class=&#34;&#34;&gt;Full example: &lt;a href=&#34;https://github.com/kjk/go-cookbook/blob/master/3-ways-to-iterate/next.go&#34;&gt;3-ways-to-iterate/next.go&lt;/a&gt;.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;bfe3f127-69ac-4423-9d78-fc63b0aab5c8&#34; class=&#34;&#34;&gt;Notes:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;700fec6a-6c6a-4e63-895d-a4a5ee9398d0&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;this method requires the largest amount of boilerplate&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;code&gt;Next()&lt;/code&gt; should return &lt;code&gt;false&lt;/code&gt; if there was an error&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;code&gt;Value()&lt;/code&gt; panics if accessed after iteration has finished&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;div id=&#34;56e7a791-8826-4a70-b71a-c1f47ca445ab&#34; class=&#34;&#34;&gt;This pattern is used in standard library:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;490c42a3-acc3-46c9-9a4c-a83e814a0a4c&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://golang.org/pkg/database/sql/#Rows.Next&#34;&gt;&lt;code&gt;Rows.Next&lt;/code&gt;&lt;/a&gt; to iterate over results of SQL &lt;code&gt;SELECT&lt;/code&gt; statement&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://golang.org/pkg/go/scanner/#Scanner.Scan&#34;&gt;&lt;code&gt;Scanner.Scan&lt;/code&gt;&lt;/a&gt; to iterate over text&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://golang.org/pkg/encoding/xml/#Decoder.Token&#34;&gt;&lt;code&gt;Decoder.Token&lt;/code&gt;&lt;/a&gt; for XML parsing&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://golang.org/pkg/encoding/csv/#Reader.Read&#34;&gt;&lt;code&gt;Reader.Read&lt;/code&gt;&lt;/a&gt; in CSV reader&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;div id=&#34;142f1dd4-819c-4a84-925c-53f9eb9f3e32&#34; class=&#34;&#34;&gt;Some of those iterators combine &lt;code&gt;Next()&lt;/code&gt; and &lt;code&gt;Value()&lt;/code&gt; into a single function returning multiple values.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;581888bf-69e8-402e-b9af-4ac1be293bda&#34; class=&#34;&#34;&gt;Iterating with a channel&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;bf58432a-1f5b-42a8-bb28-29ebe5647873&#34; class=&#34;&#34;&gt;Channels and goroutines are Go’s banner features and can be used as iterators.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;f37cc17c-f6d4-476e-aa8a-f60e67700b92&#34; class=&#34;&#34;&gt;Caller side:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;printEvenNumbers&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;max&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;val&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;range&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;generateEvenNumbers&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;max&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;val&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Fatalf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;Error: %s\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;val&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;fmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Printf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;%d\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;val&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;fa6ba0c2-2845-475a-b8c4-7dd277b662cd&#34; class=&#34;&#34;&gt;&lt;code&gt;generateEvenNumbers()&lt;/code&gt; returns a channel which will be closed to indicate end of iteration. Closing the channel ends &lt;code&gt;for&lt;/code&gt; loop.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;7f620bc8-fd48-4dd7-87b8-89396f042174&#34; class=&#34;&#34;&gt;If there is no possibility of failing we can send just values over the channel.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;c74c6ed1-065f-4d2d-85ad-ad3ea7428909&#34; class=&#34;&#34;&gt;In our case a failure is possiblity, so we have to send a struct that packages the value and possible error:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;type&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;IntWithError&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;Int&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;Err&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;error&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;49d200e0-7ba8-470f-9d3e-3764f3798562&#34; class=&#34;&#34;&gt;Generator side:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;func generateEvenNumbers(max int) chan IntWithError {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    ch := make(chan IntWithError)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    go func() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        defer close(ch)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        if max &amp;lt; 0 {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            ch &amp;lt;- IntWithError{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                Err: fmt.Errorf(&amp;#34;&amp;#39;max&amp;#39; is %d and should be &amp;gt;= 0&amp;#34;, max),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            return&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        for i := 2; i &amp;lt;= max; i += 2 {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            ch &amp;lt;- IntWithError{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                Int: i,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    }()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    return ch&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;1988edad-ecb7-4ffd-b5f8-036d3d2066e0&#34; class=&#34;&#34;&gt;Full example: &lt;a href=&#34;https://github.com/kjk/go-cookbook/blob/master/3-ways-to-iterate/channel.go&#34;&gt;3-ways-to-iterate/channel.go&lt;/a&gt;.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;25d6f11b-39e5-4e48-a487-ba7d2e957f4f&#34; class=&#34;&#34;&gt;We could use buffered channel, e.g.: &lt;code&gt;ch := make(chan IntWithError, 128)&lt;/code&gt;. That would speed up things if both generation and processing are time consuming by parallelizing those 2 processes.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;a2542ba1-07be-47f3-a116-654c0e100083&#34; class=&#34;&#34;&gt;Adding cancellation to channel-based iterator&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;5515be7a-a23c-4198-8a53-3e873ddc943c&#34; class=&#34;&#34;&gt;In the above example the client doesn’t have a way to stop the channel-based iterator.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;bfc94590-0a31-42c4-8b58-4993da13e106&#34; class=&#34;&#34;&gt;If it just stops processing values from the channel before it’s closed, the generator goroutine will be forever blocked trying to send on a channel no-one is reading from. The goroutine will leak.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;76cf46dc-962f-428c-9b49-c0d0baf90c5d&#34; class=&#34;&#34;&gt;We can add ability to stop a channel iterator by using &lt;code&gt;context&lt;/code&gt; created with &lt;code&gt;context.WithCancel&lt;/code&gt;.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;81e5afa5-0241-4a14-bb4a-a76af183bb3b&#34; class=&#34;&#34;&gt;Here’s slightly modified generator function:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;generateEvenNumbers&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ctx&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;context&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Context&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;max&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;chan&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;IntWithError&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;ch&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;make&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;chan&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;IntWithError&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;go&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;defer&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;close&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ch&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;max&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nx&#34;&gt;ch&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;IntWithError&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;nx&#34;&gt;Err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;fmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Errorf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;&amp;#39;max&amp;#39; is %d and should be &amp;gt;= 0&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;max&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;max&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ctx&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;c1&#34;&gt;// if context was cancelled, we stop early&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;                &lt;span class=&#34;k&#34;&gt;select&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;-&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Done&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;():&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;k&#34;&gt;default&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nx&#34;&gt;ch&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;IntWithError&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;nx&#34;&gt;Int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ch&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;5e70bdaa-2544-490b-b7c7-e6461ba4bbde&#34; class=&#34;&#34;&gt;Full example: &lt;a href=&#34;https://github.com/kjk/go-cookbook/blob/master/3-ways-to-iterate/channel-cancellable.go&#34;&gt;3-ways-to-iterate/channel-cancellable.go&lt;/a&gt;.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;75d153f0-2356-482a-8ea1-5b2ea4899865&#34; class=&#34;&#34;&gt;Here’s a user of the above code that stops the generator a bit early:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;printEvenNumbersCancellable&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;max&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;stopAt&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;cancel&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;context&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;WithCancel&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;context&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Background&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;defer&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;cancel&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;ch&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;generateEvenNumbers&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;max&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;val&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;range&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ch&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;val&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Fatalf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;Error: %s\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;val&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;val&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Int&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;stopAt&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nf&#34;&gt;cancel&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;c1&#34;&gt;// notice we keep going in order to drain the channel&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;            &lt;span class=&#34;k&#34;&gt;continue&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;// process the value&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;        &lt;span class=&#34;nx&#34;&gt;fmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Printf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;%d\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;val&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;e6361372-45cb-4903-977b-e6e756f598c2&#34; class=&#34;&#34;&gt;Important to note:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;9a201da9-1a11-4ad9-a5a8-ab3256ed72fa&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;we must call &lt;code&gt;cancel()&lt;/code&gt; on the context we get from &lt;code&gt;context.WithCancel()&lt;/code&gt; or else it’ll leak&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;it’s safe to call &lt;code&gt;cancel()&lt;/code&gt; multiple times&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;code&gt;cancel()&lt;/code&gt; asks for the generator to stop but cannot guarantee it. After cancelling we’ll get more values queued on the channel&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;div id=&#34;c1de6baf-c9fe-4370-9c53-5f9c86593603&#34; class=&#34;&#34;&gt;It’s important to fully drain the channel or else we’ll leak the goroutine that is trying to write to it. In this example we skip processing after reaching stop value.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;e96afea4-227c-438c-9e81-b600adfc78ed&#34; class=&#34;&#34;&gt;Alternatively, we could break the processing loop and then have a loop just to drain the channel:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;_&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;range&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ch&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;h2 id=&#34;a803f06c-8d1a-4caf-a05e-32728612cfe5&#34; class=&#34;&#34;&gt;Which way is the best?&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;7ec4cfb0-3f11-494f-8a63-fcff70955cc9&#34; class=&#34;&#34;&gt;The one that best fits your scenario.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;747a026f-b3a8-49c5-a705-f7bed851dcb5&#34; class=&#34;&#34;&gt;The callback pattern makes for a simple implementation of the iterator but callbacks in Go have akward syntax.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;c09c256b-ba59-4ac6-9e59-d14c6851d326&#34; class=&#34;&#34;&gt;Using &lt;code&gt;Next()&lt;/code&gt; is the hardest to implement but presents nice interface to the caller. It’s most commonly used in Go standard library for complex iterators.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;e92ffd64-e9b5-4b97-916a-ee09e473a311&#34; class=&#34;&#34;&gt;Channel-based iterator is easy to implent and use by the caller but most expensive. Only in exceptional circumstances the cost should be of concern.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;aa592d14-9e90-424d-b601-a84b0dcb1249&#34; class=&#34;&#34;&gt;It’s also the only one that is concurrent by nature.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;382191d9-f927-402d-bb91-cda0ebb80467&#34; class=&#34;&#34;&gt;At the same time, channels are heavy machinery. Don’t over-use them, don’t use channels for things that can be done without them.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;f0b689ae-c613-4bad-956c-bb1a7439624e&#34; class=&#34;&#34;&gt;Channels are meant to serve as a coordination mechanism between goroutines. If you don’t need to launch a goroutine, then you probably don’t need to use channels.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;116b7b6b-ff9c-471b-a841-177fca9c4fa4&#34; class=&#34;&#34;&gt;Code for this chapter: &lt;a href=&#34;https://github.com/kjk/go-cookbook/tree/master/3-ways-to-iterate&#34;&gt;https://github.com/kjk/go-cookbook/tree/master/3-ways-to-iterate&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;&lt;/div&gt;</content>
  </entry>
  <entry>
   <title>HTTPS for free in Go, with little help of Let&#39;s Encrypt</title>
   <link href="https://blog.kowalczyk.info/article/Jl3G/https-for-free-in-go-with-little-help-of-lets-encrypt.html" rel="alternate"></link>
   <updated>2017-07-02T00:00:00Z</updated>
   <id>tag:blog.kowalczyk.info,2017-07-02:/article/Jl3G/https-for-free-in-go-with-little-help-of-lets-encrypt.html</id>
   <content type="html">&lt;p&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;notion-page&#34; id=&#34;e1f2db06-a96a-413a-a10e-e0723f6802ab&#34;&gt;&#xA;  &lt;h2 id=&#34;dc390be6-a75f-421f-9e81-a0bcae3b0bd6&#34; class=&#34;&#34;&gt;Why HTTPS?&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;b6bf8229-2c7a-4944-96ff-5b93cc42526e&#34; class=&#34;&#34;&gt;Having HTTPS for your website is important:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;4fec6d48-fc48-4bfd-80b1-4e95db376de6&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;HTTPS encrypts the traffic between browser and server. Passwords of your users are protected from traffic sniffing on naughty intermediary servers or miscreants sniffing wifi packets in a cafe.&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;new HTTP/2 protocol is faster than HTTP/1.1 but only works over HTTPS.&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;if you care about SEO, Google ranks HTTPS websites higher than HTTP ones.&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;browser vendors will increasingly &lt;a href=&#34;https://www.troyhunt.com/life-is-about-to-get-harder-for-websites-without-https/&#34;&gt;scare users of HTTP websites&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;h2 id=&#34;2b07e712-239e-4e8c-ada7-50edf8ec9ce2&#34; class=&#34;&#34;&gt;Using someone else to provide HTTPS&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;14293580-54d7-4fbb-b093-b7b7c07f3e38&#34; class=&#34;&#34;&gt;Before we learn how to support HTTPS directly in your Go web server, let’s talk about simpler options.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;f785ad1b-8283-4981-8fd3-707c45873792&#34; class=&#34;&#34;&gt;You can use a third-party service like &lt;a href=&#34;https://www.cloudflare.com/&#34;&gt;Cloudflare&lt;/a&gt;.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;208032d3-3c98-44c4-bf48-d7177eb73395&#34; class=&#34;&#34;&gt;Their free plan offers acting as HTTPS proxy to your HTTP-only website.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;ecc9076b-f4f4-4b16-b09b-1c3a1c488d63&#34; class=&#34;&#34;&gt;To use Cloudflare: * configure your domain to use CloudFlare’s DNS servers * in CloudFlare’s DNS settings point the domain to your server and set Status to “DNS and HTTP proxy (CDN)” * in CloudFlare’s Crypto settings, set SSL to “Flexible” (browser talks to CloudFlare via HTTPS, CloudFlare talks to * configure CloudFlare’s HTTPS proxy in their web interface by providing IP address of your server. * for good measure, also enable “Always use HTTPS”&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;bac4b314-70b2-4489-9c21-ba2145c1ad04&#34; class=&#34;&#34;&gt;Browser talks to CloudFlare, which takes care of provisioning SSL certificate and proxies the traffic to your server. This might be slower due to additional traffic or faster due to CloudFlare servers being faster than yours (being faster is their business).&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;b35467e7-7ba6-40a9-ae27-d00a722144cb&#34; class=&#34;&#34;&gt;AWS, Google Cloud and some other hosting providers also provide free HTTPS for servers hosted on their infrastructures.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;fadd43bb-a16c-44eb-8e48-2c605a2e8ac7&#34; class=&#34;&#34;&gt;Another option is to run your server behind reverse proxy supporting HTTPS, like &lt;a href=&#34;https://caddyserver.com/&#34;&gt;Caddy&lt;/a&gt;.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;402e3ed5-05f3-424a-a59e-eac3ce271f0f&#34; class=&#34;&#34;&gt;Directly supporting HTTPS&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;1a332460-1e50-4807-a9f3-226b8f3206ac&#34; class=&#34;&#34;&gt;Not long ago, if you wanted a SSL certificate, you had to pay many dollars a year for each domain.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;1bda29c3-10fb-49d0-bc16-aed6ca6add18&#34; class=&#34;&#34;&gt;&lt;a href=&#34;https://letsencrypt.org/&#34;&gt;Let’s Encrypt&lt;/a&gt; changed that. It’s a non-profit organization that provides certificates for free and offers HTTP API for obtaining certificates. API allows automating the process.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;ee214fe1-da6e-48d5-8f4e-26731f067831&#34; class=&#34;&#34;&gt;Before Let’s Encrypt you would buy a certificate, which is just a bunch of bytes. You would save certificate to a file and configure your web server to use it.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;0ce104b4-6992-405d-a297-ac0eba9a3862&#34; class=&#34;&#34;&gt;With Let’s Encrypt you can use their API to obtain the certificate for free, automatically, when your server starts.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;17ebaf3f-1836-4972-9e03-fff13bb0d41e&#34; class=&#34;&#34;&gt;Thankfully all the hard work of talking to the API has already bee done by others. We just need to plug it in.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;7bbcc7fc-6a5e-44be-8a4f-7b386852a349&#34; class=&#34;&#34;&gt;There are a couple of Go libraries that implement Let’s Encrypt support.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;045540ed-8402-4518-9044-dd35aae0edad&#34; class=&#34;&#34;&gt;I’ve been using &lt;a href=&#34;https://godoc.org/golang.org/x/crypto/acme/autocert&#34;&gt;golang.org/x/crypto/acme/autocert&lt;/a&gt;, which is developed by Go core developers. It’s been several months now and it works flawlessly.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;012a214b-beb6-4886-8291-562e05a213b8&#34; class=&#34;&#34;&gt;Here’s how to start HTTPS web server that uses free SSL certificates from Let’s Encrypt.&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;htmlIndex&lt;/span&gt;    &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;`&amp;lt;html&amp;gt;&amp;lt;body&amp;gt;Welcome!&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;`&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;inProduction&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;handleIndex&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;w&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;http&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ResponseWriter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;r&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;http&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Request&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;io&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;WriteString&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;w&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;htmlIndex&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;makeHTTPServer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;http&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Server&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;mux&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;http&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ServeMux&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;mux&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;HandleFunc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;/&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;handleIndex&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// set timeouts so that a slow or malicious client doesn&amp;#39;t&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// hold resources forever&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;http&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Server&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;ReadTimeout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;  &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Second&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;WriteTimeout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Second&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;IdleTimeout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;  &lt;span class=&#34;mi&#34;&gt;120&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Second&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;Handler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;      &lt;span class=&#34;nx&#34;&gt;handler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;main&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;httpsSrv&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;http&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Server&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;m&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;autocert&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Handler&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// when testing locally it doesn&amp;#39;t make sense to start&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// HTTPS server, so only do it in production.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// In real code, I control this with -production cmd-line flag&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;inProduction&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;// Note: use a sensible value for data directory&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;        &lt;span class=&#34;c1&#34;&gt;// this is where cached certificates are stored&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;        &lt;span class=&#34;nx&#34;&gt;dataDir&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;.&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;hostPolicy&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ctx&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;context&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Context&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;host&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;error&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;c1&#34;&gt;// Note: change to your real domain&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;            &lt;span class=&#34;nx&#34;&gt;allowedHost&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;www.mydomain.com&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;host&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;allowedHost&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;fmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Errorf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;acme/autocert: only %s host is allowed&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;allowedHost&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;httpsSrv&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;makeHTTPServer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;m&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;autocert&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Manager&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nx&#34;&gt;Prompt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;     &lt;span class=&#34;nx&#34;&gt;autocert&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;AcceptTOS&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nx&#34;&gt;HostPolicy&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;hostPolicy&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nx&#34;&gt;Cache&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;      &lt;span class=&#34;nx&#34;&gt;autocert&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;DirCache&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;dataDir&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;httpsSrv&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Addr&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;:443&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;httpsSrv&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;TLSConfig&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;tls&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Config&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;GetCertificate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;m&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;GetCertificate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;go&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;httpsSrv&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;ListenAndServeTLS&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Fatalf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;httpsSrv.ListendAndServeTLS() failed with %s&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;httpSrv&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;makeHTTPServer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;m&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;// allow autocert handle Let&amp;#39;s Encrypt auth callbacks over HTTP.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;        &lt;span class=&#34;c1&#34;&gt;// it&amp;#39;ll pass all other urls to our hanlder&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;        &lt;span class=&#34;nx&#34;&gt;httpSrv&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Handler&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;m&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;HTTPHandler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;httpServ&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Handler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;httpSrv&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Addr&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;:80&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;httpSrv&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;ListenAndServe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Fatalf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;httpSrv.ListenAndServe() failed with %s&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;a6e0501e-cc08-40b4-ab5f-3483f2d6ac66&#34; class=&#34;&#34;&gt;Full example: &lt;a href=&#34;https://github.com/kjk/go-cookbook/blob/master/free-ssl-certificates/main.go&#34;&gt;free-ssl-certificates/main.go&lt;/a&gt;.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;ff869de6-7080-495c-a741-6124e5a95d53&#34; class=&#34;&#34;&gt;There are some important things to note.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;0cb18c44-a27f-4945-b993-bbab4e854b9e&#34; class=&#34;&#34;&gt;&lt;strong&gt;1.&lt;/strong&gt; The standard port for HTTPS is 443&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;dbb68689-5849-494a-a0c6-64804d2cbabe&#34; class=&#34;&#34;&gt;&lt;strong&gt;2.&lt;/strong&gt; You can run only HTTP, only HTTPS or both.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;8630248b-2589-4e48-805d-d68158daaac3&#34; class=&#34;&#34;&gt;&lt;strong&gt;3.&lt;/strong&gt; If the server doesn’t have a certificate, it’ll use HTTP API to ask Let’s Encrypt servers for it.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;7ab47dc1-a688-440a-9e7c-9f29ad5c73eb&#34; class=&#34;&#34;&gt;Those requests are throttled to 20 per week to avoid over-loading Let’s Encrypt servers.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;e52840a5-e9d7-4030-acdd-d7e8ebbfc6ca&#34; class=&#34;&#34;&gt;It’s therefore important to cache the certificate somewhere. In our example we cache them on disk, using &lt;a href=&#34;https://godoc.org/golang.org/x/crypto/acme/autocert#DirCache&#34;&gt;&lt;code&gt;autocert.DirCache&lt;/code&gt;&lt;/a&gt; cache.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;eb5596b5-b4c8-483e-8e07-1a8e7fc4b469&#34; class=&#34;&#34;&gt;Cache is an interface so you could implement your own storage e.g. in a SQL database or Redis.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;62f1a75a-0376-43ce-ad29-a94e3d4097c3&#34; class=&#34;&#34;&gt;&lt;strong&gt;4.&lt;/strong&gt; You must set up DNS correctly.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;23087b38-03b0-49a9-b391-0674074a0f81&#34; class=&#34;&#34;&gt;To verify that you’re the owner of domain for which you want a certificate, Let’s Encrypt server calls back your server.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;585aaabc-f874-430d-b2b6-c56df86266f1&#34; class=&#34;&#34;&gt;For that to work, DNS name must resolve to the IP address of your server.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;1ce2dd63-cff1-48bc-880b-db5069069a3e&#34; class=&#34;&#34;&gt;This means that local testing of HTTPS code-path is hard. I usually don’t bother.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;cb075fc1-c3cf-4612-9cec-e6d8f388cb83&#34; class=&#34;&#34;&gt;If you really want to, you can use &lt;a href=&#34;https://ngrok.com/&#34;&gt;ngrok&lt;/a&gt; to expose your local port to the internet, set DNS to resolve your domain to public DNS name that ngrok creates, wait a bit to make sure that DNS information propagates to Let’s Encrypt computers.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;61f6a8dd-50fe-43fa-9903-08e55a5cd57b&#34; class=&#34;&#34;&gt;&lt;strong&gt;5.&lt;/strong&gt; You might be wondering: what is this HostPolicy business?&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;d6386ab8-1cf0-4056-910e-bb63a7e6fd32&#34; class=&#34;&#34;&gt;As I mentioned, Let’s Certificate throttles certificate provisioning so you need to ensure the server won’t ask for certificates for domains you don’t care about. Autocert docs &lt;a href=&#34;https://github.com/golang/crypto/blob/5a033cc77e57eca05bdb50522851d29e03569cbe/acme/autocert/autocert.go#L104&#34;&gt;explain this well&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;9fe0860f-eae3-4f05-bf5c-ba27266d1e27&#34; class=&#34;&#34;&gt;Our example assumes most common case: a server that only responds to a single domain. You can easily change the logic.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;0f289e00-d44d-41f0-a3ea-47dfef0fe517&#34; class=&#34;&#34;&gt;&lt;strong&gt;6.&lt;/strong&gt; We’re not running HTTPS when testing locally.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;b7aa95d7-995b-46a9-b6c9-ce0775413a75&#34; class=&#34;&#34;&gt;When testing locally on your laptop, there’s no point in running HTTPS version. Your computer most likely doesn’t have publicly visible IP address so Let’s Encrypt servers can’t reach you, so you won’t get the certificate.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;84020e4f-861c-4325-9435-863b3bd4b784&#34; class=&#34;&#34;&gt;We also won’t be able to bind to HTTPS port 443 (only root processes can bind to ports lower than 1024).&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;61df6233-a0fe-4f1b-8af5-2eba6ca2018c&#34; class=&#34;&#34;&gt;In the example I use &lt;code&gt;inProduction&lt;/code&gt; flag to decide if I should start HTTPS server.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;963e419a-b3c0-427c-bae8-8cbf31a855da&#34; class=&#34;&#34;&gt;In real code I add code that checks for &lt;code&gt;-production&lt;/code&gt; cmd-line flag and use that.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;ba56083a-c78a-4c10-841a-5d4a711c75bf&#34; class=&#34;&#34;&gt;Redirecting from HTTP to HTTPS&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;ccaa2e9b-be42-431b-8551-693131872ce4&#34; class=&#34;&#34;&gt;If you can do HTTPS there’s no point in providing plain HTTP.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;11b1c3c4-9e5e-4f4f-9e5e-05804c3bffd2&#34; class=&#34;&#34;&gt;We can re-direct all HTTP request to HTTPS equivalent, for better security and SEO (Google doesn’t like duplicate content so your SEO rank will be better with a single version of the website).&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;makeServerFromMux&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;mux&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;http&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ServeMux&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;http&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Server&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// set timeouts so that a slow or malicious client doesn&amp;#39;t&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// hold resources forever&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;http&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Server&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;ReadTimeout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;  &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Second&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;WriteTimeout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Second&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;IdleTimeout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;  &lt;span class=&#34;mi&#34;&gt;120&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Second&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;Handler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;      &lt;span class=&#34;nx&#34;&gt;mux&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;makeHTTPToHTTPSRedirectServer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;http&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Server&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;handleRedirect&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;w&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;http&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ResponseWriter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;r&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;http&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Request&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;newURI&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;https://&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Host&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;URL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;http&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Redirect&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;w&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;newURI&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;http&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;StatusFound&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;mux&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;http&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ServeMux&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;mux&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;HandleFunc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;/&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;handleRedirect&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;makeServerFromMux&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;mux&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;main&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;httpSrv&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;makeHTTPToHTTPSRedirectServer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;httpSrv&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Handler&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;m&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;HTTPHandler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;httpServ&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Handler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;httpSrv&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Addr&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;:80&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;fmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Printf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;Starting HTTP server on %s\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;httpSrv&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Addr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;httpSrv&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;ListenAndServe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Fatalf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;httpSrv.ListenAndServe() failed with %s&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;1c08c304-164d-4e0a-9cf7-66715cf2c6ec&#34; class=&#34;&#34;&gt;Code for this chapter: &lt;a href=&#34;https://github.com/kjk/go-cookbook/tree/master/free-ssl-certificates&#34;&gt;https://github.com/kjk/go-cookbook/tree/master/free-ssl-certificates&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;90f427f0-d6e0-4611-9f07-ba96a727d0d5&#34; class=&#34;&#34;&gt;How certificates came to be free&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;0d0d6c56-be99-4f24-b298-8ed8577c0a3b&#34; class=&#34;&#34;&gt;Arguably due to a design mistake, SSL protocol not only encrypts but also proves site’s identity to the browser.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;d968478c-efec-43a4-9d9f-9647a91f222a&#34; class=&#34;&#34;&gt;It provides accountability so that we can trace the ownership of google.com and see that it is indeed owned by Google, Inc in US, and not Ivan The Hacker in Moscow.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;fc88a228-8890-46fb-9198-70607866fc1d&#34; class=&#34;&#34;&gt;We implement that accountability by trusting a very small number of companies (Certificate Authorities) to issue certificates that prove the identity of the website owner.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;5aed4b4e-9805-4223-a925-542cc738aaf1&#34; class=&#34;&#34;&gt;When you apply for a certificate, Certificate Authority has to verify your identity. They do it by checking your papers.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;833d8924-e904-47d1-b8bd-2a7121bbb467&#34; class=&#34;&#34;&gt;Verifying the papers requires labor. Keeping certificates safe requires labor. It’s reasonable that Certificate Authorities charge for the service of issuing certifcates.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;edc10ca7-97ae-45bd-ad09-5b1ae2282c0d&#34; class=&#34;&#34;&gt;The trust doesn’t scale. Browser and OS vendors can trust 10 companies to not issue invalid certificates, but they can’t trust a thousand.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;a7abb261-eef1-4e98-b367-2a5f03ba01fe&#34; class=&#34;&#34;&gt;We don’t want any random company to become a rogue certificate authority and start issuing certificates for google.com domains to Ivan The Hacker.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;d55bca98-3b68-480c-88e8-127a170ac42c&#34; class=&#34;&#34;&gt;It would be too much effort to continuosly audit thousands of Certificate Authority companies so as a result we ended up with just a few.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;961bbbf2-6ce5-452c-b3db-4344cb0179ce&#34; class=&#34;&#34;&gt;A market controlled by small number of companies tends to become a cartel that keeps prices high due to lack of competition.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;36d5f074-0dd4-4908-8b07-6acd2ca3d378&#34; class=&#34;&#34;&gt;That’s exactly what happened in SSL certificates market. You can have a low-end server for $60/year and a certificate alone would cost more than that.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;7c455fe2-edf6-479e-8dcd-c9cc7dfa6781&#34; class=&#34;&#34;&gt;That was a problem because the cost of SSL certificates was a significant barrier to adopting encryption by all websites.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;8d555afd-273a-4f1f-9b34-ce54088d202f&#34; class=&#34;&#34;&gt;A few companies decided to pool their resources and solve that problem for the greater good of the web.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;7b2b0f62-0408-4073-b960-f4236f08a77f&#34; class=&#34;&#34;&gt;They funded Let’s Encrypt which became a Certificate Authority, wrote necessary software and is running the servers that do the work of issuing certificates. It’s been a &lt;a href=&#34;//letsencrypt.org/2017/06/28/hundred-million-certs.html&#34;&gt;raging success&lt;/a&gt;.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;b5e91575-e790-4c9e-b774-33014fe13405&#34; class=&#34;&#34;&gt;And that’s how free certificates came to be.&#xA;  &lt;/div&gt;&#xA;&lt;/div&gt;</content>
  </entry>
  <entry>
   <title>Solo founders with profitable businesses, collected stories</title>
   <link href="https://blog.kowalczyk.info/article/wjRD/solo-founders-with-profitable-businesses-collected-stories.html" rel="alternate"></link>
   <updated>2017-06-23T23:44:19Z</updated>
   <id>tag:blog.kowalczyk.info,2017-06-23:/article/wjRD/solo-founders-with-profitable-businesses-collected-stories.html</id>
   <content type="html">&lt;p&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;notion-page&#34; id=&#34;431295a5-4f7e-4208-869f-4763862c1f05&#34;&gt;&#xA;  &lt;div id=&#34;dda9e4a5-22d8-48e7-a6de-a981c9943b37&#34; class=&#34;&#34;&gt;People sometimes wonder: can I have a successful business as a single founder?&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;2c249c6c-3a8d-45a7-9275-e1cfba7d2ed5&#34; class=&#34;&#34;&gt;The answer is: &lt;strong&gt;yes&lt;/strong&gt;.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;d420b94a-9c58-4147-8e32-971be7384d50&#34; class=&#34;&#34;&gt;This is a collection of solo-preneur success stories (with occasional 2 people bands).&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;f51e3e53-5472-4cf1-872f-d394f9b78afc&#34; class=&#34;&#34;&gt;I only include businesses generating significant revenues. In this context it’s around $5k/mo or more, enough to replace full-time salary.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;1b85f9a6-6e6e-4f02-8645-912078699e4c&#34; class=&#34;&#34;&gt;Maybe it’ll inspire you to start your own, solo business.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;a98b30b0-ab17-4a41-84d8-a53272e20680&#34; class=&#34;&#34;&gt;Before you get too excited, keep the following in mind.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;707038f0-882c-4235-a6e6-8bb83d9f2901&#34; class=&#34;&#34;&gt;This list is the pinnacle of survivorship bias. Solo-preneur software business is not different that any other business, and most businesses fail. You have 10-20% chance of success so pick your idea wisely, work hard and if you fail, do it again.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;9c00bad8-c485-4324-8468-5bbb28bbb96a&#34; class=&#34;&#34;&gt;At the same time, this is only tip of the iceberg. Those are the stories that people shared, cribbed from a few online sources.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;1cae84d0-8b9b-4664-b9e3-816feb7dc002&#34; class=&#34;&#34;&gt;There are 100x more successful solo founders that don’t share their numbers publicly. A silent majority of successful solo businesses.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;2abf0483-b9aa-4f43-a694-24d69071ac75&#34; class=&#34;&#34;&gt;1. Anonymous making $750k/year with a desktop app, sold via his website&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;5af1e1ad-f0ba-4c78-aa43-32f2be496c90&#34; class=&#34;&#34;&gt;Source: &lt;a href=&#34;https://news.ycombinator.com/item?id=13168965&#34;&gt;https://news.ycombinator.com/item?id=13168965&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;38c7496b-061a-43df-8687-778c62d7f15d&#34; class=&#34;&#34;&gt;Adivice: &#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;889be54c-46e1-4ee5-b137-b34b453640ab&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;make a desktop app that you can sell for $50-$300. &#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;attack a large market that has stagnated or has entranched players with lousy apps i.e. make a better mousetrap. &#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;electron is a good technology to write such app.&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;div id=&#34;c4b6b0a9-2338-4428-a9d6-492ea911e3e8&#34; class=&#34;&#34;&gt;Important factors of success: &#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;81711fa9-ab90-4654-875f-37b26170b454&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;SEO&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;good reputation&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;big market&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;staying alive long enough for word of mouth to kick in&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;h2 id=&#34;ae00bd40-a827-4196-9240-521d8c3baad5&#34; class=&#34;&#34;&gt;2. JollyTurns&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;fa8a18aa-edd5-4a8c-ad50-3bba00b103b9&#34; class=&#34;&#34;&gt;Source: &lt;a href=&#34;https://news.ycombinator.com/item?id=13170798&#34;&gt;https://news.ycombinator.com/item?id=13170798&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;6a6f8610-5046-42b1-98c4-43626220777f&#34; class=&#34;&#34;&gt;Web/iOS/Android app for ski resorts.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;508f6871-c9c5-4009-8bc0-f0ddf3d8faf2&#34; class=&#34;&#34;&gt;Income: unknown.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;55401006-b70b-4467-9630-5adfcf130ba7&#34; class=&#34;&#34;&gt;How it makes money: in-app purchase on iOS/Android ($1 per ski resort or $15 for all of them, see &lt;a href=&#34;https://itunes.apple.com/us/app/jollyturns/id719208522&#34;&gt;https://itunes.apple.com/us/app/jollyturns/id719208522&lt;/a&gt;)&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;546880b9-3d4d-43e1-b441-78831ff2fea2&#34; class=&#34;&#34;&gt;First version (iOS only) released in Dec 2013 after 2 1/2 years of work (&lt;a href=&#34;https://jollyturns.com/blog/first-public-release&#34;&gt;https://jollyturns.com/blog/first-public-release&lt;/a&gt;).&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;3f44e588-3936-4b16-9591-ef7f43f02386&#34; class=&#34;&#34;&gt;Insight: code is the easy part, marketing is hardest.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;55d2de50-35f2-44f5-aa44-13b10ae958c6&#34; class=&#34;&#34;&gt;Tried to find a partner in SV but couldn’t. Wrote code himself, hired people to collect data about ski resorts.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;117e05aa-fa71-4f9c-938c-9b3eede12085&#34; class=&#34;&#34;&gt;3. CRM plugin that finds location of customer’s offices based on address of the hotel you’re traveling to&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;d22e3bef-7bfe-44ee-91dd-018da3f72428&#34; class=&#34;&#34;&gt;Source: &lt;a href=&#34;https://news.ycombinator.com/item?id=13167930&#34;&gt;https://news.ycombinator.com/item?id=13167930&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;167bcf79-98dd-4c11-ad65-01bb010e1d75&#34; class=&#34;&#34;&gt;Sold for $50. “made pretty good money”.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;369edc08-05d1-448d-9f24-15139633d2c3&#34; class=&#34;&#34;&gt;4. Niche app, $200k/year after 6 years&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;83a988e7-db18-4054-9209-028a5a735cb9&#34; class=&#34;&#34;&gt;Source: &lt;a href=&#34;https://news.ycombinator.com/item?id=13170346&#34;&gt;https://news.ycombinator.com/item?id=13170346&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;5a41ba28-dfbf-4890-bd4e-c78d6faf6613&#34; class=&#34;&#34;&gt;Most likely USB driver that allows using USB devices remotely over a network (&lt;a href=&#34;https://www.virtualhere.com&#34;&gt;https://www.virtualhere.com,&lt;/a&gt; &lt;a href=&#34;https://forum.openwrt.org/search.php?action=show_user_posts&amp;amp;user_id=129043&#34;&gt;https://forum.openwrt.org/search.php?action=show_user_posts&amp;amp;user_id=129043&lt;/a&gt; is same user name as on HN post).&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;b794c7bb-4915-4d4e-9119-671a39b0edd4&#34; class=&#34;&#34;&gt;No marketing, gets sales via word of mouth and internet searches.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;575423e7-3a26-484b-8ea9-6e6b8e868699&#34; class=&#34;&#34;&gt;5. Website templates, $100k/year profit&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;1a08da54-94c8-4ae9-9b99-243b81ed1a42&#34; class=&#34;&#34;&gt;Source: &lt;a href=&#34;https://news.ycombinator.com/item?id=13935663&#34;&gt;https://news.ycombinator.com/item?id=13935663&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;f42f768e-88d0-496b-9cdd-b80e8deed234&#34; class=&#34;&#34;&gt;Sold on &lt;a href=&#34;https://themeforest.net/&#34;&gt;https://themeforest.net/&lt;/a&gt;. Working about 4hrs a week.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;34f49aba-b323-49d9-aa61-8d3a47a78624&#34; class=&#34;&#34;&gt;6. Shopify plugins&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;5bd4d5a2-a6b0-428e-a77d-ebd36388de29&#34; class=&#34;&#34;&gt;Source: &lt;a href=&#34;https://news.ycombinator.com/item?id=13167990&#34;&gt;https://news.ycombinator.com/item?id=13167990&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;3e953dd2-cd0f-4bc9-8f46-d6049f98e727&#34; class=&#34;&#34;&gt;Makes enough money to hire full-time developer for onboarding of new clients, support and documentation. Does product development himself.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;74fe0c1c-62e4-4987-9609-ca07517d318f&#34; class=&#34;&#34;&gt;Insight: the trick for coming up with product ideas is to first do custom development. When enough clients are willing to spend a few thousand for a personal implementation of something, that’s when you know you have an opportunity to charge 40$ per month for a SaaS version.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;974baa77-1944-4a58-9c71-253f5785071c&#34; class=&#34;&#34;&gt;7. $20k/mo from 3 niche SaaS products&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;d0191544-43fd-4a32-b557-bfdbb97b8440&#34; class=&#34;&#34;&gt;Source:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;f9c66b29-5c92-4bfb-ab7d-7bceb0fb28ed&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://news.ycombinator.com/item?id=13170108&#34;&gt;https://news.ycombinator.com/item?id=13170108&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://news.ycombinator.com/item?id=12066819&#34;&gt;https://news.ycombinator.com/item?id=12066819&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;div id=&#34;1a7dfa46-0f52-4cf3-82ab-5866b26f6c20&#34; class=&#34;&#34;&gt;Also in the past ran real estate SaaS, making $70k/mo at its height (it then crashed when real estate market crashed).&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;324ae6e7-864d-469f-965f-4afaf8ede2b8&#34; class=&#34;&#34;&gt;Advice:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;fef02de8-29d9-4e54-b6e4-5f06a918942c&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;tip for a newbie would be to look for something niche that solves a pain point or allows your customer to make or save money. For example: if your customer can spend $10/mo on your software and make or save $30/mo from that, you will have no problem getting &amp;amp; keeping customers. &#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;don’t focus on becoming a unicorn. You can make some serious money and build a very comfortable life running a $300k to $1m dollar business, and your chances of succeeding at that are much greater.&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;Look for things outside of tech. There are so many problems to solve in small businesses. Many will say there is no money to make with small businesses.&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;Learn everything you can about advertising. Get really good at it and be willing to spend money on advertising.&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;Be willing to kill something off quickly if it doesn’t make money. Test your market early to make sure people will pay for it. I have made the mistake of not doing this and I have lost a lot of money because of it. Now, I need to be able to see a positive ROI on my spend within 3 to 4 months. So if I spent $100 to acquire a customer, I want to be able to get that back + more within 3 to 4 months. I know this timeline is probably really short for a VC funded company, but I have always been bootstrapped so don’t have the luxury of risking a longer time-frame for return.&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;it is not easy! Prepare to put in long hours especially in the beginning. Prepare for it to take a mental toll at times. You will second guess yourself, feel insecure, be consumed oftentimes with your business.&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;h2 id=&#34;369aa92d-7e0e-4ce4-b9cf-5f2134c255ab&#34; class=&#34;&#34;&gt;8. Ngrok&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;9fcef9e5-7a2b-4211-a7a3-f83cad9a57e4&#34; class=&#34;&#34;&gt;Source: &lt;a href=&#34;https://news.ycombinator.com/item?id=13168185&#34;&gt;https://news.ycombinator.com/item?id=13168185&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;73fef07e-f6cf-4c33-831e-d339bc26d774&#34; class=&#34;&#34;&gt;Full time for past 4 years.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;f54aa626-5704-43ee-887f-bdabf95e0039&#34; class=&#34;&#34;&gt;Insight: minimize support by improving UX, documentation, error messages.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;04675731-91d7-4a15-abd7-c6260c7ded89&#34; class=&#34;&#34;&gt;9. Football betting analysis/predictions website, £75k / year&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;59006d09-5083-4385-9c0e-62b90bbe4f74&#34; class=&#34;&#34;&gt;Source: &lt;a href=&#34;https://news.ycombinator.com/item?id=11216868&#34;&gt;https://news.ycombinator.com/item?id=11216868&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;a16bf144-d6de-45ac-80ce-f8f12e963e35&#34; class=&#34;&#34;&gt;&lt;a href=&#34;http://betalyst.com/&#34;&gt;http://betalyst.com/&lt;/a&gt; makes £75,000 per year in advertising/sponsorship/affiliate revenue.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;4c209268-8493-4f98-bba9-8a4eafdca63f&#34; class=&#34;&#34;&gt;Gets !25k visitors a month.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;d5427793-1930-4cec-b7d6-32718f6a962e&#34; class=&#34;&#34;&gt;Works 2-3 hrs a week.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;43a420e0-19e6-4282-8887-1f14b7304d4f&#34; class=&#34;&#34;&gt;Website traffic generation:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;09a38d56-c16c-4ea2-bae6-f7f31da105c0&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;Android app with 75k users&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;10% of traffic is from organic search&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;majority of traffic from email (20k subscribers)&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;h2 id=&#34;0f8eec30-e8db-4dca-bac0-023f8410bf16&#34; class=&#34;&#34;&gt;10. Watermarking desktop app for Mac/Windows&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;160d94f5-6e55-4ff0-994a-f9b9f4de9055&#34; class=&#34;&#34;&gt;Source:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;f45cdb6d-b59d-4dfa-bcd9-60a12d22e06c&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://news.ycombinator.com/item?id=13150864&#34;&gt;https://news.ycombinator.com/item?id=13150864&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://news.ycombinator.com/item?id=11604736&#34;&gt;https://news.ycombinator.com/item?id=11604736&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;div id=&#34;0bbac3b4-2c9e-4fd4-ae6c-a9c8d2a37ee0&#34; class=&#34;&#34;&gt;Makes $3k-$5k per month after 5 years.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;49ffa39d-ad28-450e-b536-76761ead454d&#34; class=&#34;&#34;&gt;Sells for $30/$60/$140.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;940207c7-04b5-4ed6-974d-f01b6e5c6568&#34; class=&#34;&#34;&gt;11. Real estate startup, $130k/year of profit&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;8145e302-5f91-4d4b-80c7-2b2baf47af56&#34; class=&#34;&#34;&gt;Source: &lt;a href=&#34;https://news.ycombinator.com/item?id=10887978&#34;&gt;https://news.ycombinator.com/item?id=10887978&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;25519952-083a-4d75-94b6-7ded1605b698&#34; class=&#34;&#34;&gt;Profit:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;f28e3179-2d2f-4367-a789-eb2d36e6489b&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;130k in 2013&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;$100k in 2014&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;$140k in 2015&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;div id=&#34;4828726d-b1be-41b0-9571-68cbe5ada9af&#34; class=&#34;&#34;&gt;Source of revenues:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;a208b848-8df4-46df-9227-6d4b60583721&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;30% AdSense&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;20% users&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;50% affiliate marketing&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;div id=&#34;1b518098-65d8-4f0c-8c84-ec09d585d68b&#34; class=&#34;&#34;&gt;Sources of traffic:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;45f8e6d3-f640-452f-ae21-0988cb4b8af1&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;50% organic&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;37% referral&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;13% direct&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;div id=&#34;dfdc05b0-9726-4133-871e-9bf158a643cb&#34; class=&#34;&#34;&gt;No marketing, no blog, no social presence.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;803ec0cf-52ca-4630-8ad4-80344d26b26f&#34; class=&#34;&#34;&gt;12. Dan Grossman, improvely and w3counter, $45k/mo&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;ea056b67-2184-4be1-87af-a5cd007da770&#34; class=&#34;&#34;&gt;Source:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;ad615de3-4c3f-4a66-a64b-116e7e822abc&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://news.ycombinator.com/item?id=10881301&#34;&gt;https://news.ycombinator.com/item?id=10881301&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://news.ycombinator.com/item?id=9726951&#34;&gt;https://news.ycombinator.com/item?id=9726951&lt;/a&gt; : how he started&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://news.ycombinator.com/item?id=10201718&#34;&gt;https://news.ycombinator.com/item?id=10201718&lt;/a&gt;, &lt;a href=&#34;https://news.ycombinator.com/item?id=8396497&#34;&gt;https://news.ycombinator.com/item?id=8396497&lt;/a&gt; : business tips &#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://news.ycombinator.com/item?id=8406049&#34;&gt;https://news.ycombinator.com/item?id=8406049&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;div id=&#34;62e4db41-c191-40af-b6b9-2adacb5fb7e1&#34; class=&#34;&#34;&gt;&lt;a href=&#34;https://www.w3counter.com/&#34;&gt;https://www.w3counter.com/&lt;/a&gt; uses freemium model, people pay subscription for advanced features&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;8f3d1746-216d-4df0-8957-d8d31dae5c00&#34; class=&#34;&#34;&gt;&lt;a href=&#34;https://www.improvely.com/&#34;&gt;https://www.improvely.com/&lt;/a&gt; : SaaS priced $29/$79/$149/$299 / mo.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;bd636276-4f96-47d0-94c2-0c6c0aef767f&#34; class=&#34;&#34;&gt;First customers for improvely came from $100-$200/month AdWords advertising for the first few months and $79/month banner ad on a web stats site bought via BuySellAds.com.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;cd6d04a8-795b-415d-bd54-9f9a110eba52&#34; class=&#34;&#34;&gt;Used SnapEgage chat widget on the website to talk visitors to sign up.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;c27c781c-3cef-4425-836d-c8da0a959bb7&#34; class=&#34;&#34;&gt;Word of mouth and referrals started quickly after that.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;6dbcb25b-f00b-4610-b3c1-ad321a8aac32&#34; class=&#34;&#34;&gt;Now referrals are biggest signup drivers.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;1af3d6d8-3f08-4d12-b9ff-e00aaee0fad6&#34; class=&#34;&#34;&gt;13. VNC application for Mac and iOS&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;59fc7d47-9702-4aa8-85c0-1054b6849267&#34; class=&#34;&#34;&gt;Sells for $30 on Mac and $20 on iOS.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;ed22495b-b6b7-4ca0-b069-35cc3f271ae5&#34; class=&#34;&#34;&gt;Source:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;feded23b-136d-494a-b08a-8b9b948f26af&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://lucvandal.com/2017/01/19/ten-years/&#34;&gt;https://lucvandal.com/2017/01/19/ten-years/&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://lucvandal.com/2015/07/05/how-to-be-a-successful-indie-developer/&#34;&gt;https://lucvandal.com/2015/07/05/how-to-be-a-successful-indie-developer/&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;h2 id=&#34;a70724e1-291e-4325-9742-413b41d62a5b&#34; class=&#34;&#34;&gt;14. NomadList, $400k/year revenue&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;a57c92ca-e936-48c1-91d4-90fabae1c0ad&#34; class=&#34;&#34;&gt;Source:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;b4615054-b28e-4f4f-b703-c9d69b9a9f9b&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://news.ycombinator.com/item?id=13443124&#34;&gt;https://news.ycombinator.com/item?id=13443124&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://news.ycombinator.com/item?id=13443124&#34;&gt;https://news.ycombinator.com/item?id=13443124&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;div id=&#34;d5b91107-2cfb-404e-ad4d-4487a008ff24&#34; class=&#34;&#34;&gt;Revenue source: membership fees for community of digital nomads and remote workers.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;e1d540b8-acb5-4e4d-a7f6-7f5e635470f1&#34; class=&#34;&#34;&gt;15. B2B Windows desktop app, $1 million/year sales&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;a88df969-b15b-46f2-8b20-63ca301ddf21&#34; class=&#34;&#34;&gt;Source: &lt;a href=&#34;https://news.ycombinator.com/item?id=12066104&#34;&gt;https://news.ycombinator.com/item?id=12066104&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;3a88a385-c689-46e6-bb3b-18f4a38fe00f&#34; class=&#34;&#34;&gt;Wrote scrach-my-itch app, was side project for 10 years until it started making $120k/mo. Went full time after that. Is in a very crowded niche.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;d6d6005f-a7af-4970-9ff4-b5f456eacdfd&#34; class=&#34;&#34;&gt;Insight: coding is easy, marketing is hard. Must be persistent.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;2190a199-74c9-474f-8345-2d74a892776c&#34; class=&#34;&#34;&gt;16. Pinboard, bookmarking web service, $200k/year&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;a8acf505-805e-4b0a-b7a3-bfdb8b9fe9d5&#34; class=&#34;&#34;&gt;Source:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;194da84d-0452-4d1e-a857-9b85f43048ee&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://news.ycombinator.com/item?id=14438221&#34;&gt;https://news.ycombinator.com/item?id=14438221&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://news.ycombinator.com/item?id=14438296&#34;&gt;https://news.ycombinator.com/item?id=14438296&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;div id=&#34;a04a4ab8-90c1-4553-8560-6bffbb9ced62&#34; class=&#34;&#34;&gt;Revenue history: &lt;a href=&#34;https://blog.pinboard.in/2016/07/pinboard_turns_seven/&#34;&gt;https://blog.pinboard.in/2016/07/pinboard_turns_seven/&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;88d33fba-41ca-4a26-bab5-5aa931ad7543&#34; class=&#34;&#34;&gt;17. s3stat, “equivalent of a nice Senior Developer salary”&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;44e54070-4d7e-4d5b-9331-b9c4de8ba1c9&#34; class=&#34;&#34;&gt;Source:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;63b73ffc-e889-4e0f-b328-6b0f3c687174&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;http://www.expatsoftware.com/Articles/guy-on-the-beach-with-a-laptop.html&#34;&gt;http://www.expatsoftware.com/Articles/guy-on-the-beach-with-a-laptop.html&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://news.ycombinator.com/item?id=14438283&#34;&gt;https://news.ycombinator.com/item?id=14438283&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;h2 id=&#34;b76fa991-52cb-4934-aafa-1e1eba361255&#34; class=&#34;&#34;&gt;18. StoreSlider, $700k in 2016&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;f09a3a3d-2a7a-4783-9a31-69a6927ed4d3&#34; class=&#34;&#34;&gt;Source: &lt;a href=&#34;https://news.ycombinator.com/item?id=14438303&#34;&gt;https://news.ycombinator.com/item?id=14438303&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;ae11cd11-4409-4214-a265-4c021a9435bb&#34; class=&#34;&#34;&gt;Makes money with affiliate revenue from eBay.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;2602e93b-976a-4fa0-9c63-6d2dd4936ccf&#34; class=&#34;&#34;&gt;Built with Lumen on PHP 7.1, Nginx, running on Linode.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;30b0a710-2d96-4127-a5ff-2675501e2f0b&#34; class=&#34;&#34;&gt;Source of traffic: word of mouth, social sharing, Google search.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;890ec347-2081-42e6-8547-7b65ea7dad53&#34; class=&#34;&#34;&gt;Did a lot of A/B testing to maximize conversion.s.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;7039cbba-a466-4c1e-b594-4f02168c470b&#34; class=&#34;&#34;&gt;19. BuiltiWith.com, estimated $12 million/year&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;7c9be2b2-c682-4785-9bc1-8ed3c85b22ac&#34; class=&#34;&#34;&gt;Source:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;6baddf99-b299-41bd-be26-be80a23c5e4f&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://news.ycombinator.com/item?id=14437983&#34;&gt;https://news.ycombinator.com/item?id=14437983&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;http://www.startupdaily.net/2015/09/builtwith-is-perhaps-one-of-australias-most-profitable-online-companies-and-has-zero-staff/&#34;&gt;http://www.startupdaily.net/2015/09/builtwith-is-perhaps-one-of-australias-most-profitable-online-companies-and-has-zero-staff/&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;h2 id=&#34;c178b5a4-622d-4311-a153-cb11f4d033df&#34; class=&#34;&#34;&gt;20. tarsnap, backup service, “better than Google salary”&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;aac9c590-cdaf-4a15-9eb0-5775af1b91bd&#34; class=&#34;&#34;&gt;Source: &lt;a href=&#34;https://news.ycombinator.com/item?id=14442425&#34;&gt;https://news.ycombinator.com/item?id=14442425&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;3625e18a-7898-43a8-98af-878d007b97ac&#34; class=&#34;&#34;&gt;21. Sidekiq, $1 million/year&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;17b8cb6a-061a-4271-837b-de4f76d053f0&#34; class=&#34;&#34;&gt;Source: &lt;a href=&#34;https://www.indiehackers.com/businesses/sidekiq&#34;&gt;https://www.indiehackers.com/businesses/sidekiq&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;dd025931-ed82-46bc-afc8-302b3a82a23e&#34; class=&#34;&#34;&gt;Open source library for Ruby, a background job framework.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;ecaf8ea7-b372-4fe6-8368-efbcd9315849&#34; class=&#34;&#34;&gt;Sells pro version for $950/year and enterprise version. Only needs 800 customer&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;10b6f34e-233a-4bc8-8c1d-7d0e0c4574c2&#34; class=&#34;&#34;&gt;22. Balsamiq, $2 millions in revenue after 18 months&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;aafb7791-9419-4604-9d3d-c54fa57fe460&#34; class=&#34;&#34;&gt;Source:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;24d9c238-665c-4193-aae5-4d3170d3f7fa&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;http://conversionaid.com/podcast/peldi-guilizzoni-balsamiq/&#34;&gt;http://conversionaid.com/podcast/peldi-guilizzoni-balsamiq/&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;http://programmingzen.com/startup-interviews-balsamiq-studio-llc/&#34;&gt;http://programmingzen.com/startup-interviews-balsamiq-studio-llc/&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;div id=&#34;c957db72-aa89-405c-a106-3d2ca1bd95ea&#34; class=&#34;&#34;&gt;It’s no longer a single person but it was created in 2008 by a single person and within 18 monts reached $2 million in revenue.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;c1f12a7e-75ad-4e6c-9c4a-1e73ca536074&#34; class=&#34;&#34;&gt;It’s a desktop app for creating mockups.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;d8bac549-233c-4c2e-b4bf-2f0af8fb2ad4&#34; class=&#34;&#34;&gt;23. John Gruber, $32k/mo&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;31c23fe4-b6dc-46f7-8083-614670ca46e3&#34; class=&#34;&#34;&gt;Source: &lt;a href=&#34;https://daringfireball.net/feeds/sponsors/&#34;&gt;https://daringfireball.net/feeds/sponsors/&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;014fe955-06e2-434c-aaf3-a84f978c9282&#34; class=&#34;&#34;&gt;Makes $8k per week for sponsorship (ads) on his very popular, Apple-oriented website.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;bc9d473a-1f3d-430f-9d45-43820fee1a18&#34; class=&#34;&#34;&gt;24. Sales tracking &amp;amp; CRM app for small business.&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;dea1ca83-915b-49f8-826f-6c1e3d4b521b&#34; class=&#34;&#34;&gt;Source: &lt;a href=&#34;https://news.ycombinator.com/item?id=14439284&#34;&gt;https://news.ycombinator.com/item?id=14439284&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;d690b7a7-f09f-4609-89e8-77b0a310fecc&#34; class=&#34;&#34;&gt;&lt;a href=&#34;https://www.bottomlinehq.com/&#34;&gt;https://www.bottomlinehq.com/&lt;/a&gt;, 6-digit revenue.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;2c24f09e-495d-4792-9426-ed5f43916724&#34; class=&#34;&#34;&gt;Freemium model, $30/year. Web and iOS.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;37f85cc4-049b-4301-8efd-1671585f2fe4&#34; class=&#34;&#34;&gt;25. https://officesnapshots.com/, full-time salary.&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;92dd912c-a792-4b85-a99f-9189a8526304&#34; class=&#34;&#34;&gt;Source: &lt;a href=&#34;https://news.ycombinator.com/item?id=12065574&#34;&gt;https://news.ycombinator.com/item?id=12065574&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;93b35feb-2a98-4001-8e5c-6b1f5a4498f7&#34; class=&#34;&#34;&gt;Started 9 years ago, full-time for last 4 years.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;990eeacb-4304-494b-801e-7a175b50b6cf&#34; class=&#34;&#34;&gt;Revenue: AdSense and later selling his own advertising (ads are sold as $/month and sold in blocks of 1 to 12 months.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;dd81b444-6fca-46f4-a0cf-c3ce4e7770b7&#34; class=&#34;&#34;&gt;26. pubexchange.com&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;794667d7-72fa-4eb5-80b2-04bb7b2d5ee3&#34; class=&#34;&#34;&gt;Source:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;755f2e48-e83a-4502-81f2-312c8912c3a9&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://news.ycombinator.com/item?id=13334001&#34;&gt;https://news.ycombinator.com/item?id=13334001&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://news.ycombinator.com/item?id=12073418&#34;&gt;https://news.ycombinator.com/item?id=12073418&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;div id=&#34;b97e5cb1-32a0-4681-b455-2efded92e1d4&#34; class=&#34;&#34;&gt;Stated in 2013, operated solo since then. Profitable since 2014.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;df94e4f2-8780-4230-a2eb-3f11d6e0be75&#34; class=&#34;&#34;&gt;In early days pitched his service on linkedin, now people come from referrals.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;b8d28ff2-1e59-4ab2-b7ac-9459cd44e7c5&#34; class=&#34;&#34;&gt;27. park.io, $125k/mo&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;f883cfc7-eb6c-4a0b-a4dc-984be82f75c7&#34; class=&#34;&#34;&gt;Source:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;c4962b4f-0a85-4f70-9d56-bfd742d2b925&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://www.indiehackers.com/businesses/park-io&#34;&gt;https://www.indiehackers.com/businesses/park-io&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://news.ycombinator.com/item?id=9726435&#34;&gt;https://news.ycombinator.com/item?id=9726435&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;div id=&#34;2b215951-055a-4bc9-9cbf-7896da1327cb&#34; class=&#34;&#34;&gt;Started in 2014. In his spare time he wrote a script to auto-buy domain when it expires and turned that into paid service by adding registration, payments etc.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;fc9a141c-6ff6-4c55-806c-0b8c0a929eeb&#34; class=&#34;&#34;&gt;Most users find it from either parked domains or word of mouth.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;b8fa47fe-beb6-4ae3-9e54-5c9e72d64e8f&#34; class=&#34;&#34;&gt;Insight: automate all the things.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;5ea653d1-f2b6-43d3-bd9d-14eed9789eba&#34; class=&#34;&#34;&gt;28. cronitor, $6k/mo revenue after 3 years&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;34caeb2d-bb9b-40d9-95cb-19d16c044324&#34; class=&#34;&#34;&gt;Source:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;4413d38f-3199-4589-a7db-08b7b44ca656&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://www.indiehackers.com/businesses/cronitor&#34;&gt;https://www.indiehackers.com/businesses/cronitor&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://news.ycombinator.com/item?id=13680922&#34;&gt;https://news.ycombinator.com/item?id=13680922&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;div id=&#34;2105744d-1519-42fa-800c-94637861b7b8&#34; class=&#34;&#34;&gt;Started in 2014, written part-time by 2 people. They wrote it because it solved a real problem they had at a startup he worked at.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;deb779d9-1f56-40de-8233-1d2d1ec1e964&#34; class=&#34;&#34;&gt;Marketing tactics:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;b703e321-51cc-4d43-9b4a-74d5334e533c&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;did ShowHN&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;answered questions on StackOverflow&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;added a link from a popular, open-source PHP library they had&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;created Stackshare page&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;submitted to startupli.st (site defunct)&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;submitted to “One Thing Well” website&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;wrote high-quality docs for SEO (topic-based articles on ‘how to use cronitor to do X’)&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;div id=&#34;0f33036f-5e6e-4cd4-9a1c-bba4e9ea8a34&#34; class=&#34;&#34;&gt;Raised prices after 6 months from $7/$20/$50 =&amp;gt; $10/$25/$50 and then $24/$70/$150.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;3279759e-ab67-42e9-97fc-b8bbba81d207&#34; class=&#34;&#34;&gt;29. bugmuncher, $4k/mo revenue&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;f9c37146-29ac-41b9-8517-b4f653b75054&#34; class=&#34;&#34;&gt;Source:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;fc4a18a0-e78b-48fa-8e81-a99bdd8adf97&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://news.ycombinator.com/item?id=13327868&#34;&gt;https://news.ycombinator.com/item?id=13327868&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://www.bugmuncher.com/blog/from-side-project-to-profitable-start-up-part-29/&#34;&gt;https://www.bugmuncher.com/blog/from-side-project-to-profitable-start-up-part-29/&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;div id=&#34;425d2275-08bd-4b25-abb9-9964efa762b9&#34; class=&#34;&#34;&gt;Web-based bug tracking software.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;64da2ae3-37ae-4c0f-8e1a-c87ca52ae936&#34; class=&#34;&#34;&gt;Started as a side project in 2010, went full time in Nov 2015, reached living wage in Nov 2016.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;91ea49dd-a652-4aa0-a702-485f4aefd367&#34; class=&#34;&#34;&gt;30. &lt;a href=&#34;https://info-beamer.com&#34;&gt;https://info-beamer.com&lt;/a&gt;, close to living wage&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;6d284d68-2e07-414f-8bda-2216e9a2ae48&#34; class=&#34;&#34;&gt;Source: &lt;a href=&#34;https://news.ycombinator.com/item?id=13514865&#34;&gt;https://news.ycombinator.com/item?id=13514865&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;2b14fa12-34b6-4d99-b356-23044ab7f40e&#34; class=&#34;&#34;&gt;Digital signage for Raspberry PI. Started as a side project, turned into profitable business.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;d8aafe96-0c27-4f1d-884a-47f198364b96&#34; class=&#34;&#34;&gt;31. Anonymous app, $5k/mo profit on $7k/mo revenue&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;dd5e57a0-90f8-4aa0-a28f-29d2b20a3117&#34; class=&#34;&#34;&gt;Source: &lt;a href=&#34;https://news.ycombinator.com/threads?id=gaeappthrowaway&#34;&gt;https://news.ycombinator.com/threads?id=gaeappthrowaway&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;8637e4dd-f596-4543-a0e8-2cf50ea9739c&#34; class=&#34;&#34;&gt;App hosted on App Engine, ~50 users paying between $30/mo and $500/mo. Analytics API.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;6ff06705-acf7-40b1-9cd1-5d642d40b4c8&#34; class=&#34;&#34;&gt;32. Wordpress theme, $5k/mo&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;981da92a-dbb3-4687-a565-4ed9248a18a3&#34; class=&#34;&#34;&gt;Source: &lt;a href=&#34;https://news.ycombinator.com/item?id=8107836&#34;&gt;https://news.ycombinator.com/item?id=8107836&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;089491ba-131c-4ff4-b512-d4dbd5c96829&#34; class=&#34;&#34;&gt;Sold via ThemeForest.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;1f0f9a8d-27df-4c68-9287-f7842846569d&#34; class=&#34;&#34;&gt;33. Radio Silence, main income&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;7224c949-938e-4aac-bc3b-5b6af6db66aa&#34; class=&#34;&#34;&gt;Source:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;bad88f3a-29e2-41d4-8b0f-ab1b3b6da07c&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://news.ycombinator.com/item?id=12722772&#34;&gt;https://news.ycombinator.com/item?id=12722772&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://news.ycombinator.com/item?id=12034558&#34;&gt;https://news.ycombinator.com/item?id=12034558&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;div id=&#34;b61bf6e4-0942-43fd-a4e7-20fd931d1aab&#34; class=&#34;&#34;&gt;Mac app (&lt;a href=&#34;https://radiosilenceapp.com/&#34;&gt;https://radiosilenceapp.com/&lt;/a&gt;) that evolved from side project to providing main income for the author. Sells for $9.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;908f0e33-1d07-4946-b35d-14ba61fe4867&#34; class=&#34;&#34;&gt;Author was able to quit his job.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;afe2ffc0-7682-43bc-9cd3-e8e5fe3acf8e&#34; class=&#34;&#34;&gt;Business tip: build related free app and host on the same domain.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;c030d5fe-0aec-4dec-9416-cbf90f11048c&#34; class=&#34;&#34;&gt;34. Ryan Clark, 10 Games, $3+ million over 10 years&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;225b8b5a-903f-4881-b088-93b860f9f2b5&#34; class=&#34;&#34;&gt;Source: &lt;a href=&#34;http://www.gamasutra.com/blogs/RyanClark/20150917/253842/What_Makes_an_Indie_Hit_How_to_Choose_the_Right_Design.php&#34;&gt;http://www.gamasutra.com/blogs/RyanClark/20150917/253842/What_Makes_an_Indie_Hit_How_to_Choose_the_Right_Design.php&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;514a1aa6-a960-4cf1-bc63-423519207bd2&#34; class=&#34;&#34;&gt;Working full time since 2004 on his games. Wrote 10 games in 11 years. 8 been profitable, 3 grossed more than $1M.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;9f964b51-a8f4-4927-ab8f-7599cc11bbe7&#34; class=&#34;&#34;&gt;35. &lt;a href=&#34;https://phantomjscloud.com&#34;&gt;https://PhantomJsCloud.com&lt;/a&gt;, ramen profitable for Seattle&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;62f8a69b-8e13-4949-ba75-9db7d5135e55&#34; class=&#34;&#34;&gt;Source: &lt;a href=&#34;https://news.ycombinator.com/item?id=13327835&#34;&gt;https://news.ycombinator.com/item?id=13327835&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;5143a6bd-6146-4c34-bbb2-beca96021425&#34; class=&#34;&#34;&gt;36. Desktop app, seating planning, $120k+/year&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;afec9c50-c0d2-4ecd-a86d-ecb8e8688d07&#34; class=&#34;&#34;&gt;Source:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;59b52049-2b3b-40a9-9376-77a0623a4984&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://successfulsoftware.net/2015/01/07/10-years-a-microisv/&#34;&gt;https://successfulsoftware.net/2015/01/07/10-years-a-microisv/&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://successfulsoftware.net/2013/11/06/lifestyle-programming/&#34;&gt;https://successfulsoftware.net/2013/11/06/lifestyle-programming/&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;div id=&#34;e2c78f99-943d-4c2f-8a9a-5da4c07e2e31&#34; class=&#34;&#34;&gt;2 desktop apps for Windows, written in C++.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;202cca6b-8ff6-4c3d-a7a4-130839603493&#34; class=&#34;&#34;&gt;Over 10 years, sold 40 thousand licenses of first desktop app, the cheapest is $30, which is at least $120k/year.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;6b127eac-4d68-4c92-9435-b130130c081d&#34; class=&#34;&#34;&gt;37. Desktop app in construction industry, making a living&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;c3cf193f-bb8e-45e5-8e10-376b857e449b&#34; class=&#34;&#34;&gt;Source: &lt;a href=&#34;https://news.ycombinator.com/item?id=11659140&#34;&gt;https://news.ycombinator.com/item?id=11659140&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;7daee09a-5edd-4ad8-91f1-316a13945ac7&#34; class=&#34;&#34;&gt;21-year old app for Windows, written in Delphi 5.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;6237fdd6-c4a1-4f36-80ca-debb134cabb0&#34; class=&#34;&#34;&gt;38. Video games, making a living&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;ba950e73-3fe0-4574-af4c-437cb4925709&#34; class=&#34;&#34;&gt;Source:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;9aa38941-8206-4e88-8c69-2310efe25102&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://news.ycombinator.com/item?id=11659283&#34;&gt;https://news.ycombinator.com/item?id=11659283&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://news.ycombinator.com/item?id=13838257&#34;&gt;https://news.ycombinator.com/item?id=13838257&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://news.ycombinator.com/item?id=13328138&#34;&gt;https://news.ycombinator.com/item?id=13328138&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://news.ycombinator.com/item?id=12066509&#34;&gt;https://news.ycombinator.com/item?id=12066509&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;div id=&#34;5e831518-80d4-440c-845a-5eecfa9b8eaa&#34; class=&#34;&#34;&gt;Multi-platform games written in C#, based on Unity game engine, released on Steam. Makes a game every X months.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;284a10a0-be17-45c7-a91c-aec19afc06e8&#34; class=&#34;&#34;&gt;39. Pinegrow Web Editor, comfortable living&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;d89a38e5-ea17-4aaf-af32-107beeb7d923&#34; class=&#34;&#34;&gt;Source:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;0a7e7343-d110-4b3f-a796-085c96771785&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://news.ycombinator.com/item?id=11659561&#34;&gt;https://news.ycombinator.com/item?id=11659561&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://medium.com/@mattront/pinegrow-year-in-review-2014-from-0-to-100k-fed4e7a05689&#34;&gt;https://medium.com/@mattront/pinegrow-year-in-review-2014-from-0-to-100k-fed4e7a05689&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;div id=&#34;f2be1f55-7369-4213-8277-ed8d6e615184&#34; class=&#34;&#34;&gt;Desktop web editor built with NWJS/Electron. Started by a single person, grew to 3 full-time people.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;77c1f25d-337d-4309-bfa3-281fc344a14d&#34; class=&#34;&#34;&gt;Launched in January 2014 after 2.5 years in development, sold $100k the first year. Sells for $49/$79.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;9b885717-2b59-4d04-a344-cc257a347f3f&#34; class=&#34;&#34;&gt;Marketing: website and asking for e-mail address when starting the trial to build e-mail database to send promotions to.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;e341ea03-c3be-40cc-b3ec-8baf0e517a1d&#34; class=&#34;&#34;&gt;Tried Carbon, Google and Reddit ads but was losing money on them.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;e88047ea-f02a-4709-9ea4-36c621e3ef60&#34; class=&#34;&#34;&gt;40. &lt;a href=&#34;https://ipinfo.io&#34;&gt;https://ipinfo.io&lt;/a&gt;, full-time job&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;d36a9414-81ea-4a2c-b0c0-10c8df8a8874&#34; class=&#34;&#34;&gt;Source:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;26a0048e-3936-4707-9324-25b43137404c&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://blog.ipinfo.io/api-side-project-to-250-million-requests-with-0-marketing-budget-bb0de01c01f6&#34;&gt;https://blog.ipinfo.io/api-side-project-to-250-million-requests-with-0-marketing-budget-bb0de01c01f6&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://news.ycombinator.com/item?id=14678473&#34;&gt;https://news.ycombinator.com/item?id=14678473&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;div id=&#34;f3f5af65-a92b-4c84-a1e5-531fac85a6f8&#34; class=&#34;&#34;&gt;Wrote and launched in a couple of hours as a response to StackOverflow question about. Posted as a response, forgot about it, it became popular so he implemented paid plans and started charging for it.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;391da935-8d3e-4ad9-ae0c-11a5105f6855&#34; class=&#34;&#34;&gt;41. &lt;a href=&#34;http://duetapp.com&#34;&gt;http://duetapp.com&lt;/a&gt;, $3-4k/mo&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;224f4991-15f5-4bbe-a1f9-c8a4b94124e8&#34; class=&#34;&#34;&gt;Self-hosted, web-based invoicing and project management app.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;0f519ba5-27de-4a6f-b52d-f812345367e6&#34; class=&#34;&#34;&gt;Source: &lt;a href=&#34;https://news.ycombinator.com/item?id=8630931&#34;&gt;https://news.ycombinator.com/item?id=8630931&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;299e7344-df53-4b92-aaee-b633d6ed4235&#34; class=&#34;&#34;&gt;42. &lt;a href=&#34;https://betterexplained.com&#34;&gt;https://betterexplained.com&lt;/a&gt;, $6k/mo after 10 years&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;1a05bae4-1b60-45de-9dcb-0067f7cd434b&#34; class=&#34;&#34;&gt;A website with math tutorials. Started in 2006. Content is free. Makes money selling ebooks (on Amazon kindle and directly from the website), amazon affiliate links and newsletter sponsorships.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;190fb068-3a54-4808-8b6f-6496780fde17&#34; class=&#34;&#34;&gt;Source:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;5c569800-1c3e-45ff-92b8-83c186ae716b&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://betterexplained.com/articles/life-lessons-10-years/&#34;&gt;https://betterexplained.com/articles/life-lessons-10-years/&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://news.ycombinator.com/item?id=8631204&#34;&gt;https://news.ycombinator.com/item?id=8631204&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://news.ycombinator.com/item?id=870015&#34;&gt;https://news.ycombinator.com/item?id=870015&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;h2 id=&#34;8437052b-3bd6-4007-8a58-4f921e9bf211&#34; class=&#34;&#34;&gt;43. Website with special-interest news, $!0-15k/mo&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;c1735149-1815-404c-8484-3e97e8198e8a&#34; class=&#34;&#34;&gt;Money from AdSense.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;3fc78b3b-4d5c-424c-8787-648c8b733f0c&#34; class=&#34;&#34;&gt;Source: &lt;a href=&#34;https://news.ycombinator.com/item?id=8630369&#34;&gt;https://news.ycombinator.com/item?id=8630369&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;1b34cd93-c73c-420d-b1d3-712f6dbe8b91&#34; class=&#34;&#34;&gt;44. $5.5k/mo from Udemy course&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;4957198a-da52-48c8-aca4-e9885a0d884c&#34; class=&#34;&#34;&gt;Source: &lt;a href=&#34;https://news.ycombinator.com/item?id=8631035&#34;&gt;https://news.ycombinator.com/item?id=8631035&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;da84a2a3-aefc-4a16-bc70-275d29c1b4c4&#34; class=&#34;&#34;&gt;45. Storemapper, $21k/mo&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;b4b6e93f-1cf8-455a-be29-4b2dbf118b92&#34; class=&#34;&#34;&gt;Source:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;682afe54-f99c-4f03-aafe-91f419fa4d31&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://www.indiehackers.com/businesses/storemapper&#34;&gt;https://www.indiehackers.com/businesses/storemapper&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;http://tylertringas.com/storemapper-bootstrapped-to-50000year-in-2-years-with-live-metrics/&#34;&gt;http://tylertringas.com/storemapper-bootstrapped-to-50000year-in-2-years-with-live-metrics/&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;h2 id=&#34;9a5e145a-7f89-4a7d-aa6b-b322c6aefd4e&#34; class=&#34;&#34;&gt;46. Brendan Dunn, $451k revenue in 2014 from several products&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;07e85d4c-e717-4d5a-acca-f45a93c997df&#34; class=&#34;&#34;&gt;Source:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;8ad8807e-b461-4782-b53d-6fe910056102&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://doubleyourfreelancing.com/annual-review-2014/&#34;&gt;https://doubleyourfreelancing.com/annual-review-2014/&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://doubleyourfreelancing.com/2016-year-review/&#34;&gt;https://doubleyourfreelancing.com/2016-year-review/&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://doubleyourfreelancing.com/2015-year-in-review/&#34;&gt;https://doubleyourfreelancing.com/2015-year-in-review/&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;div id=&#34;6d836b4c-9f24-4a58-9e7e-2cc992a1410a&#34; class=&#34;&#34;&gt;His income:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;9adbfa09-b8bd-4291-b8d4-dcc2b4da5dd1&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://doubleyourfreelancing.com/rate/&#34;&gt;https://doubleyourfreelancing.com/rate/&lt;/a&gt;, online course, $207k&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;http://doubleyourfreelancing.com/leads/&#34;&gt;http://doubleyourfreelancing.com/leads/&lt;/a&gt;, online course, $40k&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://planscope.io/&#34;&gt;https://planscope.io/&lt;/a&gt;, SaaS app, $71k&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;http://buildaconsultancy.com/&#34;&gt;http://buildaconsultancy.com/&lt;/a&gt;, live classes, $44k from 3 live classes&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;consulting and coaching, $89k from 6 weeks of consulting&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;h2 id=&#34;adde6d19-f5ca-427f-b6bb-d1ca5fa9f04c&#34; class=&#34;&#34;&gt;47. Cooking blog, $5-6k/mo&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;dbf1388d-e860-47dc-b0ff-f5016767456f&#34; class=&#34;&#34;&gt;He does the design/programming/marketing/monetization work behind &lt;a href=&#34;http://www.theyummylife.com&#34;&gt;http://www.theyummylife.com&lt;/a&gt;, his mother does the writing. Revenue from Amazon affiliates, ads and ebook.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;f4aa1e9a-404d-4665-89c8-2f9d7141d686&#34; class=&#34;&#34;&gt;Source:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;680b58c3-5808-4211-82be-244d2d252662&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://news.ycombinator.com/item?id=4467954&#34;&gt;https://news.ycombinator.com/item?id=4467954&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://www.lessannoyingcrm.com/blog/2012/04/259/How+I+monetized+a+blog+in+30+days%3A+what+worked%2C+and+what+didn%27t&#34;&gt;https://www.lessannoyingcrm.com/blog/2012/04/259/How+I+monetized+a+blog+in+30+days%3A+what+worked%2C+and+what+didn%27t&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;h2 id=&#34;0c4c5da3-c342-4e78-ae0f-5b394caa8da1&#34; class=&#34;&#34;&gt;48. Few dozens entertainment-related websites, $122k/mo&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;ccdbcc5a-123c-4c5b-8773-613c95f38257&#34; class=&#34;&#34;&gt;Revenue from AdSense, 8 million monthly uniques, after 6 years.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;01a978a6-e2f2-415a-8aba-1892f5e7db91&#34; class=&#34;&#34;&gt;Source: &lt;a href=&#34;https://news.ycombinator.com/item?id=4468067&#34;&gt;https://news.ycombinator.com/item?id=4468067&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;5000a905-ad62-497a-80af-cbe525a19698&#34; class=&#34;&#34;&gt;49. &lt;a href=&#34;https://www.tiki-toki.com/&#34;&gt;https://www.tiki-toki.com/&lt;/a&gt;, $5k/mo&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;c4ea9827-dc9f-46b7-8e8d-021df2767aa4&#34; class=&#34;&#34;&gt;Web and desktop app for creating pretty timelines. Makes money from premium accounts and selling desktop app.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;2b873ef0-8046-41f5-80a0-33d8e013001d&#34; class=&#34;&#34;&gt;Source: &lt;a href=&#34;https://news.ycombinator.com/item?id=4469672&#34;&gt;https://news.ycombinator.com/item?id=4469672&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;10ee4195-fd7a-476f-9ef8-e64a79a7c569&#34; class=&#34;&#34;&gt;50. Webapp in education space, $90k/m&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;746c4247-711b-465b-ad06-5281aede2229&#34; class=&#34;&#34;&gt;Revenue: AdSsense.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;959d0bdb-f3e4-4c72-b031-1c9c9f8f7297&#34; class=&#34;&#34;&gt;Source: &lt;a href=&#34;https://news.ycombinator.com/item?id=4468535&#34;&gt;https://news.ycombinator.com/item?id=4468535&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;7aa58c72-2e1e-493f-b13a-0a3a96b887f3&#34; class=&#34;&#34;&gt;51. Large web community, $90-110k/mo&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;68005f08-e544-4323-b02f-bc1c1599e571&#34; class=&#34;&#34;&gt;Revenu from subscription, adsense, other ad revenue, license and royalty revenue&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;b47ecaa7-2b2e-4094-8700-3593b973d90a&#34; class=&#34;&#34;&gt;Source: &lt;a href=&#34;https://news.ycombinator.com/item?id=2567487&#34;&gt;https://news.ycombinator.com/item?id=2567487&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;af2675bf-24e8-40af-b6a9-dbdd00f4c39c&#34; class=&#34;&#34;&gt;52. Zencaster, $12k/mo&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;6ef8602a-9cdd-46e1-93ce-a868278d1dc1&#34; class=&#34;&#34;&gt;Zencaster is a web-based tool that helps podcasters record their guests in studio quality.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;4298b1b7-4a21-428c-8388-ff5ab1e5c20a&#34; class=&#34;&#34;&gt;Source:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;4b910228-8cd9-49db-a7c7-445b62308e2f&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://www.indiehackers.com/businesses/zencastr&#34;&gt;https://www.indiehackers.com/businesses/zencastr&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://news.ycombinator.com/item?id=13533894&#34;&gt;https://news.ycombinator.com/item?id=13533894&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://news.ycombinator.com/item?id=13169704&#34;&gt;https://news.ycombinator.com/item?id=13169704&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;h2 id=&#34;c7461f37-5f97-4462-ba11-9e0289458b40&#34; class=&#34;&#34;&gt;53. Workflowy, $800k/year&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;e14d9f83-44d4-409d-9c66-1fdf5efc216e&#34; class=&#34;&#34;&gt;Jesse Patel learn how to program building https://workflowy.com/. After 9 months of working on it alone, he asked a friend he knew from college to join him. They got into YC to work on a different idea but pivoted back to Workflowy.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;cd656895-7768-4d77-a15b-b0820a6b3b50&#34; class=&#34;&#34;&gt;They started charging 2 years after they launched and got enough revenue to pay for living expenses.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;51bbd9f5-ebd0-4080-9a51-fe2ba345b4a5&#34; class=&#34;&#34;&gt;They have 100k paying users and $800k/year revenue.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;668148aa-71ab-4293-8f4d-ae25cf4dd031&#34; class=&#34;&#34;&gt;Source:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;6cd38cf2-a8ee-44e2-a6c2-93501b2db796&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://www.indiehackers.com/podcast/037-jesse-patel-of-workflowy&#34;&gt;https://www.indiehackers.com/podcast/037-jesse-patel-of-workflowy&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;&lt;/div&gt;</content>
  </entry>
  <entry>
   <title>Advanced command execution in Go with os/exec</title>
   <link href="https://blog.kowalczyk.info/article/wOYk/advanced-command-execution-in-go-with-osexec.html" rel="alternate"></link>
   <updated>2017-06-23T09:43:49Z</updated>
   <id>tag:blog.kowalczyk.info,2017-06-23:/article/wOYk/advanced-command-execution-in-go-with-osexec.html</id>
   <content type="html">&lt;p&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;notion-page&#34; id=&#34;8ede890d-08ce-444f-a52d-105ddea4d3e4&#34;&gt;&#xA;  &lt;div id=&#34;07114496-4589-4361-b32a-8fc2c2c3c896&#34; class=&#34;&#34;&gt;Go has excellent support for executing external programs. Let’s start at the beginning.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;1357f32f-256a-4d07-a68e-cae8d150e5c0&#34; class=&#34;&#34;&gt;In our examples we&amp;#x27;ll be running &lt;code&gt;ls -lah&lt;/code&gt; command as it produces an output. There is no &lt;code&gt;ls&lt;/code&gt; on Windows so you can change that to e.g. &lt;code&gt;tasklist&lt;/code&gt;.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;8db48922-3d84-46ce-8106-37ebba38cd02&#34; class=&#34;&#34;&gt;Running a command&#xA;  &lt;/h2&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;cmd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;exec&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Command&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;ls&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;-lah&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;runtime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;GOOS&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;windows&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;nx&#34;&gt;cmd&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;exec&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Command&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;tasklist&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;cmd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Run&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Fatalf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;cmd.Run() failed with %s\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;4b34a06b-15c6-4a14-aaad-d5cc4257306e&#34; class=&#34;&#34;&gt;Full example at &lt;a href=&#34;https://codeeval.dev/gist/8e5b7808e6a21f3d9d70e844646b55de&#34;&gt;https://codeeval.dev/gist/8e5b7808e6a21f3d9d70e844646b55de&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;249ef718-0536-4d19-ad45-0278f804adba&#34; class=&#34;&#34;&gt;If you run it, nothing seems to happen. Fear not, the command has actually been executed.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;3e346ec6-0590-4fbe-a50d-046a8fd6a874&#34; class=&#34;&#34;&gt;If we were running &lt;code&gt;ls -lah&lt;/code&gt; in the shell, the shell would copy programs&amp;#x27; stdout and stderr to console, so that we can see it.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;1af2f912-8324-49a3-98f9-8eba4a23a675&#34; class=&#34;&#34;&gt;We&amp;#x27;re executing the program via Go standard library function and by default stdout and stderr are discarded.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;8bb171ca-055e-40ac-8ef4-560dc604d1fc&#34; class=&#34;&#34;&gt;Running a command and showing output&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;226e841d-b182-489c-804f-1e20f4d71bed&#34; class=&#34;&#34;&gt;To let the human see the output, we can connect the output (&lt;code&gt;cmd.Stdout&lt;/code&gt; and &lt;code&gt;cmd.Stderr&lt;/code&gt;) of the program we&amp;#x27;re executing to &lt;code&gt;os.Stdout&lt;/code&gt; and &lt;code&gt;os.Stderr&lt;/code&gt;, which is the output of our program:  &#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;cmd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;exec&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Command&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;ls&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;-lah&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;cmd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Stdout&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Stdout&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;cmd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Stderr&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Stderr&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;cmd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Run&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Fatalf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;cmd.Run() failed with %s\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;a8e5f815-b2c9-4572-9816-4675fcfdfbdf&#34; class=&#34;&#34;&gt;Full example at &lt;a href=&#34;https://codeeval.dev/gist/08e519d3d47a6d82ca289ccff53e28b7&#34;&gt;https://codeeval.dev/gist/08e519d3d47a6d82ca289ccff53e28b7&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;fbffda81-3b76-40ea-814d-b0add203eac5&#34; class=&#34;&#34;&gt;&lt;code&gt;cmd.Stdout&lt;/code&gt; and &lt;code&gt;cmd.Stderr&lt;/code&gt; are declared as &lt;code&gt;io.Writer&lt;/code&gt; interface so we can set them to any type that implements &lt;code&gt;Write()&lt;/code&gt; method, like &lt;code&gt;os.File&lt;/code&gt; or an in-memory buffer &lt;code&gt;bytes.Buffer&lt;/code&gt;.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;7450795e-6411-41d3-a459-ffae85bac757&#34; class=&#34;&#34;&gt;&lt;code&gt;io.Reader&lt;/code&gt; and &lt;code&gt;io.Writer&lt;/code&gt; are very simple, yet very powerful, abstractions.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;fae6738d-4737-4d6f-b9fb-1d56e8957af3&#34; class=&#34;&#34;&gt;Running a command and capturing the output&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;330ec460-07e8-4409-a806-7a31b11c38b8&#34; class=&#34;&#34;&gt;The above examples allows human to see the output but sometimes we want to capture the output and analyze it:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;main&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;cmd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;exec&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Command&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;ls&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;-lah&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;out&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;cmd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;CombinedOutput&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Fatalf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;cmd.Run() failed with %s\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;fmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Printf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;combined out:\n%s\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;out&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;44000b71-6317-4048-b48a-f85290b8df4c&#34; class=&#34;&#34;&gt;Full example at &lt;a href=&#34;https://codeeval.dev/gist/4dfceac2f8229a077595c6cae84b3a56&#34;&gt;https://codeeval.dev/gist/4dfceac2f8229a077595c6cae84b3a56&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;b3f9b3d6-6505-4d62-a4fa-9c026a268fcc&#34; class=&#34;&#34;&gt;&lt;code&gt;CombinedOutput&lt;/code&gt; runs a command and returns combined stdout and stderr.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;930e8487-89ea-4346-ae23-d570cbe6f3b1&#34; class=&#34;&#34;&gt;Behind the scenes of &lt;code&gt;CombinedOutput&lt;/code&gt;&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;ad8eca2c-ef38-405b-9604-d66a48694a97&#34; class=&#34;&#34;&gt;The good thing about Go is that it&amp;#x27;s open source so we can peek at how a given functionality is implemented.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;2db3b51d-fa3e-42d0-9c70-fbd5c5416ae3&#34; class=&#34;&#34;&gt;Another good thing is that most of the code in the standard library is simple. Here&amp;#x27;s how &lt;code&gt;CombinedOutput&lt;/code&gt; is implemented:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;c&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Cmd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;CombinedOutput&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;([]&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;byte&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Stdout&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;errors&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;New&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;exec: Stdout already set&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Stderr&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;errors&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;New&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;exec: Stderr already set&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;b&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;bytes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Buffer&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Stdout&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;b&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Stderr&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;b&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Run&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Bytes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(),&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;cff732c1-b782-480c-add1-9e269f97b109&#34; class=&#34;&#34;&gt;Notice that it&amp;#x27;s almost as simple as our second example. Instead of setting &lt;code&gt;cmd.Stdout&lt;/code&gt; and &lt;code&gt;cmd.Stderr&lt;/code&gt; to standard output, we set them to a single in-memory buffer.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;40d2d275-6ff8-414c-98d4-83d15fe52870&#34; class=&#34;&#34;&gt;When program finishes, we returned everything written to that buffer.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;9aea3f3c-a004-4afd-9816-5c63ad20c172&#34; class=&#34;&#34;&gt;Don&amp;#x27;t be afraid to peruse the code of standard library.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;ca6597d7-e802-4d4e-9522-0e6197de19c9&#34; class=&#34;&#34;&gt;Capture stdout and stderr separately&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;8b4e28cb-cedb-4feb-a50f-34e72dcb0a6b&#34; class=&#34;&#34;&gt;What if you want to do the same but capture stdout and stderr separately?&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;main&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;cmd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;exec&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Command&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;ls&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;-lah&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;stdout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;stderr&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;bytes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Buffer&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;cmd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Stdout&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;stdout&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;cmd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Stderr&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;stderr&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;cmd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Run&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Fatalf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;cmd.Run() failed with %s\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;outStr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;errStr&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;stdout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Bytes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()),&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;stderr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Bytes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;fmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Printf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;out:\n%s\nerr:\n%s\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;outStr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;errStr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;4bff7ef7-8685-4cfc-bd76-df674abeecce&#34; class=&#34;&#34;&gt;Full example at &lt;a href=&#34;https://codeeval.dev/gist/8d5cf19aa518c45f6ea8e35ae5250cda&#34;&gt;https://codeeval.dev/gist/8d5cf19aa518c45f6ea8e35ae5250cda&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;5c2d3535-7e9a-47f4-9c00-9188f8e1d500&#34; class=&#34;&#34;&gt;Capture output but also show progress&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;0990f2db-dcb6-4990-91c8-3d4616371c8f&#34; class=&#34;&#34;&gt;What if the command takes a long time to finish?&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;49b26db8-4cfc-4b91-81d4-982e2d145581&#34; class=&#34;&#34;&gt;It would be nice to see its progress on the console as it happens in addition to capturing stdout/stderr.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;c2f0b8fa-a241-4701-be71-59c4a01fd702&#34; class=&#34;&#34;&gt;It’s a little bit more involved, but not terribly so.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;4a028f74-18be-4716-aa86-b355439d4c00&#34; class=&#34;&#34;&gt;First, a helper function that copies from reader to a writer and also captures copied data:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;copyAndCapture&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;w&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;io&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Writer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;r&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;io&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Reader&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;([]&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;byte&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;out&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;byte&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;buf&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;make&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;([]&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;byte&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1024&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1024&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Read&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;buf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[:])&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;n&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nx&#34;&gt;d&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;buf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[:&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nx&#34;&gt;out&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;append&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;out&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nx&#34;&gt;_&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;w&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Write&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;out&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;c1&#34;&gt;// Read returns io.EOF at the end of file, which is not an error for us&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;            &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;io&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;EOF&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;out&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;7a2014a2-c7df-483f-9297-8111a3fc2307&#34; class=&#34;&#34;&gt;Handling errors from &lt;code&gt;Read&lt;/code&gt; is subtle. An error &lt;code&gt;io.EOF&lt;/code&gt; means that we&amp;#x27;ve read everything. It&amp;#x27;s not an actual error so we turn &lt;code&gt;io.EOF&lt;/code&gt; into &lt;code&gt;nil&lt;/code&gt;.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;0074cd53-4dba-4453-b478-6f37f9ef3cdb&#34; class=&#34;&#34;&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;815368c2-5e03-41d3-8e4e-a772782f3af8&#34; class=&#34;&#34;&gt;The meat of the code:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;main&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;cmd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;exec&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Command&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;ls&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;-lah&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;stdout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;stderr&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;byte&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;errStdout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;errStderr&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;error&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;stdoutIn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;_&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;cmd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;StdoutPipe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;stderrIn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;_&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;cmd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;StderrPipe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;cmd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Start&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Fatalf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;cmd.Start() failed with &amp;#39;%s&amp;#39;\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;c1&#34;&gt;// cmd.Wait() should be called only after we finish reading&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&#x9;&lt;span class=&#34;c1&#34;&gt;// from stdoutIn and stderrIn.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&#x9;&lt;span class=&#34;c1&#34;&gt;// wg ensures that we finish&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&#x9;&lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;wg&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;sync&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;WaitGroup&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;wg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Add&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;k&#34;&gt;go&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;nx&#34;&gt;stdout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;errStdout&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;copyAndCapture&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Stdout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;stdoutIn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;nx&#34;&gt;wg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Done&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;p&#34;&gt;}()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;stderr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;errStderr&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;copyAndCapture&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Stderr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;stderrIn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;wg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Wait&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;cmd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Wait&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Fatalf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;cmd.Run() failed with %s\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;errStdout&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;errStderr&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Fatal&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;failed to capture stdout or stderr\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;outStr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;errStr&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;stdout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;stderr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;fmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Printf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;\nout:\n%s\nerr:\n%s\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;outStr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;errStr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;0df25a72-ea93-4ce3-8d0f-8ecd5272b78b&#34; class=&#34;&#34;&gt;Full example at &lt;a href=&#34;https://codeeval.dev/gist/7cca5f1526b2785e03febc16542b96ea&#34;&gt;https://codeeval.dev/gist/7cca5f1526b2785e03febc16542b96ea&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;30ae5908-09f6-437c-a915-531055dae7cc&#34; class=&#34;&#34;&gt;We have two outputs to copy. To avoid serializing them, we&amp;#x27;ll read one in a goroutine.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;32690c31-5bbb-4c2c-a0c2-ff670ab5ee2b&#34; class=&#34;&#34;&gt;As documentation of &lt;code&gt;StdoutPipe&lt;/code&gt; warns, &lt;code&gt;Wait&lt;/code&gt; will close the pipes when the process finishes. This might lead to losing some output if we haven&amp;#x27;t finished reading it.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;bd703bea-1bda-47d1-9d49-e15e97345b1c&#34; class=&#34;&#34;&gt;To prevent that we use &lt;code&gt;sync.WaitGroup&lt;/code&gt; to ensure that the gorutine handling &lt;code&gt;os.Stdout&lt;/code&gt; finishes reading before we call &lt;code&gt;cmd.Wait&lt;/code&gt;.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;424248bf-68cb-4818-b908-73ace72eb0c2&#34; class=&#34;&#34;&gt;I encourage you to read the implementation of &lt;code&gt;cmd.StdoutPipe&lt;/code&gt;. You&amp;#x27;ll be surprised by how short it is.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;009fb3c0-9bcf-4b51-ab0e-5a459d5c4fbb&#34; class=&#34;&#34;&gt;Capture output but also show progress #2&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;901f5f2a-7bb4-49ff-9197-1b1b5c0728a5&#34; class=&#34;&#34;&gt;Previous solution works but &lt;code&gt;copyAndCapture&lt;/code&gt; looks like we’re re-implementing &lt;code&gt;io.Copy&lt;/code&gt;. Thanks to Go’s use of interfaces we can re-use &lt;code&gt;io.Copy&lt;/code&gt;.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;478b1717-c394-4a0d-8800-da0bdb075f56&#34; class=&#34;&#34;&gt;We’ll write &lt;code&gt;CapturingPassThroughWriter&lt;/code&gt; struct implementing &lt;code&gt;io.Writer&lt;/code&gt; interface. It’ll capture everything that’s written to it and also write it to underlying &lt;code&gt;io.Writer&lt;/code&gt;.&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// CapturingPassThroughWriter is a writer that remembers&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// data written to it and passes it to w&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;type&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;CapturingPassThroughWriter&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;buf&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;bytes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Buffer&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;w&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;io&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Writer&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// NewCapturingPassThroughWriter creates new CapturingPassThroughWriter&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;NewCapturingPassThroughWriter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;w&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;io&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Writer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;CapturingPassThroughWriter&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;CapturingPassThroughWriter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;w&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;w&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;w&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;CapturingPassThroughWriter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;Write&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;d&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;byte&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;w&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;buf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Write&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;w&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;w&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Write&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Bytes returns bytes written to the writer&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;w&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;CapturingPassThroughWriter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;Bytes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;byte&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;w&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;buf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Bytes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;main&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;cmd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;exec&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Command&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;ls&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;-lah&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;errStdout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;errStderr&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;error&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;nx&#34;&gt;stdoutIn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;_&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;cmd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;StdoutPipe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;nx&#34;&gt;stderrIn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;_&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;cmd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;StderrPipe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;nx&#34;&gt;stdout&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;NewCapturingPassThroughWriter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Stdout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;nx&#34;&gt;stderr&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;NewCapturingPassThroughWriter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Stderr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;cmd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Start&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&#x9;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Fatalf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;cmd.Start() failed with &amp;#39;%s&amp;#39;\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;wg&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;sync&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;WaitGroup&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;nx&#34;&gt;wg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Add&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;k&#34;&gt;go&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&#x9;&lt;span class=&#34;nx&#34;&gt;_&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;errStdout&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;io&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Copy&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;stdout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;stdoutIn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&#x9;&lt;span class=&#34;nx&#34;&gt;wg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Done&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;p&#34;&gt;}()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;nx&#34;&gt;_&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;errStderr&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;io&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Copy&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;stderr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;stderrIn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;nx&#34;&gt;wg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Wait&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;cmd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Wait&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Fatalf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;cmd.Run() failed with %s\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;errStdout&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;errStderr&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Fatalf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;failed to capture stdout or stderr\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;outStr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;errStr&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;stdout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Bytes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()),&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;stderr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Bytes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;fmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Printf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;\nout:\n%s\nerr:\n%s\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;outStr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;errStr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;6ad283ca-7aa8-4946-9f16-3d539fa4d7f1&#34; class=&#34;&#34;&gt;Full example at &lt;a href=&#34;https://codeeval.dev/gist/2e4acc63f4f67eafc9c9a30776c01a22&#34;&gt;https://codeeval.dev/gist/2e4acc63f4f67eafc9c9a30776c01a22&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;9e022514-5381-40b1-8266-6dee52beb6e4&#34; class=&#34;&#34;&gt;Capture output but also show progress #3&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;1d034284-e1de-4b45-bb53-a1a296009351&#34; class=&#34;&#34;&gt;Turns out Go’s standard library implements &lt;a href=&#34;https://golang.org/pkg/io/#MultiWriter&#34;&gt;io.MultiWriter&lt;/a&gt;, which is a more generic version of &lt;code&gt;CapturingPassThroughWriter&lt;/code&gt;. Let’s use that instead:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;main&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;cmd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;exec&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Command&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;ls&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;-lah&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;runtime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;GOOS&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;windows&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;nx&#34;&gt;cmd&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;exec&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Command&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;tasklist&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;stdoutBuf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;stderrBuf&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;bytes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Buffer&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;cmd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Stdout&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;io&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;MultiWriter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Stdout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;stdoutBuf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;cmd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Stderr&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;io&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;MultiWriter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Stderr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;stderrBuf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;cmd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Run&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Fatalf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;cmd.Run() failed with %s\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;outStr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;errStr&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;stdoutBuf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Bytes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()),&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;stderrBuf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Bytes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;fmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Printf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;\nout:\n%s\nerr:\n%s\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;outStr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;errStr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;fb00d227-c213-42e5-ae62-b1ecffb73474&#34; class=&#34;&#34;&gt;Full example at &lt;a href=&#34;https://codeeval.dev/gist/2846af817271b7cb727962c8e556650b&#34;&gt;https://codeeval.dev/gist/0a1c2b1df8f675c86429768ae496c6f0&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;a240e550-3121-4174-a906-3f65fae2b893&#34; class=&#34;&#34;&gt;It’s good to be able to write the code ourselves, but it’s even better to know standard library well!&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;1371cb02-dbb9-41ea-a924-21602fd4bc32&#34; class=&#34;&#34;&gt;Writing to program’s stdin&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;9b018102-234d-4afa-8e42-b6d686743a5a&#34; class=&#34;&#34;&gt;We know how to read program’s stdout but we can also write to its stdin.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;b953c2a9-cdca-4c5a-b39b-39581e875977&#34; class=&#34;&#34;&gt;There is no Go library to do bzip2 compression (only decompression is available in standard library).&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;9b1c6406-4fc2-4548-b5fe-e597005a1bae&#34; class=&#34;&#34;&gt;We can use &lt;code&gt;bzip2&lt;/code&gt; to do the compression by: * writing the data to a temporary file * call &lt;code&gt;bzip2 -c ${file_in}&lt;/code&gt; and capture its stdout&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;c6b596cd-7b85-4c51-b12f-620e3e14a323&#34; class=&#34;&#34;&gt;It would be even better if we didn’t have to create a temporary file.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;46d64ccd-26f4-4674-a2cd-c18120475ce2&#34; class=&#34;&#34;&gt;Most compression programs accept data to compress/decompress on stdin.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;a6edeaf3-c247-44c6-a26c-dcf73d50f0f1&#34; class=&#34;&#34;&gt;To do that on command-line we would use the following command: &lt;code&gt;bzip2 -c &amp;lt;${file_in} &amp;gt;${file_out}&lt;/code&gt;.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;89e8e4d2-2982-43b5-9d4c-ec60ae8cc94b&#34; class=&#34;&#34;&gt;Here’s the same thing in Go:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// compress data using bzip2 without creating temporary files&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;bzipCompress&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;d&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;byte&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;([]&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;byte&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;out&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;bytes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Buffer&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// -c : compress&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// -9 : select the highest level of compresion&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;cmd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;exec&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Command&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;bzip2&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;-c&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;-9&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;cmd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Stdin&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;bytes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;NewBuffer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;cmd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Stdout&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;out&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;cmd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Run&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;out&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Bytes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(),&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;e2d047c6-0690-44a3-bd69-437eebcfcc13&#34; class=&#34;&#34;&gt;Full example at &lt;a href=&#34;https://codeeval.dev/gist/1d572914e41d8d88f9ec92230dae25b7&#34;&gt;https://codeeval.dev/gist/1d572914e41d8d88f9ec92230dae25b7&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;7a67b357-56d6-4229-adfa-b0556ab71118&#34; class=&#34;&#34;&gt;We can also call &lt;code&gt;cmd.StdinPipe()&lt;/code&gt;, which returns &lt;code&gt;io.WriteCloser&lt;/code&gt;. It’s more complicated but gives more control over writing.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;65d3adb0-6112-47bd-8da6-5599f412382c&#34; class=&#34;&#34;&gt;Changing environment of executed program&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;d04ce5f2-15bb-4c86-8ed0-62dda0c3b87e&#34; class=&#34;&#34;&gt;Things to know about environment variables in Go:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;38885ea9-f21a-4ea1-9bb0-a6bbbb06662f&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;&lt;code&gt;os.Environ()&lt;/code&gt; returns &lt;code&gt;[]string&lt;/code&gt; where each string is in form of &lt;code&gt;FOO=bar&lt;/code&gt;, where &lt;code&gt;FOO&lt;/code&gt; is the name of environment variable and &lt;code&gt;bar&lt;/code&gt; is the value &#xA;    &lt;/li&gt;&#xA;    &lt;li&gt; &lt;code&gt;os.Getenv(&amp;quot;FOO&amp;quot;)&lt;/code&gt; returns the value of environment variable &lt;code&gt;FOO&lt;/code&gt;.&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;div id=&#34;28a50d22-1b58-4223-becf-be424dfd4b1b&#34; class=&#34;&#34;&gt;Sometimes you need to modify the environment of the executed program.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;215567c4-17bb-4520-affc-3e88712363be&#34; class=&#34;&#34;&gt;Go supports that by setting &lt;code&gt;Env&lt;/code&gt; member of &lt;code&gt;exec.Cmd&lt;/code&gt;. &lt;code&gt;cmd.Env&lt;/code&gt; has the same format as &lt;code&gt;os.Environ()&lt;/code&gt;. &#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;b911bcd7-86c1-4bde-bb18-91502cee11cd&#34; class=&#34;&#34;&gt;If &lt;code&gt;Env&lt;/code&gt; is not set, the process inherits environment of the calling process.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;4747af45-5ec3-415f-8008-66d83cfd99b0&#34; class=&#34;&#34;&gt;Usually, you don’t want to construct a completely new environment from scratch but pass a modified version of an environment of the current process. Here&amp;#x27;s how to add a new variable:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;nx&#34;&gt;cmd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;exec&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Command&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;programToExecute&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;additionalEnv&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;FOO=bar&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;newEnv&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;append&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Environ&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(),&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;additionalEnv&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;cmd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Env&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;newEnv&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;out&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;cmd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;CombinedOutput&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Fatalf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;cmd.Run() failed with %s\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;fmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Printf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;%s&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;out&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;91aaf832-8a5e-46e8-a848-7711322128bf&#34; class=&#34;&#34;&gt;Full example &#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;be5c0016-56b2-4da2-8724-d216692e9886&#34; class=&#34;&#34;&gt;Things get more complicated if you want to delete an environment or to ensure you&amp;#x27;re not setting the same variable twice. Package &lt;a href=&#34;https://godoc.org/github.com/shurcooL/go/osutil#Environ&#34;&gt;shurcooL/go/osutil&lt;/a&gt;  offers an easier way of manipulating environment variables.&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;2ebc0f3d-a88e-40f0-84de-6c993e903ff1&#34; class=&#34;&#34;&gt;Check early that a program is installed&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;1035bcf7-6b37-441f-b62c-592857beb23f&#34; class=&#34;&#34;&gt;Imagine you wrote a program that takes a long time to run. At the end, you call executable &lt;code&gt;foo&lt;/code&gt; to perform an essential task.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;33cd90f1-cfec-4d49-b708-d7060fdddf9c&#34; class=&#34;&#34;&gt;If &lt;code&gt;foo&lt;/code&gt; executable is not present, the call will fail.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;72ae64d7-0e49-44dc-8f13-65620bf34cec&#34; class=&#34;&#34;&gt;It’s a good idea to detect lack of executable &lt;code&gt;foo&lt;/code&gt; at the beginning and fail early with descriptive error message.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;27a1a6ad-dbcd-4738-a927-6caa9a28361d&#34; class=&#34;&#34;&gt;You can do it using &lt;code&gt;exec.LookPath&lt;/code&gt;.&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;checkLsExists&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;exec&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;LookPath&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;ls&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;fmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Printf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;didn&amp;#39;t find &amp;#39;ls&amp;#39; executable\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;fmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Printf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;&amp;#39;ls&amp;#39; executable is in &amp;#39;%s&amp;#39;\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;d67aaa93-a374-4ab4-8efd-2f03c352d071&#34; class=&#34;&#34;&gt;Full example at &lt;a href=&#34;https://codeeval.dev/gist/2a80bc3e48d7f36f3299367660a0aedd&#34;&gt;https://codeeval.dev/gist/2a80bc3e48d7f36f3299367660a0aedd&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;d80b3225-046c-4c85-a480-4990e25288de&#34; class=&#34;&#34;&gt;Another way to check if a program exists is to try to execute it in a no-op mode (e.g. many programs support &lt;code&gt;--help&lt;/code&gt; option).&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;32bece2b-8c41-402c-9ea0-902ba88f23e3&#34; class=&#34;&#34;&gt;Code for this chapter: &lt;a href=&#34;https://github.com/kjk/go-cookbook/tree/master/advanced-exec&#34;&gt;https://github.com/kjk/go-cookbook/tree/master/advanced-exec&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;&lt;/div&gt;</content>
  </entry>
  <entry>
   <title>Generating good unique ids in Go</title>
   <link href="https://blog.kowalczyk.info/article/JyRZ/generating-good-unique-ids-in-go.html" rel="alternate"></link>
   <updated>2017-06-21T18:47:07Z</updated>
   <id>tag:blog.kowalczyk.info,2017-06-21:/article/JyRZ/generating-good-unique-ids-in-go.html</id>
   <content type="html">&lt;p&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;notion-page&#34; id=&#34;e6ca5046-8d98-4b5a-9e0c-883636419632&#34;&gt;&#xA;  &lt;div id=&#34;8d57ccdd-7ab4-4b3c-821c-2058a944ee62&#34; class=&#34;&#34;&gt;Imagine you’re writing a &lt;a href=&#34;https://github.com/kjk/quicknotes&#34;&gt;note taking&lt;/a&gt; application.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;3a76f551-2043-4b9a-bbd6-6c6b1a67c180&#34; class=&#34;&#34;&gt;Each note needs a unique id.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;02967b3d-c5ca-49af-a8d1-dea7c904afc1&#34; class=&#34;&#34;&gt;Generating unique ids is easy if you can coordinate.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;7c00e18b-9cf9-4262-9303-fd5c54aa5570&#34; class=&#34;&#34;&gt;The simplest way is to get database to do it: use &lt;code&gt;AUTOINCREMENT&lt;/code&gt; column and the database will generate unique id when you insert a new &lt;code&gt;note&lt;/code&gt; row into a table.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;bbb4d72a-ea70-41bc-9485-4e526b68dfa4&#34; class=&#34;&#34;&gt;What if you can’t coordinate?&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;629329b6-2edc-422f-966e-be576d3cee96&#34; class=&#34;&#34;&gt;For example, you want the app to also generate unique note id when offline, when it cannot talk to the database.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;ad3161e7-8ab4-4ebd-9160-e362e057a342&#34; class=&#34;&#34;&gt;The requirement of non-coordinated generation of unique ids comes up often in distributed systems.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;07333b40-e292-4e15-9f46-48fe390b6c7b&#34; class=&#34;&#34;&gt;A simple solution is to generate a random id. If you give it 16 bytes of randomness, the chances of generating the same random number are non-existent.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;2038df1b-7de3-436c-a441-c08d62c47fe6&#34; class=&#34;&#34;&gt;It’s such a common problem that over 30 years ago we created a standard for this called &lt;a href=&#34;https://en.wikipedia.org/wiki/Universally_unique_identifier&#34;&gt;UUID/GUID&lt;/a&gt;.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;3f65cd4a-ffa4-439e-90d0-093f42b126fe&#34; class=&#34;&#34;&gt;We can do better than GUID. A good random unique id:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;c34eca85-36aa-4de9-8d46-2b3a74f9caba&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;is unique; we can’t skip the basics&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;can be sorted by its string representation&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;is time-clustered i.e. ids generated at the same time are close to each other when sorted&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;string representation can be used as part of URL without escaping&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;the shorter, the better&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;div id=&#34;11f7c6de-b419-42d4-a9dc-7330875bfbfe&#34; class=&#34;&#34;&gt;There are few Go implementation of such id, following the same basic idea:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;48abed79-9afe-4d4b-99f9-0c85f788e059&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;use time as part of the id to achieve time-clustering&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;fill rest of the id with random data&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;encode as a string in a way that allows lexicographic sorting and is url-safe&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;div id=&#34;6f44d038-4d03-4217-85b6-29d4b66062a7&#34; class=&#34;&#34;&gt;Here are Go packages for generating unique id and how their ids look like in string format:&#xA;  &lt;/div&gt;&#xA;&lt;div id=&#34;fc1e9c3f-3358-448c-987d-9e459cd60c06&#34; class=&#34;collection-content&#34;&gt;&#xA;&lt;h4 class=&#34;collection-title&#34;&gt;GUID libraries&lt;/h4&gt;&#xA;&lt;table class=&#34;collection-content&#34;&gt;&#xA;&lt;thead&gt;&#xA;&lt;tr&gt;&#xA;&lt;th width=&#34;276&#34;&gt;Package&lt;/th&gt;&#xA;&lt;th width=&#34;268&#34;&gt;Id&lt;/th&gt;&#xA;&lt;th width=&#34;458&#34;&gt;Format&lt;/th&gt;&#xA;&lt;/tr&gt;&#xA;&lt;/thead&gt;&#xA;&lt;tbody&gt;&#xA;&lt;tr id=&#34;b9ab6038-561b-4964-af34-105fe878a422&#34;&gt;&#xA;&lt;td class=&#34;cell-title col-type-title&#34;&gt;&lt;a href=&#34;https://github.com/segmentio/ksuid&#34;&gt;github.com/segmentio/ksuid&lt;/a&gt;&lt;/td&gt;&#xA;&lt;td class=&#34;cell-.SqS col-type-text&#34;&gt;&lt;code&gt;&lt;strong&gt;0pPKHjWprnVxGH7dEsAoXX2YQvU&lt;/strong&gt;&lt;/code&gt;&lt;/td&gt;&#xA;&lt;td class=&#34;cell-Bvbb col-type-text&#34;&gt;4 bytes of time (seconds) + 16 random bytes&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&#xA;&lt;tr id=&#34;65fd961e-4c28-4702-b55c-111539dd7aee&#34;&gt;&#xA;&lt;td class=&#34;cell-title col-type-title&#34;&gt;&lt;a href=&#34;https://github.com/rs/xid&#34;&gt;github.com/rs/xid&lt;/a&gt;&lt;/td&gt;&#xA;&lt;td class=&#34;cell-.SqS col-type-text&#34;&gt;&lt;code&gt;&lt;strong&gt;b50vl5e54p1000fo3gh0&lt;/strong&gt;&lt;/code&gt;&lt;/td&gt;&#xA;&lt;td class=&#34;cell-Bvbb col-type-text&#34;&gt;4 bytes of time (seconds) + 3 byte machine id + 2 byte process id + 3 bytes random&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&#xA;&lt;tr id=&#34;f3fcbf35-374c-454a-a2e2-c2589264259e&#34;&gt;&#xA;&lt;td class=&#34;cell-title col-type-title&#34;&gt;&lt;a href=&#34;https://github.com/kjk/betterguid&#34;&gt;github.com/kjk/betterguid&lt;/a&gt;&lt;/td&gt;&#xA;&lt;td class=&#34;cell-.SqS col-type-text&#34;&gt;&lt;code&gt;&lt;strong&gt;-Kmdih_fs4ZZccpx2Hl1&lt;/strong&gt;&lt;/code&gt;&lt;/td&gt;&#xA;&lt;td class=&#34;cell-Bvbb col-type-text&#34;&gt;8 bytes of time (milliseconds) + 9 random bytes&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&#xA;&lt;tr id=&#34;c571ee07-af53-4998-99dc-841eeb5f9b57&#34;&gt;&#xA;&lt;td class=&#34;cell-title col-type-title&#34;&gt;&lt;a href=&#34;https://github.com/sony/sonyflake&#34;&gt;github.com/sony/sonyflake&lt;/a&gt;&lt;/td&gt;&#xA;&lt;td class=&#34;cell-.SqS col-type-text&#34;&gt;&lt;code&gt;&lt;strong&gt;20f8707d6000108&lt;/strong&gt;&lt;/code&gt;&lt;/td&gt;&#xA;&lt;td class=&#34;cell-Bvbb col-type-text&#34;&gt;~6 bytes of time (10 ms) + 1 byte sequence + 2 bytes machine id&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&#xA;&lt;tr id=&#34;6191d49c-d499-49a1-bd87-f9d2fd9c0420&#34;&gt;&#xA;&lt;td class=&#34;cell-title col-type-title&#34;&gt;&lt;a href=&#34;https://github.com/oklog/ulid&#34;&gt;github.com/oklog/ulid&lt;/a&gt;&lt;/td&gt;&#xA;&lt;td class=&#34;cell-.SqS col-type-text&#34;&gt;&lt;code&gt;&lt;strong&gt;01BJMVNPBBZC3E36FJTGVF0C4S&lt;/strong&gt;&lt;/code&gt;&lt;/td&gt;&#xA;&lt;td class=&#34;cell-Bvbb col-type-text&#34;&gt;6 bytes of time (milliseconds) + 8 bytes random&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&#xA;&lt;tr id=&#34;0d944e6b-188d-424a-b0cd-f4f095b6dcb8&#34;&gt;&#xA;&lt;td class=&#34;cell-title col-type-title&#34;&gt;&lt;a href=&#34;https://github.com/chilts/sid&#34;&gt;github.com/chilts/sid&lt;/a&gt;&lt;/td&gt;&#xA;&lt;td class=&#34;cell-.SqS col-type-text&#34;&gt;&lt;code&gt;&lt;strong&gt;1JADkqpWxPx-4qaWY47~FqI&lt;/strong&gt;&lt;/code&gt;&lt;/td&gt;&#xA;&lt;td class=&#34;cell-Bvbb col-type-text&#34;&gt;8 bytes of time (ns) + 8 random bytes&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&#xA;&lt;tr id=&#34;cf387202-bdab-4ecc-916a-d4c695316d12&#34;&gt;&#xA;&lt;td class=&#34;cell-title col-type-title&#34;&gt;&lt;a href=&#34;https://github.com/lithammer/shortuuid&#34;&gt;https://github.com/lithammer/shortuuid&lt;/a&gt;&lt;/td&gt;&#xA;&lt;td class=&#34;cell-.SqS col-type-text&#34;&gt;&lt;code&gt;dwRQAc68PhHQh4BUnrNsoS&lt;/code&gt;&lt;/td&gt;&#xA;&lt;td class=&#34;cell-Bvbb col-type-text&#34;&gt;UUIDv4 or v5, encoded in a more compact way&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&#xA;&lt;tr id=&#34;de5ad374-f230-4cd6-aea3-a8d305c6f69c&#34;&gt;&#xA;&lt;td class=&#34;cell-title col-type-title&#34;&gt;&lt;a href=&#34;https://github.com/satori/go.uuid&#34;&gt;github.com/satori/go.uuid&lt;/a&gt;&lt;/td&gt;&#xA;&lt;td class=&#34;cell-.SqS col-type-text&#34;&gt;&lt;code&gt;&lt;strong&gt;5b52d72c-82b3-4f8e-beb5-437a974842c&lt;/strong&gt;&lt;/code&gt;&lt;/td&gt;&#xA;&lt;td class=&#34;cell-Bvbb col-type-text&#34;&gt;UUIDv4 from &lt;a href=&#34;http://tools.ietf.org/html/rfc4122&#34;&gt;RFC 4112&lt;/a&gt; for comparison&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&#xA;&lt;tr id=&#34;d9ac6e8e-7c20-4f2c-85dd-67551384c06d&#34;&gt;&#xA;&lt;td class=&#34;cell-title col-type-title&#34;&gt;&lt;a href=&#34;https://github.com/google/uuid&#34;&gt;https://github.com/google/uuid&lt;/a&gt;&lt;/td&gt;&#xA;&lt;td class=&#34;cell-.SqS col-type-text&#34;&gt;&lt;code&gt;&lt;strong&gt;c01d7cf6-ec3f-47f0-9556-a5d6e9009a43&lt;/strong&gt;&lt;/code&gt;&lt;/td&gt;&#xA;&lt;td class=&#34;cell-Bvbb col-type-text&#34;&gt;UUIDv4&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&#xA;&lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;/div&gt;&#xA;  &lt;div id=&#34;f60d92e5-1df2-4e42-80ba-2abe0481e47b&#34; class=&#34;&#34;&gt;You can see how the values change over time by refreshing &lt;a href=&#34;/tools/generate-unique-id&#34;&gt;this test page&lt;/a&gt; a couple of times.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;8ea4dd5e-0b46-4b31-af18-37cff2cf6d9c&#34; class=&#34;&#34;&gt;How to generate unique ids using different libraries:&#xA;  &lt;/div&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;s&#34;&gt;&amp;#34;fmt&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;s&#34;&gt;&amp;#34;log&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;s&#34;&gt;&amp;#34;math/rand&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;s&#34;&gt;&amp;#34;time&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;s&#34;&gt;&amp;#34;github.com/chilts/sid&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;guuid&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;github.com/google/uuid&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;s&#34;&gt;&amp;#34;github.com/kjk/betterguid&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;s&#34;&gt;&amp;#34;github.com/lithammer/shortuuid&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;s&#34;&gt;&amp;#34;github.com/oklog/ulid&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;s&#34;&gt;&amp;#34;github.com/rs/xid&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;s&#34;&gt;&amp;#34;github.com/satori/go.uuid&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;s&#34;&gt;&amp;#34;github.com/segmentio/ksuid&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;s&#34;&gt;&amp;#34;github.com/sony/sonyflake&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;genShortUUID&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;shortuuid&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;New&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;fmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Printf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;github.com/lithammer/shortuuid: %s\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;genUUID&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;guuid&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;New&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;fmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Printf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;github.com/google/uuid:         %s\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;genXid&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;xid&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;New&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;fmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Printf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;github.com/rs/xid:              %s\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;genKsuid&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ksuid&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;New&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;fmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Printf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;github.com/segmentio/ksuid:     %s\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;genBetterGUID&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;betterguid&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;New&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;fmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Printf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;github.com/kjk/betterguid:      %s\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;genUlid&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;t&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Now&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;UTC&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;entropy&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;rand&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;New&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;rand&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;NewSource&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;t&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;UnixNano&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()))&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ulid&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;MustNew&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ulid&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Timestamp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;t&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;entropy&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;fmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Printf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;github.com/oklog/ulid:          %s\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;genSonyflake&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;flake&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;sonyflake&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;NewSonyflake&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;sonyflake&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Settings&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{})&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;flake&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;NextID&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Fatalf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;flake.NextID() failed with %s\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;c1&#34;&gt;// Note: this is base16, could shorten by encoding as base62 string&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;fmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Printf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;github.com/sony/sonyflake:      %x\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;genSid&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;sid&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;fmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Printf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;github.com/chilts/sid:          %s\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;genUUIDv4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;uuid&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;NewV4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Fatalf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;uuid.NewV4() failed with %s\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;fmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Printf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;github.com/satori/go.uuid:      %s\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;div id=&#34;243c7c29-9356-4c48-a618-40d3c34fa275&#34; class=&#34;&#34;&gt;Full example: &lt;a href=&#34;https://github.com/kjk/go-cookbook/blob/master/generate-unique-id/main.go&#34;&gt;generate-unique-id/main.go&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;h2 id=&#34;9dbddf01-2fe0-49e8-b18d-a537b15b8765&#34; class=&#34;&#34;&gt;Which one to use?&#xA;  &lt;/h2&gt;&#xA;  &lt;div id=&#34;e0c0ca81-7328-4eb9-91db-b9c716e51142&#34; class=&#34;&#34;&gt;All of them are good.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;c608a8a4-3bcc-44d8-bfd3-588d8dcf769f&#34; class=&#34;&#34;&gt;I would pick either &lt;code&gt;rs/xid&lt;/code&gt; or &lt;code&gt;segmentio/ksuid&lt;/code&gt;.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;3bccd9d7-4c58-4946-90b6-09e22a3011cf&#34; class=&#34;&#34;&gt;&lt;code&gt;oklog/ulid&lt;/code&gt; allows custom entropy (randomness) source but pays for that with complex API.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;219abb20-8c53-43eb-b715-0020e961aa03&#34; class=&#34;&#34;&gt;&lt;code&gt;sony/sonyflake&lt;/code&gt; is the smallest but also the least random. It’s based on Twitter’s design for generating IDs for tweets.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;deee7d8f-15ff-4517-a474-674accf41cb9&#34; class=&#34;&#34;&gt;For simplicity the example code serializes &lt;code&gt;sony/snoflake&lt;/code&gt; in base16. It would be even shorter in base62 encoding used by other libraries, but other libraries provide that out-of-the-box and for &lt;code&gt;sony/snoflake&lt;/code&gt; I would have to implement it myself.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;0c06138e-1f97-43e9-8098-1341a595fcaf&#34; class=&#34;&#34;&gt;The last one is UUID v4 from &lt;a href=&#34;http://tools.ietf.org/html/rfc4122&#34;&gt;RFC 4112&lt;/a&gt;, for comparison.&#xA;  &lt;/div&gt;&#xA;  &lt;div id=&#34;5c48bfac-2515-4e0d-ba74-ae1f59f738c2&#34; class=&#34;&#34;&gt;To learn more:&#xA;  &lt;/div&gt;&#xA;  &lt;ul id=&#34;d4efe545-4bed-47c9-a3c4-9d558e41b88c&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://segment.com/blog/a-brief-history-of-the-uuid/&#34;&gt;https://segment.com/blog/a-brief-history-of-the-uuid/&lt;/a&gt; : the history of ksuid&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://firebase.googleblog.com/2015/02/the-2120-ways-to-ensure-unique_68.html&#34;&gt;https://firebase.googleblog.com/2015/02/the-2120-ways-to-ensure-unique_68.html&lt;/a&gt; : inspiration for betterguid design&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;http://antoniomo.com/blog/2017/05/21/unique-ids-in-golang-part-1/&#34;&gt;http://antoniomo.com/blog/2017/05/21/unique-ids-in-golang-part-1/&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;http://antoniomo.com/blog/2017/05/28/unique-ids-in-golang-part-2/&#34;&gt;http://antoniomo.com/blog/2017/05/28/unique-ids-in-golang-part-2/&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;http://antoniomo.com/blog/2017/06/03/unique-ids-in-golang-part-3/&#34;&gt;http://antoniomo.com/blog/2017/06/03/unique-ids-in-golang-part-3/&lt;/a&gt;&#xA;    &lt;/li&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://blog.twitter.com/engineering/en_us/a/2010/announcing-snowflake.html&#34;&gt;https://blog.twitter.com/engineering/en_us/a/2010/announcing-snowflake.html&lt;/a&gt; : details of Twitter’s snowflake on which sonyflake is based&#xA;    &lt;/li&gt;&#xA;  &lt;/ul&gt;&#xA;  &lt;div id=&#34;c17471a0-9acf-41be-9b82-b082b2b62618&#34; class=&#34;&#34;&gt;Code for this chapter: &lt;a href=&#34;https://github.com/kjk/go-cookbook/tree/master/generate-unique-id&#34;&gt;https://github.com/kjk/go-cookbook/tree/master/generate-unique-id&lt;/a&gt;&#xA;  &lt;/div&gt;&#xA;&lt;/div&gt;</content>
  </entry>
 </feed>