<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Aja</title>
    <description>The latest articles on DEV Community by Aja (@thagomizer).</description>
    <link>https://dev.to/thagomizer</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1478%2F479249.png</url>
      <title>DEV Community: Aja</title>
      <link>https://dev.to/thagomizer</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/thagomizer"/>
    <language>en</language>
    <item>
      <title>Contact Tracing and Exposure Notification</title>
      <dc:creator>Aja</dc:creator>
      <pubDate>Wed, 27 May 2020 00:17:45 +0000</pubDate>
      <link>https://dev.to/thagomizer/contact-tracing-and-exposure-notification-2b75</link>
      <guid>https://dev.to/thagomizer/contact-tracing-and-exposure-notification-2b75</guid>
      <description>&lt;p&gt;When Google and Apple announced in April that they would be working together on a contract tracing API, a lot of people got concerned about privacy. Today, I’m going to try to explain how these apps work so that people can make an informed decision about the technology. Personally, if an app using this technology becomes available for my area, I’ll install it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dispelling Misconceptions
&lt;/h2&gt;

&lt;p&gt;First, Apple and Google aren’t making apps. Instead, they are working together to build core technology to make it easier for public health authorities to build apps for their local area. By working together, they can ensure that everyone can get important health information no matter what phone they have.&lt;/p&gt;

&lt;p&gt;Second, the Google and Apple joint effort doesn’t use location data[1]. Some COVID related health apps use &lt;a href="https://9to5mac.com/2020/05/13/utah-dismisses-covid-19-exposure-api-from-apple-and-google-opts-for-location-based-solution/"&gt;location data&lt;/a&gt;, but this project does not. To understand why location data via GPS isn’t ideal, think about how often the GPS in your phone is off by a block. Also, location data can’t differentiate between someone on the 1st floor of a building and the 15th floor of a skyscraper. That difference could be relevant for COVID-19 exposure.&lt;/p&gt;

&lt;p&gt;The Apple and Google effort uses Bluetooth. Bluetooth works well over the distances health experts say are relevant when determining COVID-19 exposure. Bluetooth also works in cell phone and GPS dead zones. Like subway stations, basements, parking garages, and offices and houses like mine that don’t have a strong cell or GPS signal. Also, the signal strength of the Bluetooth connection can be used to approximate the distance between two phones to determine if they are close enough that COVID transmission is likely.&lt;/p&gt;

&lt;p&gt;Finally, you must actively consent to have your data shared. By default, all the data stays on your device. Data is only shared if you get sick and you tell the app to share your data, which it does anonymously.&lt;/p&gt;

&lt;p&gt;If you are curious, here is &lt;a href="https://blog.google/documents/66/Overview_of_COVID-19_Contact_Tracing_Using_BLE_1.pdf"&gt;Google’s Explanation&lt;/a&gt; of the API and &lt;a href="https://covid19-static.cdn-apple.com/applications/covid19/current/static/contact-tracing/pdf/ExposureNotification-FAQv1.1.pdf"&gt;Apple’s FAQ&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  So How Does It Work?
&lt;/h2&gt;

&lt;p&gt;So if these apps don’t use location data, how do they work?&lt;/p&gt;

&lt;p&gt;First, you have to install an app from your local health department. Your phone won’t get the exposure notification application automatically.&lt;/p&gt;

&lt;p&gt;Once you install the app, your phone starts broadcasting a random code via Bluetooth to anyone nearby who also has the app installed. This code changes a couple of times an hour as an additional privacy measure. Your phone keeps track of which codes it has broadcast.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1ONgnluW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/http://www.thagomizer.com/img/Broadcasting.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1ONgnluW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/http://www.thagomizer.com/img/Broadcasting.jpg" alt="Phone Broadcasting Codes"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Your phone is also listening for any other phones nearby that are broadcasting codes. It records all the codes it “hears.”&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--c1bp4GGe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/http://www.thagomizer.com/img/Listening.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--c1bp4GGe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/http://www.thagomizer.com/img/Listening.jpg" alt="Phone Listening for Codes"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once a day or so, the app on your phone contacts the cloud. It downloads a list of these random codes that were broadcast by the phones of people diagnosed with COVID-19. It compares that list to the list of codes it has “heard.” If there’s a match, the app shows you an alert saying you may have been exposed to COVID-19 and gives you instructions about how to proceed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--c5JedgVW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/http://www.thagomizer.com/img/Notification.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--c5JedgVW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/http://www.thagomizer.com/img/Notification.jpg" alt="Exposure Notification"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you get diagnosed with COVID-19, you can open up the app and volunteer to share all the codes your phone has broadcast for the last 14 days. If you volunteer this info, the list of codes gets uploaded to servers in the cloud. None of your personally identifying information is shared, just the codes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QA-_zNy3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/http://www.thagomizer.com/img/SickPhone.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QA-_zNy3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/http://www.thagomizer.com/img/SickPhone.jpg" alt="Sick phone calling the cloud"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;To summarize the essential points:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The exposure notification software Google and Apple are cooperating on does not use location data.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;No personally-identifying information is shared.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;No data leaves your phone without your consent.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You choose whether or not to share your COVID-19 diagnosis, and if you do, it’s done so anonymously.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The main downside I see to these apps is that they need as many people as possible to install them to be effective. My goal for this blog post is to explain the technology so that people can make an informed decision about whether to install the app for their region. I’ll be installing it, and I hope many others do as well.&lt;/p&gt;




&lt;p&gt;[1] The &lt;a href="https://blog.google/documents/72/Exposure_Notifications_Service_Additional_Terms.pdf"&gt;terms of service&lt;/a&gt; for the Exposure Notification API explicitly prevent apps from using location data. Apps also must be endorsed by a government health authority. Apps will not be available in the relevant store if they don’t meet terms of service.&lt;/p&gt;

</description>
      <category>covid19</category>
    </item>
    <item>
      <title>CatOps: Functions Framework, Cloud Tasks, and my cat</title>
      <dc:creator>Aja</dc:creator>
      <pubDate>Wed, 22 Apr 2020 22:19:51 +0000</pubDate>
      <link>https://dev.to/googlecloud/catops-functions-framework-cloud-tasks-and-my-cat-4gem</link>
      <guid>https://dev.to/googlecloud/catops-functions-framework-cloud-tasks-and-my-cat-4gem</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fwww.thagomizer.com%2Fimg%2FNickDoor.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fwww.thagomizer.com%2Fimg%2FNickDoor.gif" alt="Nick Opening the Door"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I have a relatively simple problem, my cat, Nick, can open our front door and let himself out. Since he’s a strictly indoor cat, this is a problem. In true computer nerd fashion, I way over-engineered a solution to lock the doors automatically after a specified delay to prevent Nick from escaping. My solution involves the &lt;a href="https://github.com/GoogleCloudPlatform/functions-framework-ruby" rel="noopener noreferrer"&gt;Ruby Functions Framework&lt;/a&gt;, a container, &lt;a href="https://cloud.google.com/run" rel="noopener noreferrer"&gt;Google Cloud Run&lt;/a&gt;, and &lt;a href="https://cloud.google.com/tasks" rel="noopener noreferrer"&gt;Google Cloud Tasks&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;While this post is about CatOps, the solution is much more generalizable. If you want to call a webhook after a delay for a chatbot, send an email, or remind folks that they have items in their cart they haven’t purchased, you could do something similar to this CatOps project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;

&lt;p&gt;The hardware and triggers side of this is project is a Rube Goldberg of smart home applications and &lt;a href="https://ifttt.com/" rel="noopener noreferrer"&gt;IFTTT&lt;/a&gt;. But the essential thing to know is that my doors have a simple HTTP interface. They send an HTTP request when unlocked and will lock if they receive a specific HTTP request.&lt;/p&gt;

&lt;p&gt;The interesting part of this project was writing some code that I could call when the door was unlocked, which would then lock it 10 minutes later. It sounds simple, but I ran into many challenges.&lt;/p&gt;

&lt;p&gt;First, I didn’t want to set up a server or build a Rails or Sinatra app. I knew that this should only be a few lines of code, and that felt like overkill. I also wanted to use Functions as a Service, which is advertised as ideal for simple scripts. &lt;a href="https://cloud.google.com/functions" rel="noopener noreferrer"&gt;Google’s FaaS&lt;/a&gt; solution doesn’t support Ruby, but Google just released the Ruby Functions Framework. The framework allows you to build and run functions anywhere you can run a container. I had a few choices for where to run my container, but I was curious about &lt;a href="https://cloud.google.com/run" rel="noopener noreferrer"&gt;Cloud Run&lt;/a&gt;, and it promised to be simple to set up.&lt;/p&gt;

&lt;p&gt;The second issue I ran into was the 10-minute delay between opening the door and locking the door. My first attempt was to use &lt;code&gt;sleep(600)&lt;/code&gt;, but both IFTTT and the webhook interface for the door timed out waiting for a response. So I was forced to do something more elegant.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://cloud.google.com/tasks" rel="noopener noreferrer"&gt;Cloud Tasks&lt;/a&gt; was the right solution for the time out problem for several reasons. First, the HTTP Target task type sends an HTTP request when the task executes. I set the HTTP target to the webhook URL, which saved me from needing to writing code to call the webhook myself. Second, Cloud Tasks lets you schedule a task to execute at a specific time, so I could schedule my lock request to run exactly 10 minutes after the door was unlocked.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Code
&lt;/h2&gt;

&lt;p&gt;The Ruby Functions Framework is available as a gem, and you can include it in your project in the standard way using Bundler and the &lt;code&gt;Gemfile&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To write a function, you pass your code as a block to the &lt;code&gt;http&lt;/code&gt; class method of the &lt;code&gt;FunctionsFramework&lt;/code&gt; class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"functions_framework"&lt;/span&gt;

