<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.3.1">Jekyll</generator><link href="https://www.weinertworks.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://www.weinertworks.com/" rel="alternate" type="text/html" /><updated>2024-07-30T23:21:32-05:00</updated><id>https://www.weinertworks.com/feed.xml</id><title type="html">Weinert Works</title><subtitle>The opinions and code of Jim Weinert.</subtitle><entry><title type="html">That Went South Faster Than I Expected</title><link href="https://www.weinertworks.com/2022/09/30/that-went-south-faster-than-i-expected.html" rel="alternate" type="text/html" title="That Went South Faster Than I Expected" /><published>2022-09-30T00:58:55-05:00</published><updated>2022-09-30T00:58:55-05:00</updated><id>https://www.weinertworks.com/2022/09/30/that-went-south-faster-than-i-expected</id><content type="html" xml:base="https://www.weinertworks.com/2022/09/30/that-went-south-faster-than-i-expected.html"><![CDATA[<p>I could not throw a game that hard if I tried. I signed up for <a rel="me" href="https://hachyderm.io/@countingtoten">Mastodon</a> the same day they started banning posting to external services.</p>
      ]]></content><author><name>Jim</name></author><summary type="html"><![CDATA[I could not throw a game that hard if I tried. I signed up for Mastodon the same day they started banning posting to external services.]]></summary></entry><entry><title type="html">Were I the God-King of Twitter</title><link href="https://www.weinertworks.com/2022/09/30/twas-i-the-god-king-of-twitter.html" rel="alternate" type="text/html" title="Were I the God-King of Twitter" /><published>2022-09-30T00:58:55-05:00</published><updated>2022-09-30T00:58:55-05:00</updated><id>https://www.weinertworks.com/2022/09/30/twas-i-the-god-king-of-twitter</id><content type="html" xml:base="https://www.weinertworks.com/2022/09/30/twas-i-the-god-king-of-twitter.html"><![CDATA[<p>I don’t want to write this.</p>
    <p>I thought I could burn this draft once the Twitter acquisition was signed.</p>
    <p>…but Twitter is my favorite social media site. No where else can you interact with minor celebrities like you can on Twitter. I’ve gleaned more knowledge from random programmers I don’t follow on Twitter than I have from conferences.</p>
    <p>…and someone else’s bad opinions to monetize Twitter leaked from their drafts.</p>
    <p>First, Twitter needs to make more money off of their current user base. It’s been stuck at 300 million active users for as long as I can remember. They’re not growing their ad revenue anytime soon. One problem is most people are poor. Millenials haven’t recovered from the 2010 recession, and we’re already jumping into the next one. Twitter needs a way to monetize users while ensuring the experience mostly remains free.</p>
    <p>Every day I see public figures begging for a blue checkmark from Twitter support. People want something. It’s exclusive. It’s obvious. Charge for the blue checkmark. People give you proof of a government ID and $5 a month and they join the protected class. Twitter can still gift blue checks to journalists, politicians, and other users at-risk of impersonation.</p>
    <p>Give blue check users additional controls to limit how other users interact with their tweets and everyone will want to be a blue check. Imagine celebrities stopping harassment by only allowing blue checks to quote tweet, retweet, or reply to their tweets. Blocks mean something now: no more coordinated harassment campaigns chasing users off Twitter.</p>
    <p>A common form of spam is a blue check changing their username and profile picture to match a celebrity and then promote scams in their comments. I think most of these accounts are compromised. If they aren’t charging $5 and having a user’s government ID is a huge</p>
    <p>Twitter is the only social network that allows porn and nudity on the platform (RIP Tumblr). Gate nudity with the blue check mark. You get additional revenue from users making money on your platform <em>and</em> you get proof the user is of age.</p>
    <p>Next, segment your users.</p>
    <p>Celebrities and small businesses need multiple people to manage their account. Sell that. There are startups that offer that feature. Those startups should not exist. Account management should be part of your core product. I don’t know what’s reasonable, $30/month for five users to own an account?</p>
    <p>Do cable companies and airlines pay for their Twitter accounts? They should, at least $100k/year. If they’re using it as a support platform, they should pay. Add an Enterprise tier with everything they need, more users, a ticket queuing system, new APIs to connect it to Front, whatever they need.</p>
    <p>Lastly, the best innovations have come from outside the company. Give users better API access to Twitter and Twitter features. Let users generate their own API tokens and do whatever they want with it, as long as they’re paying for the blue checkmark. If they’re already paying you, do you care if they’re using a third-party Twitter client?</p>
    <p>I use a third party client. I wish I had polls and spaces, but the experience is too good to switch. Plus, I don’t see ads! Open up the API. Create a new version of the API every year. Developer momentum will not be slowed by supporting the last two years’ versions.</p>
    <p>So yeah, make me the god king of Twitter. I don’t want the job, but there’s no other tech company I care about this much.</p>
    ]]></content><author><name>Jim</name></author><summary type="html"><![CDATA[I don’t want to write this.]]></summary></entry><entry><title type="html">Keep Ruby Weird 2018</title><link href="https://www.weinertworks.com/2019/03/18/keep-ruby-weird-2018.html" rel="alternate" type="text/html" title="Keep Ruby Weird 2018" /><published>2019-03-18T20:20:35-05:00</published><updated>2019-03-18T20:20:35-05:00</updated><id>https://www.weinertworks.com/2019/03/18/keep-ruby-weird-2018</id><content type="html" xml:base="https://www.weinertworks.com/2019/03/18/keep-ruby-weird-2018.html"><![CDATA[<blockquote class="twitter-tweet" data-lang="en">
    <p lang="en" dir="ltr">Play iterated games. All the returns in life, whether in wealth, relationships, or knowledge, come from compound interest.</p>
    &mdash; Naval (@naval) <a href="https://twitter.com/naval/status/1002103908947263488?ref_src=twsrc%5Etfw">May 31, 2018</a></blockquote>
  <script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
  <p>Programming (or writing or making art) is knowledge wealth building. Practicing your craft is compounding interest, and the best practice is to have a side project.</p>
  <p>Working on an application from inception to deployment teaches you so much more than you would learn at work. Your work projects are too big for you to master every part of the application. When the application is small, you can take the time to master every part from the authentication to the deployment.</p>
  <p>Keep Ruby Weird is a celebration of side projects that delight. None of these talks will make you a millionaire, but you’ll learn something. Maybe that something will help you on a future project. Maybe it will encourage you to start your own absurd side project.</p>
  <h2 id="opening-keynote---yukihiro-matz-matsumoto">Opening Keynote - Yukihiro ‘Matz’ Matsumoto</h2>
  <p><a href="https://confreaks.tv/videos/keeprubyweird2018-opening-keynote">https://confreaks.tv/videos/keeprubyweird2018-opening-keynote</a></p>
  <p>2018 is the last Keep Ruby Weird, and they managed to get a talk from Matz. Did they have excess budget and need to spend every penny? Did they troll us by having a very normal conference talk at their weirdo conference? Who cares? You get to watch a talk from the creator of Ruby!</p>
  <p>Matz talks about how method hash arguments work in Ruby today and considers how they will work in the future. Nothing is set in stone, and they’re still taking comments/suggestions. If you want to learn about some weird edge cases for method hash arguments, this talk is for you.</p>
  <h2 id="distributed-fizz-buzz-passing-the-microservices-interview">Distributed Fizz Buzz: Passing the Microservices Interview</h2>
  <p>Skip it! This talk is an advertisement for the actual talk, which is a three day course on microservices.</p>
  <h2 id="the-teenage-mutant-ninja-turtles-guide-to-color-theory---louisa-barrett">The Teenage Mutant Ninja Turtles Guide to Color Theory - Louisa Barrett</h2>
  <p><a href="https://confreaks.tv/videos/keeprubyweird2018-the-teenage-mutant-ninja-turtles-guide-to-color-theory">https://confreaks.tv/videos/keeprubyweird2018-the-teenage-mutant-ninja-turtles-guide-to-color-theory</a></p>
  <p>Louisa explains the basics of color theory via Ninja Turtles. If you use colors but don’t know anything about color groups, what emotions are associated with which colors, how to get color groups from the color wheel, and what properties make up a color then this talk is for you.</p>
  <p>It’s a very introductory talk, but I still learned something. I had no idea how the properties of color worked.</p>
  <h2 id="game-show-intermission">Game Show (Intermission)</h2>
  <p><a href="https://confreaks.tv/videos/keeprubyweird2018-game-show">https://confreaks.tv/videos/keeprubyweird2018-game-show</a></p>
  <p>Skip it! The Game Show was fun in person, but it was filler. It’s not going to be as fun online.</p>
  <h2 id="using-psql-to-watch-star-wars-and-other-silly-things---will-leinweber">Using psql to \watch Star Wars And other silly things! - Will Leinweber</h2>
  <p><a href="https://confreaks.tv/videos/keeprubyweird2018-using-psql-to-watch-star-wars-and-other-silly-things">https://confreaks.tv/videos/keeprubyweird2018-using-psql-to-watch-star-wars-and-other-silly-things</a></p>
  <p>Will uses the ascii version of Star Wars to explain the Postgresql \watch command. The \watch command is like a tail for your query. It reruns the query at some specified interval and displays the output. Combine \watch with a Postgresql function and a counter, and you’re watching Star Wars!</p>
  <p>Then comes the step up. Will uses ffmpeg to render Star Wars as emoji characters. He even hooks up his Mac’s webcam to ffmpeg to render live video of himself as emoji. It’s an utterly delightful moment.</p>
  <p>If you like Will’s talk, check out <a href="https://confreaks.tv/videos/keeprubyweird2015-a-collection-of-fun-with-ruby-and-friends">his talk from Keep Ruby Weird 2015</a>. They’re both must sees.</p>
  <h2 id="cats-the-musical-algorithmic-song-meow-ification---beth-haubert">Cats, The Musical! Algorithmic Song Meow-ification - Beth Haubert</h2>
  <p><a href="https://confreaks.tv/videos/keeprubyweird2018-cats-the-musical-algorithmic-song-meow-ification">https://confreaks.tv/videos/keeprubyweird2018-cats-the-musical-algorithmic-song-meow-ification</a></p>
  <p>Beth sets out to write an application that takes a song as input and returns the same melody but sung by cat meows.</p>
  <p>Extracting the melody from a song automatically is really hard, and she doesn’t get that part working. Instead she outsources the task to an online melody extractor. Even that extractor is terrible.</p>
  <p>This is a good talk to watch Beth encounter setbacks and work around them. There are compromises and work arounds, but she gets it working. The payoff? A cat rendition of the Game of Thrones theme.</p>
  <h2 id="using-ruby-to-build-a-modern-memex---andrew-louis">Using Ruby to build a modern Memex! - Andrew Louis</h2>
  <p><a href="https://confreaks.tv/videos/keeprubyweird2018-using-ruby-to-build-a-modern-memex">https://confreaks.tv/videos/keeprubyweird2018-using-ruby-to-build-a-modern-memex</a></p>
  <p>Andrew obsessively catalogues every detail about his life. To categorize that information, he creates a <a href="https://en.wikipedia.org/wiki/Memex">Memex</a>. The Memex was a thought experiment from the 1940’s by Vannevar Bush. He wanted to organize data by associations. For Andrew, that means being able to look up every podcast he listened to on a road trip three years ago.</p>
  <p>I want this. I want a personalized search engine for my life.</p>
  <p>The most impressive part is he’s using Ruby on Rails and Postgresql. I expected him to be using a <a href="http://titan.thinkaurelius.com">Graph Database</a>, which is almost a Memex. Graph Databases are super super new, meaning they’re all still pre-1.0 and unreliable. This is a friendly reminder that Rails and Postgresql are still incredibly powerful.</p>
  <p>Andrew wants to share his Memex but doesn’t know how. He’s unsure about opensourcing it. He never mentions it, but users of opensource have entitlement issues. There is an emotional cost to fielding support and feature requests. The <a href="https://marketplace.digitalocean.com">DigitalOcean Marketplace</a> would be perfect for his Memex. He could build a 1-click, only need to support one version of the OS, Rails, and Postgresql, and share it with people without outsourcing it.</p>
  <h2 id="transcendental-programming-in-ruby---yusuke-endoh">Transcendental Programming in Ruby - Yusuke Endoh</h2>
  <p><a href="https://confreaks.tv/videos/keeprubyweird2018-transcendental-programming-in-ruby">https://confreaks.tv/videos/keeprubyweird2018-transcendental-programming-in-ruby</a></p>
  <p>Yusuke is an MRI contributor who’s side project is <a href="https://en.wikipedia.org/wiki/Quine_(computing)">quines</a>. A quine is program that prints its own sourcecode. If you can speak Japanese, Yusuke wrote <a href="https://www.amazon.co.jp/あなたの知らない超絶技巧プログラミングの世界-遠藤-侑介/dp/4774176435">a book</a> about them.</p>
  <p>Quines are simple to write in Ruby. This example is from the wikipedia article on Quines. Represent your program as a string and eval that string.</p>
  <figure class="highlight">
    <pre><code class="language-ruby" data-lang="ruby"><span class="nb">eval</span> <span class="n">s</span><span class="o">=</span><span class="s2">"print 'eval s=';p s"</span></code></pre>
  </figure>
  <p>Simple, right? Nope, nope, nope. Yusuke shows off some absurd quines.</p>
  <p>This talk was on the weirder side of Keep Ruby Weird. Definitely check it out.</p>
  <h2 id="closing-keynote---avdi-grimm">Closing Keynote - Avdi Grimm</h2>
  <p><a href="https://confreaks.tv/videos/keeprubyweird2018-closing-keynote">https://confreaks.tv/videos/keeprubyweird2018-closing-keynote</a></p>
  <p>Avdi Grimm, of <a href="https://www.rubytapas.com">Ruby Tapas</a> fame, starts his keynote as a discussion about the roots of Object Oriented Programming and how the world misunderstood objects and methods in OOP. Alan Kay’s original description of OOP is more similar to the <a href="http://dspace.mit.edu/bitstream/handle/1721.1/41962/AI_WP_134A.pdf">Actor model</a> and Functional Programming than it is to modern OOP. Avdi iterates all of the problems with object messaging and how it’s broken.</p>
  <p>Avdi realizes that the same problems with OOP messaging apply to his life. He’s so laser focused on accomplishing a checklist of goals, that he postpones happiness and doesn’t enjoy the journey.</p>
  <p>The second half of his keynote is about being present and enjoying the moment. That focusing on the a goal at the expense of your day to day life isn’t healthy.</p>
  <p>Be content that your goals will take a decade to achieve. Savor the journey, especially with your family.</p>
  <p>Oh, and take more selfies.</p>
  <p>😎</p>
  ]]></content><author><name>Jim</name></author><summary type="html"><![CDATA[Play iterated games. All the returns in life, whether in wealth, relationships, or knowledge, come from compound interest.&mdash; Naval (@naval) May 31, 2018]]></summary></entry><entry><title type="html">Use Pointers in Golang Arrays</title><link href="https://www.weinertworks.com/2018/12/31/golang-use-pointers-in-slices.html" rel="alternate" type="text/html" title="Use Pointers in Golang Arrays" /><published>2018-12-31T00:59:17-06:00</published><updated>2018-12-31T00:59:17-06:00</updated><id>https://www.weinertworks.com/2018/12/31/golang-use-pointers-in-slices</id><content type="html" xml:base="https://www.weinertworks.com/2018/12/31/golang-use-pointers-in-slices.html"><![CDATA[<p>In Go when I have an array, or slice, of structs, I prefer to always make that an array of pointers to the structs. You might want to do this so you’re not copying the entire struct every time you append to the slice. Using pointers is nanoseconds faster, but the real reason is developer ergonomics! It’s cumbersome to make changes to an array of structs.</p>
  <p>For example if you have the following struct.</p>
  <figure class="highlight">
    <pre><code class="language-go" data-lang="go"><span class="k">type</span> <span class="n">Object</span> <span class="k">struct</span> <span class="p">{</span>
	<span class="n">Value</span> <span class="kt">int</span>
<span class="p">}</span>