&lt;span class="no"&gt;FunctionsFramework&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;http&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"my_function_name"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;

  &lt;span class="no"&gt;Do&lt;/span&gt; &lt;span class="no"&gt;Stuff&lt;/span&gt; &lt;span class="no"&gt;Here&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Cloud Tasks requires some setup on the server-side, which is explained well &lt;a href="https://cloud.google.com/tasks/docs/creating-queues" rel="noopener noreferrer"&gt;in the docs&lt;/a&gt;, so I won’t recreate it here. Once the initial setup is complete, creating tasks is straight forward. You create a hash that has the appropriate keys and call the &lt;code&gt;create_task&lt;/code&gt; method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"google/cloud/tasks"&lt;/span&gt;

&lt;span class="n"&gt;tasks_client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Google&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Cloud&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Tasks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;span class="no"&gt;PARENT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tasks_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;queue_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;PROJECT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;COMPUTE_REGION&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;QUEUE_NAME&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;http_request: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;http_method: &lt;/span&gt;&lt;span class="s2"&gt;"POST"&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:schedule_time&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;seconds: &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to_i&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:http_request&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;url: &lt;/span&gt;&lt;span class="no"&gt;URL_GOES_HERE&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tasks_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;PARENT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;The last thing I needed to do was add some configuration. I needed to know if the front door or the back door triggered the function, so I knew which door to lock after the delay. I set up an IFTTT trigger to send a parameter called “door” that could be either “front” or “back”. Since the request object is an instance of &lt;code&gt;Rack::Request&lt;/code&gt;, you can access the parameters as usual, through the &lt;code&gt;params&lt;/code&gt; method. Lastly, I extracted the URL that locks the doors into an environment variable for flexibility and privacy reasons.&lt;/p&gt;

&lt;p&gt;Below is the body of the function. To see the setup, you can look at the file on GitHub.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;FunctionsFramework&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;http&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"lock_door"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;http_request: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;http_method: &lt;/span&gt;&lt;span class="s2"&gt;"POST"&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;

  &lt;span class="n"&gt;door&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"door"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;door&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"back"&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
    &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:schedule_time&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;seconds: &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="no"&gt;DELAY_BACK&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to_i&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:http_request&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;url: &lt;/span&gt;&lt;span class="no"&gt;BACKDOOR&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;elsif&lt;/span&gt; &lt;span class="n"&gt;door&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"front"&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
    &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:schedule_time&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;seconds: &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="no"&gt;DELAY_FRONT&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to_i&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:http_request&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;url: &lt;/span&gt;&lt;span class="no"&gt;FRONTDOOR&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;begin&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tasks_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;PARENT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="no"&gt;Exception&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
    &lt;span class="no"&gt;FunctionsFramework&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt; &lt;span class="s2"&gt;"Exception creating task"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="no"&gt;FunctionsFramework&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt; &lt;span class="s2"&gt;"Created task &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="s2"&gt;"Created task &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Testing
&lt;/h2&gt;

&lt;p&gt;The Functions Framework gem comes with testing support that I was able to use. Since my code also uses Cloud Tasks, I needed to create a test double for those calls to prevent my tests from hitting my production task queue.&lt;/p&gt;

&lt;p&gt;Here’s the test double I created for Cloud Tasks Client.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Struct&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TasksClientStub&lt;/span&gt;
  &lt;span class="nb"&gt;attr_accessor&lt;/span&gt; &lt;span class="ss"&gt;:task_history&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:project&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:location&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:queue&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;
    &lt;span class="vi"&gt;@task_history&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Hash&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_task&lt;/span&gt; &lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt;
    &lt;span class="vi"&gt;@task_history&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt;

    &lt;span class="no"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;task_history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;length&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;queue_path&lt;/span&gt; &lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;queue&lt;/span&gt;
    &lt;span class="vi"&gt;@project&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;project&lt;/span&gt;
    &lt;span class="vi"&gt;@location&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;location&lt;/span&gt;
    &lt;span class="vi"&gt;@queue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;queue&lt;/span&gt;

    &lt;span class="s2"&gt;"projects/&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/locations/&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/queues/&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The double includes the methods I call in my function, &lt;code&gt;initialize&lt;/code&gt;, &lt;code&gt;create_task&lt;/code&gt;, and &lt;code&gt;queue_path&lt;/code&gt;. Internally it represents task queues as a hash of arrays, so I can see the tasks that my function enqueues and verify they are correct. I also created a struct called response as a test double for &lt;code&gt;Rack::Response&lt;/code&gt; because I don’t need all the methods and fields in Rack.&lt;/p&gt;

&lt;p&gt;With the doubles created, I could write my tests. My code has separate branches for the front and back doors, so I wrote a test for each. Here’s the test for the front door. The test for the back door case is similar.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"functions_framework/testing"&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_function_creates_correct_task_for_front_door&lt;/span&gt;
  &lt;span class="n"&gt;task_stub&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;TasksClientStub&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;

  &lt;span class="no"&gt;Google&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Cloud&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Tasks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stub&lt;/span&gt; &lt;span class="ss"&gt;:new&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;task_stub&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;

    &lt;span class="n"&gt;load_temporary&lt;/span&gt; &lt;span class="s2"&gt;"locker.rb"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;make_post_request&lt;/span&gt; &lt;span class="s2"&gt;"http://example.com:8080/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"door=front"&lt;/span&gt;

      &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;

      &lt;span class="n"&gt;_out&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;capture_subprocess_io&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;call_http&lt;/span&gt; &lt;span class="s2"&gt;"lock_door"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

      &lt;span class="n"&gt;assert_equal&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;

      &lt;span class="n"&gt;parent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;task_stub&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;task_history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;
      &lt;span class="n"&gt;assert_equal&lt;/span&gt; &lt;span class="s2"&gt;"projects/thagomizer-home-automation/locations/us-central1/queues/door-locker"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parent&lt;/span&gt;

      &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;task_stub&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;task_history&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;

      &lt;span class="n"&gt;assert_match&lt;/span&gt; &lt;span class="sr"&gt;/front/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:http_request&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="ss"&gt;:url&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On line 6, I use Minitest’s stub method to make the function use the test double. Then on line 8, I load the function file. Inside the block, I use the function framework helper method &lt;code&gt;make_post_request&lt;/code&gt; to build up a post request that includes the &lt;code&gt;door=front&lt;/code&gt; parameter my function requires. To call the function, you use the &lt;code&gt;call_http&lt;/code&gt; helper from the function framework testing package and &lt;code&gt;capture_subprocess_io&lt;/code&gt;. Finally, the assertions verify that the function returned successfully, verify that the queue/parent is set correctly, and ultimately ensure that the task object created was correct for a front door request.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deployment and Conclusions
&lt;/h2&gt;

&lt;p&gt;The next post in this series will explain how to do the initial deployment to Google Cloud Run. It will also show you how to set up basic CI / CD on Cloud Build for any Ruby App. But if you are super excited to move forward, you can do an initial manual deploy by following the &lt;a href="https://github.com/GoogleCloudPlatform/functions-framework-ruby" rel="noopener noreferrer"&gt;Readme&lt;/a&gt; for the Functions Framework.&lt;/p&gt;

&lt;p&gt;I want to close this post by being clear that the entire premise of this post is ridiculous. A much more straightforward, and likely cheaper, solution to having a problematic cat would have been to buy new doorknobs that he can’t open. But, I took joy in the pure ridiculousness of this scenario. I got to learn some new technologies in context too. I usually find that when I’m solving a problem I care about, I learn much better than if I’m copying and pasting code from a tutorial. Even if my particular use case is ridiculous, the underlying scenario of one event triggering a count down to another event is something I’ve run into over and over. There are a lot of serious business problems that can be solved using code similar to my CatOps project.&lt;/p&gt;

</description>
      <category>serverless</category>
      <category>cloudtasks</category>
      <category>functionsframework</category>
      <category>ruby</category>
    </item>
    <item>
      <title>Acing the DevRel Interview</title>
      <dc:creator>Aja</dc:creator>
      <pubDate>Fri, 11 Oct 2019 17:54:50 +0000</pubDate>
      <link>https://dev.to/thagomizer/acing-the-devrel-interview-115p</link>
      <guid>https://dev.to/thagomizer/acing-the-devrel-interview-115p</guid>
      <description>&lt;p&gt;I gave my 100th Developer Relations interview this week. I’ve also been on Google’s Developer Relations hiring committee for almost two years. After writing up or reading about all these interviews, I feel like I can offer some general tips on how to do well on DevRel interviews. Of course, standard caveats apply. I’ve only done DevRel at Google and only know Google’s hiring process inside and out. But, I believe the tips below are general enough to apply to many other companies as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  Know What Job You Are Interviewing For
&lt;/h2&gt;

&lt;p&gt;Developer Relations is an umbrella term that covers a lot of different job titles and roles. Also, the field is still new enough that not everyone agrees on what titles, like “Developer Advocate” or “Community Manager,” actually mean. Because of this, you need to do some research before your interview.&lt;/p&gt;

&lt;p&gt;Ask the recruiter or interview coordinator what they know about the job. What is the title? What are the core job responsibilities? Does this job report up through marketing or engineering or other? Find out as much as you can about the job because if the interviewers are any good, the questions they ask will be tailored to the day-to-day work in the role.&lt;/p&gt;

&lt;p&gt;While all companies and teams are different, there are some generalities I can share. Community Manager roles are often a specialized program manager role. Thus, you’ll be asked typical program management questions. You may also be asked about fostering diverse communities, organizing volunteers, and prioritizing tasks. Some roles may also require knowledge of open source software or training and education.&lt;/p&gt;

&lt;p&gt;Developer Advocate roles vary a lot. Usually, teams that report up through engineering are more likely to ask technical questions than jobs on the marketing team. But that isn’t always true. Your recruiter should let you know if technical questions are part of the process. If they don’t proactively tell you, it is okay to ask. The expectations for coding and technical questions vary a lot between jobs, too. At Google, we have a high coding bar for Developer Relations Engineering roles (DA and &lt;a href="https://medium.com/google-cloud/developer-programs-engineer-say-what-b12829729693"&gt;DPE&lt;/a&gt;). We often ask traditional software engineering interview questions, but we may weigh the readability and clarity of the code slightly higher than a software engineering interviewer would.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tailor Your Message to Your Audience
&lt;/h2&gt;

&lt;p&gt;One of the most critical skills in all Developer Relations roles is knowing how to change your message based on your audience. “Failure to adapt their message to the audience” is a common reason I recommend “No Hire” for a candidate. Developer Relations roles work with a variety of audiences, from students to executives, and each audience has unique needs. Developer Relations teams must be confident that you will give presentations and explanations that are appropriate to the audience in front of you.&lt;/p&gt;

&lt;p&gt;There are two main things to keep in mind to ace this part of the interview. First of all, talk to your interviewers the same way you’d speak to potential customers. That means don’t say racist or sexist things. I really shouldn’t have to include this, but I’ve learned this isn’t as obvious as I would like it to be. This includes not saying things that some folks think of as compliments, like, “You know a lot about containers for a [insert minority group here].”&lt;/p&gt;

&lt;p&gt;Related to this, don’t talk down to your interviewer, even if you are smarter than they are. Good DevRelers can explain technical things without making the listener feel dumb. If a candidate can’t do that during the interview, I most likely won’t recommend hiring them. Use polite, respectful language. If you disagree with the interviewer, do so carefully and be prepared to explain your reasoning and back your assertions with data. Part of a DA’s job is to get folks to consider new ideas and approaches, but you have to do it without pissing people off.&lt;/p&gt;

&lt;p&gt;The other thing to keep in mind is the background of your audience. I ask most candidates a question of the form “explain [technical concept] to [specific audience].” For example, “explain containers to the CEO of a small consultancy.” or “explain Python list comprehensions to a physicist.” Good candidates start their answers by clarifying the background that the audience has. They might ask if the CEO understands VMs or whether the physicist knows Python. Excellent candidates ask for more information and then repeat back what they heard to verify they fully understand the problem before answering. This doesn’t have to be awkward or take a long time, by the way. A candidate can simply say, “since the CEO has experience with VMs, I’ll start by…”In contrast, candidates who don’t do well make unstated assumptions about my background or the background of my hypothetical listener. And if you give a presentation as part of your interview process, you need to be able to clearly state the intended audience of that presentation, including the background they had with the topic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Communicate Clearly
&lt;/h2&gt;

&lt;p&gt;Developer Relations is all about communication. Even jobs that seem very technical on the surface, like SDK development, require that you communicate how to use your product through your code. Because of this, communication skills are weighted heavily in all Developer Relations interviews. This doesn’t necessarily mean that your English (or another language) needs to be perfect. But it does mean that you must be able to make yourself understood and that you can understand others.&lt;/p&gt;

&lt;p&gt;Most general interviewing tips can help with improving your communication skills, but there are a few things to keep in mind specific to DevRel. First, try not to use jargon. Every company has internal jargon, but not everyone is aware of what is internal jargon and what is a standard industry term. You can practice communicating with folks from outside your company by going to meetups or participating in online communities and open source projects. This is especially important if you haven’t had a lot of jobs or have been at the same one for a while.&lt;/p&gt;

&lt;p&gt;You can also check with your interviewer occasionally to ensure they understand what you are saying. And I recommend using CS terminology and being extra precise with your language. With friends, I have a habit of saying, “I’ll new up an object.” I picked that up at a previous job. In an interview, though, I’d say, “I’ll instantiate an object” or “I’ll create an object.”&lt;/p&gt;

&lt;p&gt;Also, any code you write or submit as part of a portfolio needs to be clear. When I do interview coaching, I remind folks “in Developer Relations, we teach through code.” Your code should be idiomatic. That means Python should be Pythonic. Ruby shouldn’t look like C. Use the data structures and libraries built into your language where appropriate unless you are told to do otherwise. Variable names and function names should be long enough to be descriptive in most languages (I see you GoLang). As a rule of thumb, an advanced beginner in the programming language should be able to understand your code on their first reading. Be wary of things that you know trip up beginners like meta-programming, multiple layers of callbacks, and unusual, but valid, syntax (I see you, Ruby).&lt;/p&gt;

&lt;h2&gt;
  
  
  Other Tips
&lt;/h2&gt;

&lt;p&gt;I could keep going, but most of my other advice is just standard technical interview advice. There are tons of books and sites available on general interview skills. A number of my questions are common interview questions given a bit of technical flavor.&lt;/p&gt;

&lt;p&gt;If you are nervous about coding questions, the best advice I have is to practice coding. Don’t spend hours memorizing since good interviewers won’t use questions that are directly from books or websites. For practice, I like problems that can be completed in less than two hours and don’t have any competitive aspect. I commonly recommend &lt;a href="https://exercism.io"&gt;exercism.io&lt;/a&gt;, &lt;a href="https://www.projecteuler.net/"&gt;Project Euler&lt;/a&gt;, and &lt;a href="http://rosalind.info/problems/locations/"&gt;Rosalind&lt;/a&gt;. &lt;a href="https://adventofcode.com/"&gt;Advent of Code&lt;/a&gt; is a bit harder but can be fun too. If you practice enough, you’ll notice that many interview questions are variations on a couple themes like text parsing, counting, trees, etc. I also recommend that folks get really comfortable with the text parsing and collections (array, associative array, etc.) classes built into their language.&lt;/p&gt;

&lt;p&gt;If you have specific questions or think I’m wrong about some aspect of this, let me know in the comments or reach out to me on Twitter.&lt;/p&gt;

</description>
      <category>google</category>
      <category>devrel</category>
      <category>interview</category>
      <category>hiring</category>
    </item>
    <item>
      <title>Cloud Build Dependency Graph</title>
      <dc:creator>Aja</dc:creator>
      <pubDate>Thu, 20 Jun 2019 18:35:25 +0000</pubDate>
      <link>https://dev.to/googlecloud/cloud-build-dependency-graph-mkp</link>
      <guid>https://dev.to/googlecloud/cloud-build-dependency-graph-mkp</guid>
      <description>&lt;p&gt;A couple of months ago, my coworker &lt;a href="https://twitter.com/Neurotic" rel="noopener noreferrer"&gt;Mark Mandel&lt;/a&gt; asked if anyone had a way of visualizing dependencies in &lt;a href="https://cloud.google.com/cloud-build/" rel="noopener noreferrer"&gt;Cloud Build&lt;/a&gt;. As you’d expect, several of us jumped at the problem. I wrote my solution up in Ruby using the graph gem.&lt;/p&gt;

&lt;p&gt;Cloud Build configuration files are in yaml. Each build step lists the container to run and some configuration options like arguments to pass and directories to use. Each step also has a &lt;code&gt;waitFor&lt;/code&gt; field to specify dependencies between the steps. This is all the information you need to parse and understand to build the dependency graph.&lt;/p&gt;

&lt;p&gt;Since I’d used it before I started with &lt;a href="https://github.com/seattlerb/graph" rel="noopener noreferrer"&gt;Zenspider’s &lt;code&gt;graph&lt;/code&gt; gem&lt;/a&gt;. The graph gem uses a DSL to output the &lt;a href="https://en.wikipedia.org/wiki/DOT_(graph_description_language)" rel="noopener noreferrer"&gt;&lt;code&gt;dot&lt;/code&gt;&lt;/a&gt; file format used by Graphviz. Using the graph gem, you define edges with the syntax &lt;code&gt;edge "A", "B"&lt;/code&gt;. An edge is just an arrow. In this case, the arrow is between A and B. If either A or B doesn’t exist yet, the gem automatically adds it.&lt;/p&gt;

&lt;p&gt;The DSL makes creating the dependency graph pretty straightforward. All I had to do was parse the build config file and then use the &lt;code&gt;waitFor&lt;/code&gt; field to draw edges between dependent steps. To make the graph easy to read, I chose to use the ID as the node label. If there was no ID field, I used the name. The only tricky bit is that Cloud Build config files don’t have to state all their dependencies using &lt;code&gt;waitFor&lt;/code&gt;. If no dependency is given for a step, it is assumed to depend on all previous steps. This meant I had to keep track of the previous steps to get the edges correct. Putting it all together resulted in this code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;require 'graph'
require 'psych'

steps = Psych.load(File.read(ARGV[0]))["steps"]

previous_steps = []

digraph do
  steps.each do |step|
    id = step["id"] || step["name"]

    deps = step["waitFor"] || previous_steps
    deps.each do |dep|
      break if dep == "-"
      edge dep, id
    end

    previous_steps &amp;lt;&amp;lt; id

  end

  save "buildgraph", "png"

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

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://gist.github.com/thagomizer/5bf613022e323a5134d22a4e4e8f026d" rel="noopener noreferrer"&gt;Gist here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Running that code against the build file Mark shared with the team leads to this dependency graph:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fthagomizer.com%2Fimg%2Fbuildgraph.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fthagomizer.com%2Fimg%2Fbuildgraph.png" alt="Agones Build Dependencies"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All told it took about 30 minutes at hack night to code this up. Most of that time was reading about the Cloud Build configuration file format. If you’re curious, the dependency graph shown above is for &lt;a href="https://agones.dev/site/" rel="noopener noreferrer"&gt;Agones&lt;/a&gt; an open source game server scaling and orchestration platform built on top of Kubernetes.&lt;/p&gt;

</description>
      <category>visualization</category>
      <category>cloudbuild</category>
      <category>gcp</category>
    </item>
    <item>
      <title>Mentoring: You're Doing It Badly</title>
      <dc:creator>Aja</dc:creator>
      <pubDate>Tue, 19 Mar 2019 11:28:54 +0000</pubDate>
      <link>https://dev.to/thagomizer/mentoring-you-re-doing-it-badly-1kio</link>
      <guid>https://dev.to/thagomizer/mentoring-you-re-doing-it-badly-1kio</guid>
      <description>&lt;p&gt;Hey tech community, we need to talk. As a whole, the tech community really doesn’t understand mentoring. You know what the word is. You know that it is good. But most of you are doing it wrong. Which is weird, because we seem to talk about it all the time. Many job ladders have phrases like “mentors junior engineers” in the expectations. Most companies assign mentors to new hires. A lot of folks in the tech world share articles about sponsorship versus mentorship or create yet another mentoring program for people in tech. But I rarely see folks checking in after the program is underway to see how it is going and ask participants if it is helping them reach their goals. Tech seems to think that mentoring means matching up folks and then having the mentor tell the mentee how to live their life.&lt;/p&gt;