<span class="k">func</span> <span class="p">(</span><span class="n">o</span> <span class="o">*</span><span class="n">Object</span><span class="p">)</span> <span class="n">Double</span><span class="p">()</span> <span class="p">{</span>
	<span class="n">o</span><span class="o">.</span><span class="n">Value</span> <span class="o">*=</span> <span class="m">2</span>
<span class="p">}</span>

<span class="k">func</span> <span class="p">(</span><span class="n">o</span> <span class="o">*</span><span class="n">Object</span><span class="p">)</span> <span class="n">String</span><span class="p">()</span> <span class="kt">string</span> <span class="p">{</span>
	<span class="k">return</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"{%d}"</span><span class="p">,</span> <span class="n">o</span><span class="o">.</span><span class="n">Value</span><span class="p">)</span>
<span class="p">}</span></code></pre>
  </figure>
  <p>If you want to change the value of the contents of the array, you would instinctively write something like this.</p>
  <figure class="highlight">
    <pre><code class="language-go" data-lang="go"><span class="k">package</span> <span class="n">main</span>

<span class="k">import</span> <span class="p">(</span>
	<span class="s">"fmt"</span>
<span class="p">)</span>

<span class="k">func</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
	<span class="n">array</span> <span class="o">:=</span> <span class="p">[]</span><span class="n">Object</span><span class="p">{</span>
		<span class="n">Object</span><span class="p">{</span>
			<span class="n">Value</span><span class="o">:</span> <span class="m">0</span><span class="p">,</span>
		<span class="p">},</span>
		<span class="n">Object</span><span class="p">{</span>
			<span class="n">Value</span><span class="o">:</span> <span class="m">1</span><span class="p">,</span>
		<span class="p">},</span>
		<span class="n">Object</span><span class="p">{</span>
			<span class="n">Value</span><span class="o">:</span> <span class="m">2</span><span class="p">,</span>
		<span class="p">},</span>
	<span class="p">}</span>

	<span class="n">fmt</span><span class="o">.</span><span class="n">Println</span><span class="p">(</span><span class="n">array</span><span class="p">)</span>

	<span class="k">for</span> <span class="n">_</span><span class="p">,</span> <span class="n">item</span> <span class="o">:=</span> <span class="k">range</span> <span class="n">array</span> <span class="p">{</span>
		<span class="n">item</span><span class="o">.</span><span class="n">Double</span><span class="p">()</span>
	<span class="p">}</span>

	<span class="n">fmt</span><span class="o">.</span><span class="n">Println</span><span class="p">(</span><span class="n">array</span><span class="p">)</span>