&lt;h2&gt;
  
  
  Your Mentee Isn’t You
&lt;/h2&gt;

&lt;p&gt;All of the problems I see in mentorship come from one flawed assumption. Too many folks assume their mentee is just like them. Or at least that their mentee is a younger, less experienced, or less capable version of themselves. And once that assumption is made it is hard to be an effective mentor. I believe most mentors don’t realize they are making this assumption. Folks are just eager to share their knowledge and experience that they don’t slow down long enough to evaluate if it is useful or relevant to the person they’re trying to help. Instead, they just plow ahead, eager to bestow their wisdom and try to make their mentee into a mini-me.&lt;/p&gt;

&lt;p&gt;But, your mentee isn’t you. Their journey to tech isn’t your journey. Their goals are probably different than yours. How they are perceived by others is probably different than how you are seen. This is especially true if your mentee is of a different race, gender, or cultural background than you. If you try to mentor someone by telling them what worked for you, your mentorship has a good chance of failure. There is no single path to success in tech. In this &lt;a href="https://hbr.org/2019/03/the-feedback-fallacy"&gt;fantastic article about feedback&lt;/a&gt;, Buckingham and Goodall phrase it this way, “We think we’re a source of truth. We aren’t. We’re a source of error.”&lt;/p&gt;

&lt;h2&gt;
  
  
  Listen and Respect
&lt;/h2&gt;

&lt;p&gt;So where do you begin a mentorship relationship if not from your own experiences? Start by listening. Ask the folks you mentor what their goals are, both for the mentoring program and their career. Learn about their past successes and their passions. Find out what they’re already tried in pursuit of their goals. Seek to understand what got them to where they’re at.&lt;/p&gt;

&lt;p&gt;It is likely that your mentee’s beliefs, experiences, or goals may feel foreign or misguided to you. When this happens, the best thing you can do is trust that your mentee is an expert on their own life. Don’t second guess what they say. Ask more questions and believe the answers they give. Help them brainstorm solutions or strategies based on what’s worked well for them in the past. Use their values and goals as the basis for mentoring them and be aware of where your experience and knowledge is helpful and where it isn’t.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sponsor Them
&lt;/h2&gt;

&lt;p&gt;There are lots of articles about &lt;a href="https://www.fastcompany.com/3050430/why-women-need-career-sponsors-more-than-mentor"&gt;sponsorship versus mentorship&lt;/a&gt; that have made the rounds. For those unfamiliar with the distinction, a crude summary is that mentors give advice and sponsors create opportunities.&lt;/p&gt;

&lt;p&gt;Sponsors use their social capital and connections to nurture the career of the person they are sponsoring. They make introductions. They pass on opportunities, even ones they may have wanted for themselves. They endorse folks publicly.&lt;/p&gt;

&lt;p&gt;But sponsorship can be done poorly. When endorsing someone in public, avoid the trap of “I think the woman I’m mentoring, Vanessa, deserves a chance to participate in this.” Instead, talk about the people you sponsor the way you talk about other valued colleagues, “We should include Vanessa, she has a lot of background with similar customers that will be helpful here.” I’ve seen folks pass off all their tedious work to their mentee in the name of sponsorship. Or including someone in a planning meeting but not letting them contribute. This isn’t sponsorship, its patronizing.&lt;/p&gt;

&lt;p&gt;When you sponsor someone, you treat them and their career as valuable as your own. You pass on opportunities to them that are relevant to their goals, even if that means you end up competing for a speaking slot at the same conference. You encourage them to take smart risks. And you let them struggle and learn from the failures the way you did. When men sponsor women, it is easy to fall into patterns of &lt;a href="https://en.wikipedia.org/wiki/Ambivalent_sexism"&gt;benevolent sexism&lt;/a&gt; where the man tries to protect the women from bad experiences and ends up limiting their opportunities. The folks you mentor don’t need to be protected, they need to be included.&lt;/p&gt;

&lt;h2&gt;
  
  
  Give a Fuck
&lt;/h2&gt;

&lt;p&gt;The last tip I have is that good mentors care and they show they care through the action. Caring can be checking in on someone when a re-org happens or when there’s an event in the broader world that is likely to upset them. Caring can also be wanting to know about their life beyond the office and remembering the name of their partner or their pets. But the best way to show caring is to make your mentee comfortable. That means having healthy boundaries and not being creepy. It means being open to meeting over meals, outside the office, or potentially having walking 1:1s if that would make the other person more comfortable.&lt;/p&gt;

&lt;p&gt;Mentoring isn’t hard, but it requires putting someone else’s goals and needs before your own. Doing it well means acknowledging that you don’t have all the answers and that their path to success likely looks different than your own. And it means treating each other like human beings, and not stepping stones on your own career path.&lt;/p&gt;

</description>
      <category>mentoring</category>
      <category>managering</category>
    </item>
    <item>
      <title>Good Interview Questions</title>
      <dc:creator>Aja</dc:creator>
      <pubDate>Wed, 12 Sep 2018 10:07:27 +0000</pubDate>
      <link>https://dev.to/thagomizer/good-interview-questions-33f5</link>
      <guid>https://dev.to/thagomizer/good-interview-questions-33f5</guid>
      <description>&lt;p&gt;Many people don’t understand that giving a good interview is a skill that can be learned and improved. There seems to be a tendency to use the same questions that you were asked when you were a candidate. It also seems like new interviewers often overestimate what can be accomplished in 30 - 60 minutes or where the hiring bar should be for a position. Whatever the reason, the result is that many technical interview questions are pretty awful at assessing anything. So I figured today I’d try to give some guidance on improving your interview questions.&lt;/p&gt;

&lt;h2&gt;
  
  
  KISS = Keep It Simple Stupid
&lt;/h2&gt;

&lt;p&gt;The first tip I have is that the meat of your question should be a sentence or two. I learned this from &lt;a href="https://twitter.com/the_zenspider"&gt;Ryan Davis&lt;/a&gt; years ago when I was starting to give interviews.&lt;/p&gt;

&lt;p&gt;A sentence probably seems too short. After all, most real coding problems and tasks take much more than a sentence to describe. It can seem impossible to ask something useful within that limit. It is difficult to distill problems down to a sentence or two, but it is worth it. Short questions give the candidate more time to work and give you more time to assess the candidate. Remember, any time you are talking is time that the candidate doesn’t have to show their skills. Concise questions also tend to be more realistic for the time frame and more readily adapted to different skill levels. If your question doesn’t fit in a sentence or two, consider breaking it up into multiple parts. That way if a candidate only has time to tackle part of it they still feel like they made progress.&lt;/p&gt;

&lt;p&gt;Another advantage to short questions is they allow you to assess the candidate’s ability to think through a problem and identify edge cases or other ambiguities. When we get tasks or features to implement we usually have to find the edge cases and work with the product owner or our co-workers to figure out how to handle them. I want to work with people who can handle a bit of ambiguity in a problem statement and can ask questions to better understand what is going on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Avoid Jargon
&lt;/h2&gt;

&lt;p&gt;Another issue I see in many interview questions is the use of jargon. Jargon may be company specific terminology; we have plenty of that at Google. Alternatively, programming language or industry-specific terms may be jargon to junior candidates.&lt;/p&gt;

&lt;p&gt;Language-specific jargon is often subtle enough we don’t realize we’re using jargon. For example, associative arrays go by different names in different languages. I’m a Rubyist and the associative array data structure in Ruby is called a hash. The same data structure is a dictionary in Python, a map in Go, and an object in JavaScript. If I asked a candidate to implement an algorithm using a hash and they aren’t familiar with Ruby they may not understand what I’m asking even if they know how to use an associative array.&lt;/p&gt;

&lt;p&gt;Here’s another jargon example in a question I used for several years, “Write a function that converts an integer to expanded form.” Most adults don’t know what is meant by “expanded form” which makes this question much harder than it needs to be. I often had to define and explain expanded form in the interview which used up precious time. I ended up rewriting the question to remove the jargon which saved time and gave me more information about the candidate’s actual skills.&lt;/p&gt;

&lt;h2&gt;
  
  
  Give Examples
&lt;/h2&gt;

&lt;p&gt;One of the easiest ways to eliminate jargon and shorten your question is to give examples. For the question expanded form I mentioned above, I rewrote it as “Write a function that takes an integer and prints out the number’s decomposition. For example, 372 would become 3 hundreds, 7 tens, 2 ones.” I’ve also heard of interviewers giving one example input and output and then asking the candidate specify the output for another input to verify understanding.&lt;/p&gt;

&lt;p&gt;When you give examples beware of giving too many or showing all the edge cases. One or two is usually sufficient. More can be distracting. Be thoughtful when choosing examples. My example for the expanded form problem doesn’t show edge cases like 0s and 1s. I’ve found that if I specify all the edge cases up front, it is overwhelming for candidates. When I let the edge cases come up organically, the candidate and I can have a conversation about them. Should 703 be 7 hundreds, 0 tens, and 3 ones? Or should it be 7 hundreds and 3 ones? Both are reasonable answers, and a discussion with the candidate about which to pick gives me more insight into how they’ll contribute to the design process if hired.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build in Ambiguity and Extensions
&lt;/h2&gt;

&lt;p&gt;I have two coding questions I use right now. Both of them are delightfully underspecified. As I mentioned above, I don’t mention the edge cases up front. The questions I use have many reasonable solutions.&lt;/p&gt;

&lt;p&gt;None of this is accidental. A problem with built-in ambiguity allows me to adapt the difficulty on the fly. If folks are struggling or we’re running low on time, I can reduce the number of edge cases I ask them to handle. I can tell a candidate what algorithm or data structure to use if they are struggling. If the candidate prefers a particular approach, like iterative instead of recursive, they can still be successful.&lt;/p&gt;

&lt;p&gt;I also have several extensions written out for my problems. These are follow up questions that can make the problem more challenging. I might ask how the code would change if the input data is significantly bigger. Or I may ask how the work cloud be distributed across multiple machines or data centers. A relatively common extension I ask is “What’s the complexity of your solution? Can you do better?” All of these let me increase the difficulty and get more information about a candidate’s strengths, weaknesses, and willingness to keep improving or expanding on something after it has been “solved.”&lt;/p&gt;

&lt;p&gt;Another reason I have flexible questions is to simulate real software development. No interview question comes close to resembling the real world. However, the problems we solve in the real world are rarely well-defined, and requirements frequently change after we’ve started coding. If a candidate gets frustrated that the problem isn’t well-defined or because I change the requirements that is a signal to ask them about how they work with capricious product owners.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make It Easier Than You Think You Should
&lt;/h2&gt;

&lt;p&gt;Interviews are an artificial environment. Candidates don’t have their tools and references. They are expected to work independently. They often have traveled and may be short on sleep. They are usually in an environment where they have no idea what to expect, where to find the bathroom, or if there will be a lunch break. It is unreasonable to expect folks to do production-quality coding in this situation. In the real world we don’t work alone, without our tools, in a short time frame while being judged continuously and potentially interrupted.&lt;/p&gt;

&lt;p&gt;All these confounding factors mean that your interview questions should be easier than you think is reasonable. My questions are usually things I would have been comfortable attacking early in my career. I aim for things that my co-workers can solve in 10 minutes so that the candidate has a good chance of finishing during a 25-30 minute interview.&lt;/p&gt;

&lt;p&gt;I use a rough rule of thirds to decide if my problem is appropriately tricky. With a good problem, I expect a third of candidates to be successful without much help. Another third will make small mistakes or require some hints. The final third is only successful with significant hints, help with an algorithm, or doesn’t finish during the time available.&lt;/p&gt;

&lt;h2&gt;
  
  
  Draw From Your Work if Possible
&lt;/h2&gt;

&lt;p&gt;My last tip is to draw from your work if possible. In the very first interviewer training I took, the instructor encouraged us to not use problems off the internet or out of books. Instead, he recommended looking for problems in the tasks we did in our day-to-day work or something particularly interesting that came up recently. Primarily this reduces the chances that the candidate has seen your problem before and/or memorized the solution.&lt;/p&gt;

&lt;p&gt;The number decomposition problem I wrote about above came directly from work I was doing at the time. One of the two questions I use right now comes from a task I did dozens of times in my career before Google. A co-worker uses a question based on a customer interaction he had. Finding these questions can be challenging, but they exist if you keep your eyes open.&lt;/p&gt;

&lt;p&gt;I also feel better about questions based on my actual work. There’s a lot of valid reasons why coding interviews are awful. If my company’s hiring process requires me to ask them, I want them to assess whether the candidate can do the job. If the question comes from the work I do, that makes me more confident that I’m assessing relevant skills.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;Coding interviews are awful. I’ve &lt;a href="http://www.thagomizer.com/blog/2017/06/18/there-is-no-perfect-interview.html"&gt;written about that before&lt;/a&gt;. However, as interviewers, there are many things we can do to make them less awful and more useful to the hiring manager. I’ve outlined some ways to improve, but I’m sure there are more. I’d love to hear your opinions about good and bad interview questions.&lt;/p&gt;

</description>
      <category>devrel</category>
      <category>interview</category>
    </item>
    <item>
      <title>Friction Logs</title>
      <dc:creator>Aja</dc:creator>
      <pubDate>Wed, 22 Aug 2018 08:58:10 +0000</pubDate>
      <link>https://dev.to/thagomizer/friction-logs-3110</link>
      <guid>https://dev.to/thagomizer/friction-logs-3110</guid>
      <description>&lt;p&gt;Good developer products are easy to use. I don’t think that statement is controversial, but one of the challenges of software design is that the things that make a product easy to use are often small and may seem insignificant on their own. Consistent APIs, logical defaults, simple getting started instructions and an uncluttered UI are the things that make good products.&lt;/p&gt;

&lt;p&gt;And yet, a lot of folks in developer relations feel a bit sheepish entering a bug like “Resource should be plural to be consistent with other commands.” Realistically that bug is going to annoy a bunch of developers, but if it is the only issue, it probably won’t block adoption of an otherwise great product. When these little issues pile up, it can be hard to explain that the net effect is a library or tool that no one wants to use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Friction logs develop user empathy
&lt;/h2&gt;

&lt;p&gt;This is where friction logs come in handy. A friction log is a document that lists all the little stuff that makes the tool hard to use, but frames it around a narrative or customer use case. I learned about them in my first weeks at Google. We often ask new folks in Developer Relations to write friction logs for common scenarios that they are familiar with from previous work. The critical difference between a friction log and a bug list is that the friction log puts the issues in context. It tells the story of the entire user experience from start to finish. Well written friction logs are one of the best tools I know for developing user empathy in folks who don’t get to talk to customers very often.&lt;/p&gt;

&lt;p&gt;The friction log template we use at Google has a few common questions at the top: the logger’s name, the platform/language/browser if relevant, the date, and the product(s) used for the log. After those basics, the logger describes the scenario they are trying. In my opinion, this is the most crucial part of a friction log. The scenario should be simple, ideally no more than two short sentences. It should be something familiar enough that no one is going to react with, “Why would someone want to do that?” when they read it.&lt;/p&gt;

&lt;p&gt;For example, the first friction log I did was “Upload a picture of my cat to Google Cloud Storage.” I’ve also done “Move a Rails website to Google Cloud.” These scenarios make good friction logs because they are things that many users are likely to do.&lt;/p&gt;

&lt;h2&gt;
  
  
  Approach friction logs as a user not as an insider
&lt;/h2&gt;

&lt;p&gt;The rest of the document is a log of what you did and any reactions you have while you work through the use case. If you searched for something online, write down the search terms. If you follow the third link in the results, make a note of that and record the URL. If you type commands into a command line or write code, copy and paste it into the log. While this is similar to recording bug reproduction steps, there is no need to find minimal steps and recording your reactions, such as “Now I’m frustrated” or “Copied and pasted this from the docs, didn’t bother reading the prose” helps tell your story.&lt;/p&gt;

&lt;p&gt;While friction logging it is vital that you try to forget your inside knowledge and approach the problem as a user would. This is one of the reasons new hires are great friction loggers; they don’t know enough about the inner workings of the product to instinctively avoid the rough spots.&lt;/p&gt;

&lt;p&gt;At Google, we use traffic-light themed highlights (red, yellow, green) in the log to point out particularly excellent or problematic parts of the experience.&lt;/p&gt;

&lt;p&gt;Green: Something delightful. Maybe there was a great example, or something worked right out of the box without requiring extra configuration. Yellow: Something that was frustrating. Perhaps you couldn’t find relevant docs after 10 minutes of searching. Red: Places where you would have given up if this wasn’t your job.&lt;/p&gt;

&lt;h2&gt;
  
  
  Remember to share
&lt;/h2&gt;

&lt;p&gt;Friction logs do no good unless you share them with people empowered and motivated to make a change. For me, this was the magic of my first few friction logs. I submitted them via a web form, and they got routed to the appropriate product manager and engineering team. They looked at my log and correlated known issues with existing bugs, leaving comments pointing to the bug in my log and comments leading to the log in the bug. If I found new problems, they entered bugs.&lt;/p&gt;

&lt;p&gt;They also left comments asking for clarification and my suggestions for how to improve parts of the experience. In addition to any product defects, the friction log pointed out places where SEO was poor, or a particular type of documentation was missing. That’s something that I find hard to write a traditional bug for but happens naturally with a narrative log.&lt;/p&gt;

&lt;h2&gt;
  
  
  Friction logging supports the whole user experience
&lt;/h2&gt;

&lt;p&gt;I’m sure many companies and teams do something similar. When I was in QA, we did acceptance and scenario testing as part of release sign off. On the surface that seems the same as friction logging. The difference I see in friction logs is that the point isn’t to find things that don’t work and enter bugs. Instead, the focus is on the entire user experience and recording all the places where things were rough, where a user would experience friction. Since there’s a log of the whole experience, you call attention to how the whole product fits together and how it works with other products.&lt;/p&gt;

&lt;p&gt;Most of my friction logs are relevant to several engineering teams. As a former tester, I appreciate that I can write a friction log on a scenario that isn’t on the critical path. Acceptance testing was all about what we were about to release and core product scenarios. I can write a friction log on nearly anything. Some of my best friction logs came from real-world experiences, like trying to deploy an app I was writing as part of a volunteer project.&lt;/p&gt;

&lt;p&gt;If you haven’t heard of friction logs before, I hope you can add this tool to your dev rel toolkit. If you have used friction logs or something similar, I hope this article gives you a different perspective on how scenario-focused feedback can be useful.&lt;/p&gt;

</description>
      <category>devrel</category>
    </item>
    <item>
      <title>Ask These Questions before Going to Prod</title>
      <dc:creator>Aja</dc:creator>
      <pubDate>Wed, 15 Aug 2018 08:11:33 +0000</pubDate>
      <link>https://dev.to/thagomizer/ask-these-questions-before-going-to-prod-5clc</link>
      <guid>https://dev.to/thagomizer/ask-these-questions-before-going-to-prod-5clc</guid>
      <description>&lt;p&gt;Many folks who are new to operations approach things with an absolutist mindset. They see things as correct or incorrect and secure or insecure. In reality, operations of all types is balancing opposing forces. After all, the most secure website in the world wouldn’t be accessible to anyone and certainly wouldn’t have users.&lt;/p&gt;

&lt;p&gt;With that in mind, I turned the typical format for the “how/where do I run my stuff” talk on its side. Instead of covering each product and the pros and cons of using it, I organized my talk around seven questions. I chose the questions based on personal experience taking apps to production.&lt;/p&gt;