<span class="p">}</span></code></pre>
  </figure>
  <p>It won’t work. When you iterate over array, item is a copy of the struct that’s in the array. Any changes we make to it are scoped to our for loop.</p>
  <p>If you want to make changes to the array, you need to assign the updated struct to its index in the array.</p>
  <figure class="highlight">
    <pre><code class="language-go" data-lang="go"><span class="k">for</span> <span class="n">i</span> <span class="o">:=</span> <span class="m">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="nb">len</span><span class="p">(</span><span class="n">array</span><span class="p">);</span> <span class="n">i</span><span class="o">++</span> <span class="p">{</span>
	<span class="n">item</span> <span class="o">:=</span> <span class="n">array</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
	<span class="n">item</span><span class="o">.</span><span class="n">Double</span><span class="p">()</span>
	<span class="n">array</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">item</span>
<span class="p">}</span>

<span class="n">fmt</span><span class="o">.</span><span class="n">Println</span><span class="p">(</span><span class="n">array</span><span class="p">)</span></code></pre>
  </figure>
  <p>If you use pointers in your arrays, you don’t have this problem. The item variable is a pointer, and you can manipulate the struct directly.</p>
  <figure class="highlight">
    <pre><code class="language-go" data-lang="go"><span class="n">pointerArray</span> <span class="o">:=</span> <span class="p">[]</span><span class="o">*</span><span class="n">Object</span><span class="p">{</span>
	<span class="o">&amp;</span><span class="n">Object</span><span class="p">{</span>
		<span class="n">Value</span><span class="o">:</span> <span class="m">0</span><span class="p">,</span>
	<span class="p">},</span>
	<span class="o">&amp;</span><span class="n">Object</span><span class="p">{</span>
		<span class="n">Value</span><span class="o">:</span> <span class="m">1</span><span class="p">,</span>
	<span class="p">},</span>
	<span class="o">&amp;</span><span class="n">Object</span><span class="p">{</span>
		<span class="n">Value</span><span class="o">:</span> <span class="m">2</span><span class="p">,</span>
	<span class="p">},</span>
<span class="p">}</span>