&lt;p&gt;These may not be the right questions to use, but I received feedback that the questions format helped folks understand trade-offs and equipped them to make architecture decisions with more confidence.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. What are my priorities?
&lt;/h2&gt;

&lt;p&gt;The most crucial question is to figure out your priorities. Operations is all about trade-offs. Because of this, it is vital that you can clearly state your goals, what is critical, and the things you aren’t concerned about. Priorities can be practical matters like “saving money” or “robustness.” They can also be more scenario-based like, “support our series B funding round by allowing investors to see our proof of concept.” Alternatively, maybe your priority is, “get valuable feedback from a limited set of friendly beta users.” Most of the time you’ll have several priorities, and it can be helpful to roughly stack-rank them.&lt;/p&gt;

&lt;p&gt;Once you have your priorities, think about what implications they have. For example, if your focus is on saving money, you may not want to pay extra for a highly managed platform-as-a-service system. If you are supporting beta users or investor rounds, it may be crucial that your system scales down to zero, so you aren’t paying when no one is looking at your app. These are just examples. There are many other ways that priorities can influence the decisions that you make.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. What does my load look like?
&lt;/h2&gt;

&lt;p&gt;The shape of your load can also have a profound impact on what compute options, and even what architectures make sense for your app. The load for many apps follows the work day. Either they’re applications people use during the workday, or they are applications people use outside of work. In both those cases, being able to scale efficiently and dynamically as load changes is essential. Some applications have loads that are bursty. They may see several hundred thousand requests in a few minutes and then only a hundred requests a minute for a few hours. Other loads require large amounts of batch processing on a regular schedule but have more modest computing requirements at other times.&lt;/p&gt;

&lt;p&gt;Most cloud providers allow for auto-scaling (and to a lesser degree auto-healing) of your application to adjust to current load. When using autoscaling, you usually specify a minimum number of instances and a max number of instances. Personally, I set my max number of instances to 5x my predicted load. I use 5x because your frontend web servers aren’t the only thing that must scale. If I get close to that 5x upper bound, I should double check that other parts of the system like the database and my mailer are still handling the load okay. Also, at 5x I ordinarily have enough warning that we’re approaching that limit to adjust it further upwards if everything is going well.&lt;/p&gt;

&lt;p&gt;Not all compute choices allow you to scale down to zero. If you know you will have no traffic sometimes, scaling to zero can be an essential way to save money. Bursty loads may be best served by a serverless compute option like Google Cloud Functions or AWS Lambda. Also, batch processing is usually cheaper using a discounted compute option like preemptible VMs. You can also save money by running jobs that aren’t hurt by latency in less expensive regions (if data locality requirements allow). There are lots of ways load can influence your choices. Your priorities should help guide which thing to prioritize right now so you can choose products that make sense for your application.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. What skills do I have?
&lt;/h2&gt;

&lt;p&gt;This is a question that some folks don’t consciously think about, but it always comes up when I’m talking to successful entrepreneurs. Think about what languages and frameworks your team already knows. Does anyone have experience setting up, maintaining, and securing VMs? What products are you most comfortable with?&lt;/p&gt;

&lt;p&gt;You don’t necessarily have to stick with what you know; sometimes a new project is a great chance to learn something new. If you go outside your comfort zone though, things may take longer. You may want to find places you can get real-time help or support before you choose to go with something you aren’t comfortable with yet.&lt;/p&gt;

&lt;p&gt;Sometimes folks are eager to use a specific technology just because they heard about it on a podcast or in a conference talk. That isn’t always an incorrect choice, but jumping into an unfamiliar technology may not be the best use of your time and money. As an example, I see many folks these days clamoring to use containers. Containers are awesome. I really like GKE. But containers add a layer of indirection and complexity to your deployments. If you aren’t entirely comfortable with your stack outside a container, you need to be aware that debugging it inside a container is harder.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. What’s the happy path?
&lt;/h2&gt;

&lt;p&gt;If you aren’t familiar with the term “happy path” it means the path you want users to go down. For e-commerce that probably is the purchase path. For a social app that might mean adding friends or sending messages. A good metric to figure out what your happy path or paths are is, “if this goes down should I be paged at 2 AM?”&lt;/p&gt;

&lt;p&gt;Once you’ve identified your happy path, you need to instrument it. Most folks know to use an uptime monitoring tool to ensure that their site is still rendering pages. Ideally, that monitor should make sure that there’s some content on that page not just that the page returns a 2XX status code. You also should think about what other indicators may show that something is wrong. Some examples: number of successful purchases, number of logins, number 4XX errors, and average load times for critical pages. When you’ve found these other indicators, instrument them as well. They may not all have a “page me at 2 AM” importance level, but these other indicators can detect errors that simple uptime checks fail to find.&lt;/p&gt;

&lt;p&gt;For one app I worked on I had a dashboard showing the number of successful payments on a week-over-week graph. Except during holiday periods, the two lines trended together. One day we noticed that the “today” line was well below the “last week” line. We investigated and found an issue with our payment processing. Since payment was one of the happy paths we monitored, we were able to address the issue quickly. We would have found the issue eventually, likely when a customer reported it. By putting the monitoring and application analytics in place up front, we heard about it before too many customers were affected.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Where are the rough spots?
&lt;/h2&gt;

&lt;p&gt;Once you’ve instrumented the happy path, you need to instrument the rough spots. By rough spots, I mean the parts of your app that you didn’t have a chance to test thoroughly. Or the places where you integrate with services that don’t feel very reliable. Sometimes rough spots are just the places that seem to break most often.&lt;/p&gt;

&lt;p&gt;Monitoring these spots can be tricky. Often they fail enough in benign ways that if you aren’t careful you’ll cause alert fatigue. I usually take an approach centered around the question, “is it getting worse?” I’ll set up dashboards showing the 50th percentile latency and perhaps put alerts on the 5th percentile latency going significantly over reasonable. I also set up application analytics that allow me to dig into requests and see what the call stack looks like or how a request propagated through a series of microservices. Sometimes this more detailed information can help me fix the code issues and smooth out this rough spot.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. What is my fault tolerance?
&lt;/h2&gt;

&lt;p&gt;Any time I mention fault tolerance in a talk some folks get a little bit tense. Most people want their application to be up all the time, and never have any errors. I get it. Success feels awesome. Having a conversation about error budgets with your boss or CEO can be challenging. But technology professionals know that an always up service isn’t realistic. Even if your service is perfect, natural disasters and &lt;a href="https://www.wired.com/2014/08/shark-cable/"&gt;sharks biting undersea cables&lt;/a&gt; can cause errors.&lt;/p&gt;

&lt;p&gt;So before you go live, think about how much downtime and what sorts of downtime you can tolerate. Is a small amount of downtime in the middle of the night okay? Is it okay to take downtime in the middle of the day to apply critical OS level patches? When &lt;a href="https://en.wikipedia.org/wiki/Heartbleed"&gt;Heartbleed&lt;/a&gt; happened, many operations folks had to explain to the business side of their company why taking a small outage in the middle of the day was prudent under the circumstances. Deciding with the business leaders on what is an acceptable error budget and some basic guidelines for what critical bugs and issues look like.&lt;/p&gt;

&lt;p&gt;If the error budget you are working with is small, you’ll need to ensure that your architecture is resilient and flexible. You may want to choose technologies that make things like &lt;a href="https://martinfowler.com/bliki/CanaryRelease.html"&gt;Canary Deployment&lt;/a&gt; easy. You’ll probably want automated rollbacks, and you’ll need to test your rollback procedure regularly. Deploying to multiple regions may be prudent. Moreover, you’ll need to come up with, and test, a disaster recovery plan.&lt;/p&gt;

&lt;p&gt;High availability/high resiliency isn’t a wrong choice, but doing it correctly requires much up-front work. That work makes you prepared when things inevitably go wrong. You can reduce some of the prep work by choosing technologies, architectures, and tools that are built to support your goals. There’s also nothing wrong with deciding that based on your priorities, occasional downtime isn’t a big deal. Once your priorities change, you can revisit this decision and make necessary changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Are my secrets secure enough?
&lt;/h2&gt;

&lt;p&gt;The last question is a reminder to audit your code, logs, and other places to ensure that your secrets are secret. By secrets, I mostly mean API Keys and customer information. Most folks know not to check in their API Keys, yet folks do it all the time. Think about what level of security is enough for your application secrets. Some folks need a tool like &lt;a href="https://www.vaultproject.io/"&gt;Hashicorp Vault&lt;/a&gt; or &lt;a href="https://cloud.google.com/kms/"&gt;Google KMS&lt;/a&gt; with support for encryption and key rotation. Other users are content ensuring that their API keys are in environment variables on production servers and not checked into the source. There are many ways to handle secrets; please make sure you are handling them purposefully after thinking through the risks and use cases.&lt;/p&gt;

&lt;p&gt;You are also a steward of your customer’s information. It is surprisingly easy for credit card numbers, usernames, and passwords to end up in logs, internal error messages, and alerts. There have been several high profile cases of this type of thing happening even at large companies with security auditing in place. The most common culprit I’ve seen is logging post bodies either to the application log, Syslog, or a third-party logging service. I’ve also seen companies use downloads of the production database for local testing. Just don’t do that. Obfuscate the data in some non-reversible way or build a new test database. The risks of a hard drive or laptop being compromised are much higher than most of us realize. Also continually monitor your application, logs, and other resources to ensure secrets haven’t made their way into places they don’t belong. Many of the well-known data breaches were due to bug fixes or other modifications years after the initial launch.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>production</category>
    </item>
    <item>
      <title>Org Mode Tagging</title>
      <dc:creator>Aja</dc:creator>
      <pubDate>Wed, 30 May 2018 20:51:09 +0000</pubDate>
      <link>https://dev.to/thagomizer/org-mode-tagging-28hh</link>
      <guid>https://dev.to/thagomizer/org-mode-tagging-28hh</guid>
      <description>&lt;p&gt;I’ve been conducting about one interview a week this year. That isn’t an impossible interview load, but it isn’t insignificant either. For me, the hardest part of interviewing is preparing questions ahead of time. It isn’t strictly necessary to come in with a plan, but I find it helps me be confident I hit all the areas I need to hit. Prepping beforehand also lets me tailor the questions to the candidate and role. However, I don’t want to ask each candidate entirely different questions, and I don’t want to have to think up questions when I’m cramming in the hour before the interview. In the past, I’ve used my previous interview notes to come up with questions but even with grep that got tedious. Luckily, &lt;code&gt;org-mode&lt;/code&gt; provides a neat solution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tagging
&lt;/h2&gt;

&lt;p&gt;Org-mode supports arbitrary tags on headlines. If you are using Org to track tasks, then you might tag items “work” or “phone”. Tags go at the end of the line, surrounded by colons. You can use &lt;code&gt;C-c C-C&lt;/code&gt; to enter a tag in the minibuffer, but most of the time I type the tag, surrounded by colons.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;* Buy cat food :store:
* Water green beans :garden:
* Eat extra cookies :work:lunch:
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You can also supply file level tags with this syntax:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#+FILETAGS: :work:supersecret:

* Come up with an awesome code name 
* Write awesome codes :computer:
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;I found tag inheritance to be non-intuitive at first. File level tags apply to every headline/bullet point in the file. So both of the headlines in the above example have the “work” and “supersecret” tags even though they aren’t on that line. The second headline also has the tag “computer.”&lt;/p&gt;

&lt;p&gt;Once you have started using tags, you can use tag searches. &lt;code&gt;C-c \&lt;/code&gt; searches the current buffer and highlights the headlines with tags matching your search. Tag search supports basic Boolean searches with &lt;code&gt;&amp;amp;&lt;/code&gt; for and, &lt;code&gt;|&lt;/code&gt; for or, and &lt;code&gt;+&lt;/code&gt; and &lt;code&gt;-&lt;/code&gt; for required or excluded matches. You can combine and nest operators which lets you do things like &lt;code&gt;store|home-garden&lt;/code&gt;. That query lists all the items marked store or home but discards items matching home if they’re also tagged garden.&lt;/p&gt;

&lt;p&gt;To search across multiple org-mode files, you need to have them included in the files searched for the &lt;a href="https://www.gnu.org/software/emacs/manual/html_node/org/Agenda-Views.html"&gt;agenda&lt;/a&gt;. To do this, you have to modify the &lt;code&gt;org-agenda-files&lt;/code&gt; variable. I found this part confusing since it doesn’t make sense to me that you modify the “agenda” to search multiple files that don’t contain any tasks but org is weird sometimes. Once you’ve configured the search path, you can use &lt;code&gt;C-c a m&lt;/code&gt; to search for matching headlines in all files. The whole tagging system is incredibly powerful, and I’ve only gone into the very basics here.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Tags for Interviewing
&lt;/h2&gt;

&lt;p&gt;I have two ways I use tags for interviewing. First I have a &lt;code&gt;questions.org&lt;/code&gt; file that lists all the questions I’m comfortable asking. I’ve tagged each question with the general category it is meant to asses (technical, developer relations, leadership, etc.) and potentially a secondary tag like “appdev” for Application Development or “warmup” for more straightforward questions. Here’s a small snippet from my questions file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;* What interests you about developer relations? :devrel:
* What is your favorite language right now? What do you like about it? :general:warmup:
* Tell me about a time when you had to push through a contentious technical change. :leadership:
* What is serverless? :serverless:appdev:technical:
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;When I’m preparing for an interview, I first create a new org file for the interview. Then I search my questions file using queries like ‘warmup’ or ‘DevOps’ to find questions that may be relevant. Once I’ve found a question I want to use I copy it to the org file for this interview.&lt;/p&gt;

&lt;p&gt;After the interview is over, I tag the interview specific file with a couple of file level tags. Usually the job ladder, an approximate level based on my leveling recommendation, and maybe the candidate’s specialty. This habit leaves me with many files containing a list of questions and a set of header tags like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#+FILETAGS: :DA:L4:L5:language
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;I use the file level tags to see the exact list of questions I’ve asked for this role before. If I’m interviewing a language DA, I’ll search for language&amp;amp;DA. I use this shortcut for common roles because it saves time and it ensures I’m asking every candidate roughly the same questions. I still spend some time tweaking questions based on the candidate’s resume or open source work, but I find being able to grab the exact list of questions very quickly is a huge time saver.&lt;/p&gt;

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

&lt;p&gt;I’m pretty sure this isn’t an intended use of org tagging, but it works well for my purpose. As I think of new questions (or come up with extensions to existing ones) I add them to my question bank for use in the future. Offloading a lot of the prep to my computer lets me show up for interviews relaxed and more focused on the candidate experience. Also, &lt;a href="https://careers.google.com/jobs#t=sq&amp;amp;q=j&amp;amp;li=20&amp;amp;l=false&amp;amp;jlo=en-US&amp;amp;jcoid=7c8c6665-81cf-4e11-8fc9-ec1d6a69120c&amp;amp;jc=DEVELOPER_RELATIONS&amp;amp;j=developer&amp;amp;"&gt;we’re hiring&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>emacs</category>
      <category>org</category>
    </item>
    <item>
      <title>The Dreaded Whiteboard</title>
      <dc:creator>Aja</dc:creator>
      <pubDate>Mon, 26 Feb 2018 20:58:38 +0000</pubDate>
      <link>https://dev.to/thagomizer/the-dreaded-whiteboard-1c1h</link>
      <guid>https://dev.to/thagomizer/the-dreaded-whiteboard-1c1h</guid>
      <description>&lt;p&gt;Last week I asked a question on Twitter about how interview processes can be improved at companies like Google, Amazon, and Microsoft.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What would make you feel more confident about interviewing at a big/famous software company like Google?&lt;/p&gt;

&lt;p&gt;— Aja Hammerly 🦖 (@the_thagomizer) &lt;a href="https://twitter.com/the_thagomizer/status/964528352295112705?ref_src=twsrc%5Etfw"&gt;February 16, 2018&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I got a lot of great replies, and one of the issues that came up was a lot of people that hate whiteboard coding. So at CoffeeOps on Friday I asked why people hate whiteboard coding. Again I got a lot of passionate and interesting replies. In both of these forums though, I noticed that people had a lot of assumptions about what made a whiteboard answer good. Most of those assumptions were not things that I and others I know look for in whiteboard interviews. So I decided to share what I’m looking for in a whiteboard interview.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I’m Not Evaluating
&lt;/h2&gt;

&lt;p&gt;There is a pervasive belief that interviewers expect candidates to be able to write syntactically correct code in one go without any mistakes, planning, or backtracking. And, sadly, I’m sure some interviewers expect exactly that. Those people are bad interviewers.&lt;/p&gt;

&lt;p&gt;I’m also not looking for you to implement algorithms from scratch. I don’t ask these questions because I don’t want to work with people who write their own sorting and compression algorithms. I want to work with people who use well tested libraries and spend their time on the business specific problems.&lt;/p&gt;

&lt;p&gt;And I don’t care what language you use. I assume that if you are otherwise a competent developer, you can learn a new language when needed. I also don’t give bonus points for using a language I like or a language that’s hard. Personally, when I interview I use Ruby because I know it well and I’m confident when using it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Am Looking For
&lt;/h2&gt;

&lt;p&gt;I use the whiteboard portion of the interview to try to figure out a couple of things. First, I want to know that the candidate can come up with at least one approach to solving a small technical problem. I’ve started to ask candidates to tell me their plan/approach and the language they are using before they start writing code. I also encourage the candidate to ask me questions about the problem. Candidates who do well usually ask about edge cases.&lt;/p&gt;

&lt;p&gt;Second, I want to find out if the candidate can explain their approach to me and justify any trade-offs or design decisions. Experience tells me that candidates who can tell me a high-level plan are more likely to succeed once we get to the actual code. Also, in DevRel we often get asked: “how would you solve this problem I’m having?” Asking the candidate to explain how they’re going to solve a coding problem is one way I evaluate how a candidate might do in customer scenarios. Also, I want to work in a culture where people can respectfully challenge each other and respond to those challenges. A respectful challenge I might use in an interview would be something like, “why did you use an array instead of a queue?” or after a candidate has finished coding I may ask “This works, but can you make it more efficient?” I’m not trying to tell the candidate their approach is wrong. I just want to understand how they made the decision they made and if they can communicate that to me.&lt;/p&gt;

&lt;p&gt;The third thing I look for is if the candidate can translate their plan into code. In DevRel we teach through our code, so the code we write needs to be simple and idiomatic. As we go I may point out missing semicolons and misspelled words, but I don’t penalize folks for those mistakes. And if the candidate wants to use a library function, I usually allow it as long as the candidate can define the inputs and outputs. I don’t expect the code to be correct the first time through. In fact, I prefer it if a candidate runs into trouble and needs to rethink their original solution. Being able to solve problems, debug, and handle changing requirements are important for success in most tech jobs, and watching a candidate rethink their solution shows how the candidate deals with those challenges.&lt;/p&gt;

&lt;p&gt;After the candidate has completed their code, I may ask for the Big-O runtime analysis. Or I might ask questions about the data structures used in the answer. A lot of people object to these type of questions, and I know they can be intimidating. I ask them because Big-O, data structures, and other CS fundamentals are one way programmers communicate about the merits and drawbacks different solutions. Runtime and space usage aren’t the only things we should use to make decisions but common vocabulary is helpful if we want to avoid “well my gut says Nick’s code is better” as our primary decision-making criteria.&lt;/p&gt;

&lt;h2&gt;
  
  
  Other Thoughts
&lt;/h2&gt;

&lt;p&gt;I’ve written &lt;a href="http://www.thagomizer.com/blog/2017/06/18/there-is-no-perfect-interview.html"&gt;before&lt;/a&gt; about how I think all interview tech processes are awful. I prefer to run interviews as a conversation where we chat about the things you’ve done that are relevant to the job, and then we work together to solve a technical problem. In the past, I’ve run the whiteboard portion of an interview more like a pairing session. I give feedback, spot errors, and help with syntax. But in the 100+ interviews I’ve done, most candidates find the collaborative approach more distracting and nerve-wracking than helpful. So now I sit quietly and say encouraging things like, “Looks like you have a great start, would it make sense to do _____ next?” and “This looks great, keep going.”&lt;/p&gt;