<span class="n">fmt</span><span class="o">.</span><span class="n">Println</span><span class="p">(</span><span class="n">pointerArray</span><span class="p">)</span>

<span class="k">for</span> <span class="n">_</span><span class="p">,</span> <span class="n">item</span> <span class="o">:=</span> <span class="k">range</span> <span class="n">pointerArray</span> <span class="p">{</span>
	<span class="n">item</span><span class="o">.</span><span class="n">Double</span><span class="p">()</span>
<span class="p">}</span>

<span class="n">fmt</span><span class="o">.</span><span class="n">Println</span><span class="p">(</span><span class="n">pointerArray</span><span class="p">)</span></code></pre>
  </figure>
  <p>Here’s a <a href="https://play.golang.org/p/9zaqm4kcwku">Go Playground</a> to play around with it.</p>
  ]]></content><author><name>Jim</name></author><summary type="html"><![CDATA[In Go when I have an array, or slice, of structs, I prefer to always make that an array of pointers to the structs. You might want to do this so you’re not copying the entire struct every time you append to the slice. Using pointers is nanoseconds faster, but the real reason is developer ergonomics! It’s cumbersome to make changes to an array of structs.]]></summary></entry><entry><title type="html">Better Golang Errors With errors.Wrap</title><link href="https://www.weinertworks.com/2018/11/27/better-golang-errors-with-errors-wrap.html" rel="alternate" type="text/html" title="Better Golang Errors With errors.Wrap" /><published>2018-11-27T23:13:59-06:00</published><updated>2018-11-27T23:13:59-06:00</updated><id>https://www.weinertworks.com/2018/11/27/better-golang-errors-with-errors-wrap</id><content type="html" xml:base="https://www.weinertworks.com/2018/11/27/better-golang-errors-with-errors-wrap.html"><![CDATA[<p><a href="#bgewe-aside1">Libraries shouldn’t log</a>. Logging takes resources and should occur as rarely as possible. If an error occurs deep in your program, you don’t want to immediately log it, bubble the error up the stack, and then log the same error again but with additional context. Thus, you should try to log as close to the UI as possible. If you’re writing a web service, only log in the handler before you send a response. The code for accessing the database is a dependency and should be in its own package. We treat it like a library and only concern ourselves with its API.</p>
  <p>But… having more context would be helpful. Even though it’s wasteful, logging an error twice reveals more about the current code path when the bug occurred.</p>
  <p>One way to provide more context is to have very specific messages for each log line that are never repeated. For example…</p>
  <figure class="highlight">
    <pre><code class="language-go" data-lang="go"><span class="k">const</span> <span class="p">(</span>
  <span class="n">UnauthorizedUserLogMsg</span>                <span class="kt">string</span> <span class="o">=</span> <span class="s">"Unauthorized request"</span>
  <span class="n">UnmarshallingRequestErrorLogMsg</span>       <span class="kt">string</span> <span class="o">=</span> <span class="s">"Error unmarshalling request body"</span>
  <span class="n">JSONMarshallingResponseErrorLogMsg</span>    <span class="kt">string</span> <span class="o">=</span> <span class="s">"Error marshalling json response body"</span>
  <span class="n">JSONAPIMarshallingResponseErrorLogMsg</span> <span class="kt">string</span> <span class="o">=</span> <span class="s">"Error marshalling jsonapi response body"</span>
  <span class="n">DatastoreErrorLogMsg</span>                  <span class="kt">string</span> <span class="o">=</span> <span class="s">"Error returned by the datastore"</span>
<span class="p">)</span>

<span class="k">func</span> <span class="p">(</span><span class="n">handler</span> <span class="o">*</span><span class="n">Handler</span><span class="p">)</span> <span class="n">CompaniesGetAll</span><span class="p">(</span><span class="n">resp</span> <span class="n">http</span><span class="o">.</span><span class="n">ResponseWriter</span><span class="p">,</span> <span class="n">req</span> <span class="o">*</span><span class="n">http</span><span class="o">.</span><span class="n">Request</span><span class="p">)</span> <span class="p">{</span>
  <span class="n">companies</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">handler</span><span class="o">.</span><span class="n">Datastore</span><span class="o">.</span><span class="n">CompaniesGetAll</span><span class="p">(</span><span class="n">limit</span><span class="p">,</span> <span class="n">offset</span><span class="p">)</span>
  <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
    <span class="n">handler</span><span class="o">.</span><span class="n">Logger</span><span class="o">.</span><span class="n">Error</span><span class="p">()</span><span class="o">.</span><span class="n">Err</span><span class="p">(</span><span class="n">err</span><span class="p">)</span><span class="o">.</span><span class="n">Msg</span><span class="p">(</span><span class="n">DatastoreErrorLogMsg</span><span class="p">)</span>
    <span class="n">http</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">rw</span><span class="p">,</span> <span class="s">"Internal Server Error"</span><span class="p">,</span> <span class="n">http</span><span class="o">.</span><span class="n">StatusInternalServerError</span><span class="p">)</span>
    <span class="k">return</span>
  <span class="p">}</span>

  <span class="c">// Handle a normal response</span>
  <span class="c">// ...</span>
<span class="p">}</span></code></pre>
  </figure>
  <p>If the Datastore package is returning a Postgresql error, that error could have come from any request to the database in CompaniesGetAll, and the error messages from the Go Postgresql adapter are especially obscure.</p>
  <p>Enter <a href="https://github.com/pkg/errors">github.com/pkg/errors</a>, a package that lets you wrap your errors with additional context. Calling errors.Wrap(err, message) returns a struct that conforms to the errors interface where message is prepended to the error. Everytime an error is returned, you can add context to that error message.</p>
  <p>My preferred way to use errors.Wrap is to keep it concise. The message added should have the format “verbing noun”.</p>
  <figure class="highlight">
    <pre><code class="language-go" data-lang="go"><span class="k">var</span> <span class="n">count</span> <span class="kt">int</span>
<span class="n">err</span> <span class="o">:=</span> <span class="n">db</span><span class="o">.</span><span class="n">DB</span><span class="o">.</span><span class="n">QueryRow</span><span class="p">(</span><span class="s">"SELECT COUNT(*) FROM companies;"</span><span class="p">)</span><span class="o">.</span><span class="n">Scan</span><span class="p">(</span><span class="o">&amp;</span><span class="n">count</span><span class="p">)</span>
<span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
  <span class="k">return</span> <span class="m">0</span><span class="p">,</span> <span class="n">errors</span><span class="o">.</span><span class="n">Wrap</span><span class="p">(</span><span class="n">err</span><span class="p">,</span> <span class="s">"querying company count"</span><span class="p">)</span>