&lt;p&gt;The other big misconception I feel many folks have is that the interview is a confrontation. At least if you are interviewing with me that isn’t the case. As the interviewer it is my job to figure out why we should hire you. I want to find the things you are good at. If you are struggling, I may ask the same question a different way. I want candidates to have multiple chances to succeed. At Google, my feedback goes to a committee of senior folks who make the hiring decision. Since that committee never meets you, I take a lot of notes, many of them verbatim, during our conversation. I refer back to those notes to ensure I’m being fair when I write my recommendations for the committee. I try as much as possible to include direction quotes from the interview so that my error-prone memory isn’t an impediment to folks getting hired. Focusing on the positives and taking extensive notes makes me feel like I’m doing everything in my power to help candidates get a chance at the job.&lt;/p&gt;

&lt;p&gt;I want to close with something I said &lt;a href="http://www.thagomizer.com/blog/2015/08/13/interviewing-tips.html"&gt;a few years ago&lt;/a&gt;. Good interviewers want you to succeed. They aren’t trying to make themselves feel better by watching you struggle. They aren’t looking for reasons to say no. Whenever I make a no-hire recommendation I look back through my notes to see if there was somewhere that I messed up. Hopefully, that is reassuring to you. I know it has been for me.&lt;/p&gt;

</description>
      <category>interview</category>
    </item>
    <item>
      <title>Auntie Aja on Abstracts: You Can Do Better</title>
      <dc:creator>Aja</dc:creator>
      <pubDate>Thu, 25 Jan 2018 22:45:42 +0000</pubDate>
      <link>https://dev.to/thagomizer/auntie-aja-on-abstracts-you-can-do-better-55po</link>
      <guid>https://dev.to/thagomizer/auntie-aja-on-abstracts-you-can-do-better-55po</guid>
      <description>&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=980WxugF3NI"&gt;Auntie Aja&lt;/a&gt; is back. This time I want to talk a bit about conference session proposals. It is CFP season. I’m busy submitting talk proposals to events I want to attend. My coworkers and friends are doing the same. Like many folks, I’m asking coworkers and friends to review my abstracts to make sure I’m not missing anything important. I’m also advising people who don’t submit to events very often on how to improve the chances that their submission will be accepted. Today I’ll cover some of the issues I see over and over. To demonstrate my suggestions, I’ll be improving one of my old abstracts that never got accepted. Here’s the original abstract:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Some years ago several of my friends got together and had a study group for SICP. While I have a CS Degree, I had never written an interpreter and barely knew Scheme, so I learned a lot during this process. That study group spun off several more over the following years. Those study groups made me a better developer, and I’m fortunate that I’ve had that experience.&lt;/p&gt;

&lt;p&gt;In this talk, I’ll tell you how our SICP study group, which finished in under a year, was organized. I’ll also share the list of books we’ve completed since that original study group. I’ll talk about how we organize our code repositories, how often we meet, and what I believe makes a good book or course for a study group to follow.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  It Is About the Attendee
&lt;/h2&gt;

&lt;p&gt;One of the most common issues I see with abstracts is the writer focusing on themselves. It is about their accomplishments, their projects, or their employer. I’ve never seen this go over well with a program committee. Conferences mostly exist for the attendees. Even conferences about a specific product, exist to get the attendees excited about the product and to help them be more successful. So an abstract needs to be focused on the attendee’s experience. Sarah Mei puts this very well:&lt;/p&gt;

&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;Don't mention yourself. The proposal isn't about you. It's about what attendees can do with the information you present.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;— Sarah Mei (@sarahmei) &lt;a href="https://twitter.com/sarahmei/status/682357215978586112?ref_src=twsrc%5Etfw"&gt;December 31, 2015&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;My example proposal uses the words “I” and “we” a lot. Reducing the use of “I” should improve it. The word “we” can be good or bad. If it refers to you and the audience of your talk, as in “&lt;em&gt;We&lt;/em&gt; will build a simple example app,” that’s good. If “we” is just the plural version of “I,” like, “At my employer, &lt;em&gt;we&lt;/em&gt; are the absolute best at checkers,” you should remove it.&lt;/p&gt;

&lt;p&gt;Another part of focusing on the attendee is removing biographical information about yourself and your employer. Your bio will be in the program. You don’t need to put it in the abstract as well. If the biographical information explains why you are qualified to speak on this topic, include it in the part of your application/proposal that only the committee can see.&lt;/p&gt;

&lt;p&gt;Also, try to use words like &lt;em&gt;learn&lt;/em&gt; and &lt;em&gt;improve&lt;/em&gt; as opposed to &lt;em&gt;show&lt;/em&gt; and &lt;em&gt;demonstrate&lt;/em&gt;. When I worked in EdTech, we focused on what students should be able to do after they finished a lesson. Think about your talk the same way. What will attendees be able to try or do after your talk? And make sure your answer isn’t, “After my talk attendees will be on their way to being half as awesome as me.”&lt;/p&gt;

&lt;p&gt;With all that in mind, here’s an improved version of my abstract from above.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Study groups are a great way to learn new things. This talk will teach you everything you need to know to have a successful study group, including how often to meet, where to meet, and what types of books work well for self-teaching. After this talk, you’ll be ready to start a study group at your meetup.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Connect the Dots
&lt;/h2&gt;

&lt;p&gt;Focusing on the attendee improved my abstract some, but I can still do better. If I pretend to be an attendee reading the abstract in a conference program, it is hard to see why the talk is relevant to me. When you are writing an abstract, you are often the worst person in the world do so. You know your topic better than the attendee, and often you’ll leave out the parts that seem obvious to you. Your job is to explain to the attendee why your talk is relevant to them and why they should attend your session instead of others in the same time slot. If you are struggling with this, it may help to use the &lt;a href="https://rubberduckdebugging.com/"&gt;Rubber Duck&lt;/a&gt; technique and imagine your duck is asking “but why should I go to your session instead of taking a nap?” over and over.&lt;/p&gt;

&lt;p&gt;It is also good to think about any other biases you may have. I’ve helped speakers who assume that the attendees are just like them, with the same preferred tools and approach to coding. These speakers are surprised when they get to the event and realize most folks have a different background or perspective than they do. So check your assumptions by doing a bit of research online. You can also look for opportunities to explain how your approach/topic/tool can be generalized to other situations. My proposal above is specifically about a community study group. It feels like I shouldn’t have to explicitly say that you can apply the same lessons to a workplace, but that is a common question I’ve received when talking about study groups. I can improve the abstract by stating it applies to more than just meetups. Here’s another version of the abstract thinking about these tips.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Have you wanted to learn a new technology or language but didn’t make a lot of progress? Study groups can provide accountability and help you meet your learning goals. This talk will teach you how to run a successful study group including, how to structure your group (where and when to meet), what types of materials are good for group study, and how to overcome common hurdles. After this talk, you’ll be ready to start a study group with your coworkers or friends.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Be Clear and Be Exciting
&lt;/h2&gt;

&lt;p&gt;One of the other issues I see in a lot of abstracts is jargon and abbreviations. Once, I observed a technical communication workshop where each participant had five minutes to explain their area of expertise, and anytime they used specialized language someone held up a sign that said “jargon!” Watching that exercise taught me how much technical vocabulary we use without even realizing it.&lt;/p&gt;

&lt;p&gt;Read through your abstract and think about whether any abbreviations need expanding and what technical terms people may not already know. If your talk is for an experienced audience, you can use more technical language than you would in an introductory session. Pay particular attention to terms specific to your organization. Inside Google, we talk about our Ruby gems and Node modules as language libraries. But when I speak to external audiences, I have to remember not to say language library but to say gem, module, SDK, or package depending on the community.&lt;/p&gt;

&lt;p&gt;Finally, it can help to have an exciting or catchy title or abstract. A talk titled, “A technical overview of my API” doesn’t sound particularly exciting. Adding a verb can help. “Introducing: my API a tool to do a thing” is better. It still isn’t great, but it is better. Other verbs I see in many titles are exploring, learning, and building. If you are going to do a cool demo, call that out in your abstract. I remember deciding to attend a talk just because the abstract mentioned a robotic blimp.&lt;/p&gt;

&lt;p&gt;You can take the “be exciting” thing too far. Too many exclamation points and adding unrelated demos will make folks roll their eyes. But program committees want talks that will draw a crowd, so talk about your examples and demos, and don’t be afraid to be a little ridiculous.&lt;/p&gt;

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

&lt;p&gt;There are many more ideas for how to improve your abstracts on the web, these just happen to be the ones on my mind right now. If you’ve reviewed abstracts or given talks what tips do you have for speakers?&lt;/p&gt;

</description>
      <category>cfp</category>
      <category>abstract</category>
      <category>auntieaja</category>
    </item>
    <item>
      <title>Hi, I'm Aja</title>
      <dc:creator>Aja</dc:creator>
      <pubDate>Fri, 06 Jan 2017 17:30:54 +0000</pubDate>
      <link>https://dev.to/thagomizer/hi-im-aja</link>
      <guid>https://dev.to/thagomizer/hi-im-aja</guid>
      <description>&lt;p&gt;I have been coding for [number] years.&lt;/p&gt;

&lt;p&gt;You can find me on GitHub as &lt;a href="https://github.com/thagomizer" rel="noopener noreferrer"&gt;thagomizer&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I live in [city].&lt;/p&gt;

&lt;p&gt;I work for [company]&lt;/p&gt;

&lt;p&gt;I mostly program in these languages: [languages].&lt;/p&gt;

&lt;p&gt;I am currently learning more about [topic].&lt;/p&gt;

&lt;p&gt;Nice to meet you.&lt;/p&gt;

</description>
      <category>introduction</category>
    </item>
  </channel>
</rss>