<span class="p">}</span></code></pre>
  </figure>
  <p>Now your errors have more context AND you are not logging in your packages.</p>
  <ol>
    <li id="bgewe-aside1">I will allow an exception for packages that start their go routines that run alongside your app. If your package <i>needs</i> to log information, please please please let the user pass in an <a href="#bgewe-aside2">io.Writer</a>. Most loggers support the io.Writer interface, so your user's can use their logging library and format. If your logger writes JSON and you use <a href="https://github.com/DataDog/dd-trace-go">Datadog's tracer</a>, error messages from Datadog aren't going to match your pretty formatted logs</li>
    <li id="bgewe-aside2">I wish there was a standard interface for logging that gave you log levels.</li>
    <li>Secret trick: if your apps performance is degrading, disable logging. <a href="https://youtu.be/Xyk99kXFHX4">Verizon disables logging</a> when new iPhones launch to prevent themselves from being DDoS'd by thirsty Apple users.</li>
    <li><a href="https://github.com/rs/zerolog">Zerolog</a>, my logger of choice, lets you use <a href="https://github.com/cloudfoundry/go-diodes">Diodes</a>, a ring buffer, for writing logs. If logs are written to Diodes faster than stdout can read them, logs get dropped until stdout catches up.</li>
  </ol>
  ]]></content><author><name>Jim</name></author><summary type="html"><![CDATA[Libraries shouldn’t log. Logging takes resources and should occur as rarely as possible. If an error occurs deep in your program, you don’t want to immediately log it, bubble the error up the stack, and then log the same error again but with additional context. Thus, you should try to log as close to the UI as possible. If you’re writing a web service, only log in the handler before you send a response. The code for accessing the database is a dependency and should be in its own package. We treat it like a library and only concern ourselves with its API.]]></summary></entry><entry><title type="html">Interviewing Like You’re Dating</title><link href="https://www.weinertworks.com/2018/09/18/interviewing-like-youre-dating.html" rel="alternate" type="text/html" title="Interviewing Like You’re Dating" /><published>2018-09-18T23:52:26-05:00</published><updated>2018-09-18T23:52:26-05:00</updated><id>https://www.weinertworks.com/2018/09/18/interviewing-like-youre-dating</id><content type="html" xml:base="https://www.weinertworks.com/2018/09/18/interviewing-like-youre-dating.html"><![CDATA[<p>Interviewing and dating are strangely similar. I assumed they’d magically work themselves out without additional effort on my part! I kid… I kid…</p>
  <h2 id="expect-to-be-rejected-a-lot">Expect to Be Rejected a Lot</h2>
  <p>It’s inevitable, but being rejected is the only way to build up a tolerance to rejection.</p>
  <h2 id="the-best-leads-are-through-your-social-circle">The Best Leads Are Through Your Social Circle</h2>
  <p>I’ve never lived Patrick McKenzie’s scenario where the company has decided they’re going to hire you and the interview is just a formality. Having someone who’s worked with you refer you to a company lets you skip some steps. Sometimes it’s only the HR screening. Sometimes you jump straight to an onsite interview.</p>
  <h2 id="be-legitimately-interested">Be Legitimately Interested</h2>
  <p>This should be obvious, but 2008 Jim needed to learn this. Don’t apply to a company unless you’re interested. Every interview process is going to ask you why you’re interested in [company]. You should know before you apply. If you have to practice it in the mirror, maybe you shouldn’t be applying.</p>
  <p>Sometimes this will come up when they ask you how you heard about [company]. Once I was able to honestly reply that I read their engineering blog and fell in love. They rejected me.</p>
  <p>Alternatively, it is very very hard to answer this question if the real answer is that [company] uses my favorite tech stach, pays above market, and the people are decent human beings.</p>
  <h2 id="ask-questions-that-start-conversations">Ask Questions that Start Conversations</h2>
  <p>One sided discussions are boring. Both you and the interviewer want to ask questions that start conversations.</p>
  <p>2008 Jim didn’t have questions to ask companies. At my early jobs developers had no input on the tech stack or build and deployment pipeline. I was less engaged in interviews, and interviewers probably mistook it for disinterest.</p>
  <p>My current favorite questions are about testing and build pipelines. Having automated builds and deployment is more and more common. It might be a very boring question in five years.</p>
  <p>My ideal interview would be talking shop about interesting bugs and what crazy ways [company] is dysfunctional (Every company is in some way). Alas, here are the questions I actually ask. I try to split them up and not ask every interviewer the same question.</p>
  <h3 id="questions-for-the-recruiter">Questions for the recruiter</h3>
  <ul>
    <li>What is the role?</li>
    <li>How many employees?</li>
    <li>Type of funding? Bootstrapped? What series? (You should know if they’re public)</li>
  </ul>
  <h3 id="questions-for-engineers">Questions for engineers</h3>
  <ul>
    <li>What languages/frameworks do you use? Why does it fit your use case?</li>
    <li>Where do you store code?</li>
    <li>How do you deploy?</li>
    <li>How is work planned?</li>
    <li>How do you monitor your app?</li>
    <li>Who responds to downtime? Is there a run book? Pager duty?</li>
    <li>If you could change one thing at [company] what would it be? (A polite way to ask “What do you hate?”)</li>
  </ul>
  <h3 id="questions-for-managers">Questions for Managers</h3>
  <ul>
    <li>How do you measure success? Six months to a year from now how do I know I’ve met my goals?</li>
    <li>How does your team celebrate victories? (It’s bad if they never stop to appreciate what they’ve done)</li>
    <li>What does the career path look like?</li>
    <li>What does a bad sprint/project look like?</li>
  </ul>
  <h3 id="questions-for-everybody">Questions for Everybody</h3>
  <ul>
    <li>What are you looking for in a coworker/employee?</li>
  </ul>
  <h3 id="questions-someone-asked-me-that-resulted-in-a-good-conversation-but-i-would-never-ask">Questions someone asked me that resulted in a good conversation but I would never ask</h3>
  <ul>
    <li>What’s your most controversial thought?</li>
  </ul>
  ]]></content><author><name>Jim</name></author><category term="interviewing" /><summary type="html"><![CDATA[Interviewing and dating are strangely similar. I assumed they’d magically work themselves out without additional effort on my part! I kid… I kid…]]></summary></entry><entry><title type="html">New Job</title><link href="https://www.weinertworks.com/2018/09/03/interviewing-stats.html" rel="alternate" type="text/html" title="New Job" /><published>2018-09-03T22:10:44-05:00</published><updated>2018-09-03T22:10:44-05:00</updated><id>https://www.weinertworks.com/2018/09/03/interviewing-stats</id><content type="html" xml:base="https://www.weinertworks.com/2018/09/03/interviewing-stats.html"><![CDATA[<p>As of August 14th, I have a new job!</p>
  <h2 id="oops">Oops!</h2>
  <p>I didn’t plan to find a new job. I wanted to start my own company and was going to stay put until I could start my own thing. A friend talked me into applying for a tech lead position at his company. I had the qualifications, and it would have been a huge promotion. I applied for other jobs, but I didn’t think any of them could beat this.</p>
  <p>It was my first onsite interview, and I bombed it.</p>
  <h2 id="doh">Doh!</h2>
  <p>My first onsite had only one technical interview. They asked me to model what happens when a user buys an SSL certificate. They didn’t say it explicitly, but they wanted a <a href="https://en.wikipedia.org/wiki/Sequence_diagram">sequence diagram</a>. I didn’t know what that was. I thought they wanted me to design a system, so I started mapping out services.</p>
  <p>My solution was way too complicated and confusing. I can blame my nerves, but I spent all of my prep-time reviewing <a href="http://www.crackingthecodinginterview.com">Cracking the Coding Interview</a> and working <a href="https://leetcode.com/problemset/top-interview-questions/">Leet Code’s interview questions</a>. The question was new, so I was unprepared. Bombing this question helped me prepare for later interviews.</p>
  <p>I regret that my first choice was my first onsite.</p>
  <h2 id="what-now">What Now?</h2>
  <p>If I hadn’t applied to other companies, I would’ve mourned my lost free-time and given up on interviewing for another year. Instead, I doubled the number of applications I had, and continued studying in my free time.</p>
  <h2 id="job-hunt-stats">Job Hunt Stats</h2>
  <div class="language-plaintext highlighter-rouge">
    <div class="highlight">
      <pre class="highlight"><code>95 days
38 jobs applied for
17 responses
6 onsites
14 rejections
2 offers
1 process ended once I accepted an offer
</code></pre>
    </div>
  </div>
  <h2 id="other-details">Other Details</h2>
  <h3 id="homework-is-a-crapshoot">Homework Is a Crapshoot</h3>
  <p>Every company judges homework differently.</p>
  <p>I spent a week working on a homework assignment for [data metrics company] only to be rejected for doing the bare minimum. They purposefully left the scope vague to see if a candidate would go above and beyond.</p>
  <p>Another homework assignment I wrote in two hours and emailed it to the recruiter with notes on what I would improve if I had more time. It was good enough to skip a technical phone screen and jump straight to onsite.</p>
  <h3 id="no-one-thinks-twice-about-your-interview">No One Thinks Twice About Your Interview</h3>
  <p>I’d heard that <a href="http://fuzzyblog.io/blog/jobhound/2018/04/24/ten-things-i-learned-from-a-job-hunt-for-a-senior-engineering-role.html#number-8-interviews-matter-much-much-more-to-you-than-to-the-company">interviews matter more to you than the company interviewing you</a>. I didn’t expect to have hard proof. I ran into two people one to two weeks after they had interviewed me. Neither recognized me or remembered my interview once I brought it up.</p>
  <p>One exception was an interview I had with a CTO. He interviewed me when he was the VP of development at a different company. He remembered me. I should’ve asked if that was a good thing or a bad thing. 😅</p>
  <h2 id="i-love-my-new-job">I Love My New Job</h2>
  <p>It’s only been two weeks, but I love my new job. It’s 100% remote, and most of my team is remote. The team is brand new built to implement a killer new product. I couldn’t be more excited.</p>
  ]]></content><author><name>Jim</name></author><category term="interviewing" /><summary type="html"><![CDATA[As of August 14th, I have a new job!]]></summary></entry><entry><title type="html">Fixing a Postgres Type Casting Bug With Regex</title><link href="https://www.weinertworks.com/2018/06/08/fixing-a-postgres-type-casting-bug-with-regex.html" rel="alternate" type="text/html" title="Fixing a Postgres Type Casting Bug With Regex" /><published>2018-06-08T00:00:00-05:00</published><updated>2018-06-08T00:00:00-05:00</updated><id>https://www.weinertworks.com/2018/06/08/fixing-a-postgres-type-casting-bug-with-regex</id><content type="html" xml:base="https://www.weinertworks.com/2018/06/08/fixing-a-postgres-type-casting-bug-with-regex.html"><![CDATA[<p>Today I learned how to fix a type casting bug in our Postgresql script with regular expressions. This deserves celebration! Details below.</p>
  <h2 id="the-problem">The Problem</h2>
  <p>We have a database table that matches data to other tables based on a specific column. The values for the column are provided by the user and stored as strings. Depending on the table being matched, the values can be strings or numbers. If the user submitted bad input for a number match, accidentally including characters in their match column, the query fails.</p>
  <p>The code handling the input was exactly the same for every match column type. I didn’t want to add a special case for this. The problem was in the query, so the fix should be in the query.</p>
  <h2 id="the-solution">The Solution</h2>
  <p>Update: I subscribe to Andy Croll’s Ruby newsletter. He <a href="https://andycroll.com/ruby/beginning-and-end-of-string-in-regex/">suggests using \A and \Z</a> to match the beginning and ends of a string because ^ and $ will match the ends of a line. If your string has newlines in it, it will be accepted by the regex.</p>
  <p>Postgres has <a href="https://www.postgresql.org/docs/10/static/functions-matching.html#FUNCTIONS-POSIX-TABLE">regular expression match operators</a>. We can update our query to check that a value is a number.</p>
  <figure class="highlight">
    <pre><code class="language-sql" data-lang="sql"><span class="c1">-- Example query</span>
<span class="k">SELECT</span> <span class="o">*</span>
  <span class="k">FROM</span> <span class="n">list_of_things</span> <span class="n">lot</span>
  <span class="k">WHERE</span> <span class="n">lot</span><span class="p">.</span><span class="n">value</span> <span class="o">~</span> <span class="s1">'^[0-9]+$'</span> <span class="k">AND</span> <span class="n">lot</span><span class="p">.</span><span class="n">value</span><span class="p">::</span><span class="nb">BIGINT</span> <span class="o">=</span> <span class="mi">12002</span><span class="p">;</span></code></pre>
  </figure>
  <p>The ^ operator matches the regular expression to the start of the string and the $ matches to the end. By combining them with [0-9]+ we check that every character in the string is a digit.</p>
  ]]></content><author><name>Jim</name></author><category term="today I learned" /><summary type="html"><![CDATA[Today I learned how to fix a type casting bug in our Postgresql script with regular expressions. This deserves celebration! Details below.]]></summary></entry><entry><title type="html">The Little Things Make Me Love Apple</title><link href="https://www.weinertworks.com/2018/06/07/the-little-things-make-me-love-apple.html" rel="alternate" type="text/html" title="The Little Things Make Me Love Apple" /><published>2018-06-07T23:02:11-05:00</published><updated>2018-06-07T23:02:11-05:00</updated><id>https://www.weinertworks.com/2018/06/07/the-little-things-make-me-love-apple</id><content type="html" xml:base="https://www.weinertworks.com/2018/06/07/the-little-things-make-me-love-apple.html"><![CDATA[<p>My brother was visiting and wanted to connect his iPad to our wifi. Because he’s in my contacts, I didn’t have to tell him my password; I just <a href="https://www.imore.com/wi-fi-password-sharing-ios-11">hit a button on my Mac</a>.</p>
  <p>It’s very inconvenient for my guests to type my super long wifi password. (All of my passwords are song lyrics with random characters.) I didn’t even know Apple had this feature, and it’s made my guests’ visits smoother.</p>
  ]]></content><author><name>Jim</name></author><summary type="html"><![CDATA[My brother was visiting and wanted to connect his iPad to our wifi. Because he’s in my contacts, I didn’t have to tell him my password; I just hit a button on my Mac.]]></summary></entry><entry><title type="html">Apple Has to Become GE</title><link href="https://www.weinertworks.com/2018/05/02/apple-has-to-become-ge.html" rel="alternate" type="text/html" title="Apple Has to Become GE" /><published>2018-05-02T23:11:58-05:00</published><updated>2018-05-02T23:11:58-05:00</updated><id>https://www.weinertworks.com/2018/05/02/apple-has-to-become-ge</id><content type="html" xml:base="https://www.weinertworks.com/2018/05/02/apple-has-to-become-ge.html"><![CDATA[<p>Apple <a href="https://arstechnica.com/gadgets/2018/04/apple-exits-wi-fi-game-airport-routers-discontinued-after-stock-sells-out">discontinued their wireless router</a>. They absolutely should have, but it’s a shame. The Airport Express was terrible. If you used it for a Time Machine backup, everyone else’s internet connection would stall. Unplanned automatic backups would knock my roommate off of World of Worldcraft and cause Youtube videos to stutter. It was embarrassingly bad and missed the trend of having multiple access points. Eero should never have happened.</p>
  <p><a href="https://stratechery.com/2017/apple-at-its-best/">Peak iPhone</a> is going to be a thing. Even if Apple was successful enough to <a href="#apple-ge-1-aside1">sell an iPhone to every person on earth</a>, they would hit peak iPhone. Investors don’t want stable, consistent returns. They want growth! Apple needs to grow its product line. It’s going to turn into G.E.</p>
  <p>Ignore the Homepod, which is <a href="#apple-ge-1-aside3">failing</a> because Siri is terrible. Selling more products to your current customers is the best strategy.</p>
  <p>Wait. What the fuck am I talking about. The Homepod might be the <a href="http://www.loopinsight.com/2018/01/24/on-homepod-and-audio-quality/">best product no one buys</a>. It costs $50 more than the Bose Soundlink I bought in 2013 and has reviews comparing it to a $50,000 speaker? I… want that. Hell, I want Apple to <a href="http://www.righto.com/2015/11/macbook-charger-teardown-surprising.html?m=1">design the power cables and other boring parts of my home appliances.</a></p>
  <p>I want to live in a world where <a href="#apple-ge-1-aside3">Apple is making my toaster oven</a>.</p>
  <ol>
    <li id="apple-ge-1-aside1">Apple will never sell everyone a phone. People's taste are too different, and some people don't like Apple for forcing its tastes on you.</li>
    <li id="apple-ge-1-aside2">I love that a failed product for Apple would be a huge success for any other company.</li>
    <li id="apple-ge-1-aside3">I don't own a toaster oven. They're all terrible.</li>
  </ol>
  ]]></content><author><name>Jim</name></author><summary type="html"><![CDATA[Apple discontinued their wireless router. They absolutely should have, but it’s a shame. The Airport Express was terrible. If you used it for a Time Machine backup, everyone else’s internet connection would stall. Unplanned automatic backups would knock my roommate off of World of Worldcraft and cause Youtube videos to stutter. It was embarrassingly bad and missed the trend of having multiple access points. Eero should never have happened.]]></summary></entry></feed>