<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
<title type="text">Bonnie Eisenman</title>
<generator uri="https://github.com/jekyll/jekyll">Jekyll</generator>
<link rel="self" type="application/atom+xml" href="http://blog.bonnieeisenman.com/feed.xml" />
<link rel="alternate" type="text/html" href="http://blog.bonnieeisenman.com" />
<updated>2024-06-17T17:20:17-04:00</updated>
<id>http://blog.bonnieeisenman.com/</id>
<author>
  <name>Bonnie Eisenman</name>
  <uri>http://blog.bonnieeisenman.com/</uri>
  <email>bonnie.eisenman@gmail.com</email>
</author>


<entry>
  <title type="html"><![CDATA[Rake errors with zsh: no matches found]]></title>
  <link rel="alternate" type="text/html" href="http://blog.bonnieeisenman.com/blog/rake-errors-with-zsh/" />
  <id>http://blog.bonnieeisenman.com/blog/rake-errors-with-zsh</id>
  <published>2024-06-17T00:00:00-04:00</published>
  <updated>2024-06-17T00:00:00-04:00</updated>
  <author>
    <name>Bonnie Eisenman</name>
    <uri>http://blog.bonnieeisenman.com</uri>
    <email>bonnie.eisenman@gmail.com</email>
  </author>
  <content type="html">&lt;p&gt;This blog runs on Jekyll, and I generate my post templates using Rake commands.&lt;/p&gt;

&lt;p&gt;When getting set up on a new laptop I ran into a new issue:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ rake draft[&quot;blog title&quot;]
zsh: no matches found: draft[blog title]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Apple changed the default shell from bash to zsh back in &lt;a href=&quot;https://www.theverge.com/2019/6/4/18651872/apple-macos-catalina-zsh-bash-shell-replacement-features&quot;&gt;2019 with macOS Catalina&lt;/a&gt;. zsh helpfully tries to &lt;a href=&quot;https://zsh.sourceforge.io/Doc/Release/Expansion.html#Glob-Operators&quot;&gt;expand&lt;/a&gt; the square bracket characters (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;]&lt;/code&gt;) and then throws an error when it fails to expand them.&lt;/p&gt;

&lt;p&gt;Uh…wait why was this working on my old laptop? Turns out I’d failed to carry over part of my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.zshrc&lt;/code&gt; which included the line:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# Allows rake[args] commands to work without escaping
alias rake=&apos;noglob rake&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Adding &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;noglob&lt;/code&gt; before invoking rake means that zsh won’t perform glob expansion. Score!!&lt;/p&gt;

&lt;p&gt;It’s nice when fixes are straightforward. And also a good reminder to myself that I should be more careful about keeping my dotfiles repo up to date….&lt;/p&gt;

  &lt;p&gt;&lt;a href=&quot;http://blog.bonnieeisenman.com/blog/rake-errors-with-zsh/&quot;&gt;Rake errors with zsh: no matches found&lt;/a&gt; was originally published by Bonnie Eisenman at &lt;a href=&quot;http://blog.bonnieeisenman.com&quot;&gt;Bonnie Eisenman&lt;/a&gt; on June 17, 2024.&lt;/p&gt;</content>
</entry>


<entry>
  <title type="html"><![CDATA[Avoid concatenation in log statements]]></title>
  <link rel="alternate" type="text/html" href="http://blog.bonnieeisenman.com/blog/avoid-concatenation-in-log-statements/" />
  <id>http://blog.bonnieeisenman.com/blog/avoid-concatenation-in-log-statements</id>
  <published>2024-06-10T00:00:00-04:00</published>
  <updated>2024-06-10T00:00:00-04:00</updated>
  <author>
    <name>Bonnie Eisenman</name>
    <uri>http://blog.bonnieeisenman.com</uri>
    <email>bonnie.eisenman@gmail.com</email>
  </author>
  <content type="html">&lt;p&gt;This is a really small programming “style” best practice that came up at work last week.&lt;/p&gt;

&lt;p&gt;TLDR; don’t pre-concatenate strings in Java log calls!&lt;/p&gt;

&lt;p&gt;I’m working in Java now after a long stint in Scala-land. I used Java in university and at my first few internships but haven’t worked in it since then. So, there are a lot of coding conventions and best practices that I’m having to re-learn…&lt;/p&gt;

&lt;p&gt;One of them is about &lt;strong&gt;logs&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;Fun fact, my very first internship project at Google was about calculating the memory usage of log strings in Gmail. Log strings can be &lt;em&gt;expensive&lt;/em&gt;. Let’s look at some code.&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Version 1 - boo&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Data processed for user &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getId&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Version 2 - yay&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Data processed for user {}&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getId&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Though they look very similar, Version 1 is more expensive than the Version 2.&lt;/p&gt;

&lt;p&gt;Let’s say that you have your logging level configured so that only ERROR level logs are actually logged. However, the concatenation in version 1 will happen &lt;strong&gt;before&lt;/strong&gt; the invocation of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;log.info&lt;/code&gt;. That means that you’re doing extra work on every request to concatenate those strings…and then throw them away! Yuck! If you are processing lots of traffic, all of those extra (useless) string allocations can add up and really impact your newgen memory churn. And worst of all, those strings are unique per user (or message ID, or whatever other variable you’re logging).&lt;/p&gt;

&lt;p&gt;In version 2, the user’s ID is passed as an argument to the logger. The logger will only build the string if it actually needs it. Yay! Much better.&lt;/p&gt;

&lt;p&gt;It feels like a small style nit but these kinds of allocations can have a real impact. Also, logs are a common place to find this kind of wasted work, but the same principle applies generally. “Don’t pre-build unique strings unless you have to” is a good rule of thumb.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Thanks to Sean Wu for discussing this with me in &lt;a href=&quot;https://www.recurse.com/&quot;&gt;Recurse Center&lt;/a&gt; chat!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Related links:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://dba-presents.com/jvm/java/120-use-the-built-in-formatting-to-construct-this-argument&quot;&gt;DBA Presents: Use the Built in Formatting to Construct This Argument&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://stackoverflow.com/questions/42388341/built-in-string-formatting-vs-string-concatenation-as-logging-parameter&quot;&gt;StackOverflow: Built-in string formatting vs string concatenation as logging parameter&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;


  &lt;p&gt;&lt;a href=&quot;http://blog.bonnieeisenman.com/blog/avoid-concatenation-in-log-statements/&quot;&gt;Avoid concatenation in log statements&lt;/a&gt; was originally published by Bonnie Eisenman at &lt;a href=&quot;http://blog.bonnieeisenman.com&quot;&gt;Bonnie Eisenman&lt;/a&gt; on June 10, 2024.&lt;/p&gt;</content>
</entry>


<entry>
  <title type="html"><![CDATA[Slack Workflows Gotchas: Problems with Programming Custom Slack Bots]]></title>
  <link rel="alternate" type="text/html" href="http://blog.bonnieeisenman.com/blog/slack-workflows/" />
  <id>http://blog.bonnieeisenman.com/blog/slack-workflows</id>
  <updated>2024-05-26T00:00:00-00:00</updated>
  <published>2024-05-24T00:00:00-04:00</published>
  
  <author>
    <name>Bonnie Eisenman</name>
    <uri>http://blog.bonnieeisenman.com</uri>
    <email>bonnie.eisenman@gmail.com</email>
  </author>
  <content type="html">&lt;p&gt;I built a Slack bot with a coworker for our internal company hackathon. It was….an experience! Here are some notes that might be useful to other folks.&lt;/p&gt;

&lt;p&gt;Our team runs a company-public “help” channel, which is meant for folks outside of our team to ask questions. Unsurprisingly, many of these questions are repetitive, and answering them is a drain on the on-call engineer’s time. I looked back at the last month of questions and found that ~35% of them were good candidates for an automated response. So, I wanted to build a bot that would automatically reply to certain keywords in a specified channel with a canned response.&lt;/p&gt;

&lt;p&gt;The first hiccup was: how do we interact with Slack? Slack has a lot of different ways to build automations: there are SDKs for standalone apps that interact with Slack via webhooks, there’s the “Workflow Builder” GUI, there are “workflows”, there are “Slack apps”, there’s Slackbot….&lt;/p&gt;

&lt;p&gt;In order to respond to the &lt;a href=&quot;https://api.slack.com/automation/triggers/event&quot;&gt;message_posted event trigger&lt;/a&gt;, you need to build a Workflow (not to be confused with the Workflow Builder). Sometimes the docs refer to this as a “Workflow App.”&lt;/p&gt;

&lt;p&gt;A Workflow is basically a special app that can be deployed to Slack. You can’t put it in the Slack apps directory. It must be written in Typescript, and you don’t run it as a standalone program.&lt;/p&gt;

&lt;p&gt;The entire Workflows developer experience has the vibe of “some folks put a lot of work into this, a while ago.” There are a lot of really cool use-cases for Slack automations, but there are also a bunch of gotchas and rough edges to the API. Here’s some of my notes from working with the Workflows API:&lt;/p&gt;

&lt;h2 id=&quot;the-sample-apps-are-your-friend&quot;&gt;The sample apps are your friend&lt;/h2&gt;

&lt;p&gt;There’s a &lt;a href=&quot;https://github.com/slack-samples&quot;&gt;robust repo of example projects&lt;/a&gt; that are the best point of reference for using the API. While the &lt;a href=&quot;https://api.slack.com/automation/quickstart&quot;&gt;docs&lt;/a&gt; are also useful, I found that the Slack sample apps were the best reference to use.&lt;/p&gt;

&lt;h2 id=&quot;nested-arrays-and-objects-dont-work&quot;&gt;Nested arrays and objects don’t work.&lt;/h2&gt;

&lt;p&gt;We ran into all sorts of bugs when trying to store arrays inside of objects or objects inside of arrays, both when interacting with the datastores but also when just trying to pass parameters between functions.&lt;/p&gt;

&lt;p&gt;There’s an &lt;a href=&quot;https://github.com/slackapi/deno-slack-sdk/issues/250&quot;&gt;open bug&lt;/a&gt; from 2023 where storing an object containing an array doesn’t work. Hopefully that will get fixed eventually?&lt;/p&gt;

&lt;h2 id=&quot;no-conditionals-or-early-termination&quot;&gt;No conditionals or early termination&lt;/h2&gt;

&lt;p&gt;Workflows consist of a series of steps. There’s no way to build in conditional branching logic into a Workflow despite &lt;a href=&quot;https://forums.slackcommunity.com/s/question/0D53a000080b4VtCAI/when-will-ifthen-steps-come-to-workflow-builder-this-was-meant-to-arrive-sometime-in-late-2021-so-just-curious-if-it-will-still-make-it-this-year?language=en_US&quot;&gt;customer&lt;/a&gt; &lt;a href=&quot;https://stackoverflow.com/questions/76282908/support-for-conditional-logic-in-slack-workflow-builder&quot;&gt;demand&lt;/a&gt;. Conditionals in Workflow Builder were announced as an upcoming feature back in &lt;a href=&quot;https://techcrunch.com/2022/09/01/slack-gains-new-automation-features-including-conditional-logic-for-workflows/&quot;&gt;2022&lt;/a&gt;, but there’s been no news about them since.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/slackapi/deno-slack-sdk/issues/276&quot;&gt;There’s also no way to end a workflow early.&lt;/a&gt; This becomes a problem if a necessary precondition is not met, because of how errors are handled.&lt;/p&gt;

&lt;h2 id=&quot;error-handling&quot;&gt;Error handling&lt;/h2&gt;

&lt;p&gt;If your workflow encounters an error, Slackbot will DM the workflow’s admin. There is no way to mute these messages.&lt;/p&gt;

&lt;p&gt;Because of this, it’s really important that you &lt;a href=&quot;https://forums.slackcommunity.com/s/question/0D5Hq00009pJ24vKAC/how-to-silently-handle-errors-in-workflow-automation?language=en_US&quot;&gt;wrap each step in a try/catch&lt;/a&gt; if there’s a chance that it will error.&lt;/p&gt;

&lt;h2 id=&quot;call-the-slack-client-from-a-custom-step-not-from-a-builtin-step&quot;&gt;Call the Slack client from a custom step, NOT from a builtin step.&lt;/h2&gt;

&lt;p&gt;I found that the Slack builtin methods for doing things will throw errors on normal user interactions (e.g. closing a modal window) which will then trigger a DM to the workflow admin. Yikes!&lt;/p&gt;

&lt;p&gt;The docs generally advise you to use one of the builtin Slack functions from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Schema.slack.functions&lt;/code&gt; for interacting with the Slack API, but you can instead use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;client&lt;/code&gt; param which is available in custom functions to call the Slack API.&lt;/p&gt;

&lt;p&gt;For example, the docs might suggest doing this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;MyWorklflow.addStep(
  Schema.slack.functions.SendMessage,
  {
    channel_id: inputs.channel_id,
    message: `Setting topic to ${generateMessageStep.outputs.message}`,
  },
);

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

&lt;p&gt;But instead, we did this from a custom Workflow step:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;let post_args: {
	&quot;channel&quot;: string;
  &quot;text&quot;: string;
        } = {
          channel: inputs.channel_id,
          text: inputs.message,
        };

await client.chat.postMessage(post_args);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Similarly, I really don’t recommend using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Schema.slack.functions.OpenForm&lt;/code&gt; Slack function in your workflows, because it will error if the user closes a modal prematurely.&lt;/p&gt;

&lt;p&gt;Instead you can do something like:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;export default SlackFunction(
  OpenModal,
  async ({ inputs, client }) =&amp;gt; {
    // Open a new modal with the end-user who interacted with the link trigger

    const view: ModalView = {
      &quot;type&quot;: &quot;modal&quot;,
      
      // Note that this ID can be used for dispatching view submissions and view closed events.
      &quot;callback_id&quot;: &quot;first-page&quot;,

      // This option is required to be notified when this modal is closed by the user
      &quot;notify_on_close&quot;: true,
      
      &quot;title&quot;: {
        &quot;type&quot;: &quot;plain_text&quot;,
        &quot;text&quot;: MODAL_TITLE,
      },
      &quot;close&quot;: { &quot;type&quot;: &quot;plain_text&quot;, &quot;text&quot;: &quot;Close&quot; },
      &quot;blocks&quot;: modalBlocksWithRows(inputs.entries),
    };

    const response = await client.views.open({
      interactivity_pointer: inputs.interactivity.interactivity_pointer,
      view: view,
    });
  }
 );
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Not only does calling a custom function let you do bespoke error handling, we also encountered problems where the Slack built-in functions would inexplicably error but our custom functions worked fine. Unclear what was going on there.&lt;/p&gt;

&lt;h2 id=&quot;mismatches-between-cli-help-messages-and-behavior&quot;&gt;Mismatches between CLI help messages and behavior&lt;/h2&gt;

&lt;p&gt;There are a few places where the CLI help messages provide commands that don’t actually work.&lt;/p&gt;

&lt;p&gt;For example, when trying to count the number of entries in a datastore:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ slack datastore count --help
...
# Count all items in a datastore
$ slack datastore count --datastore tasks

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

&lt;p&gt;If you try to actually do this, it will error.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ slack datastore count --datastore response_store
Check .slack/logs/slack-debug-20240514.log for full error logs

🚫 No expression was provided (invalid_datastore_expression)

Suggestion:

Provide a JSON expression in the command arguments
Use slack datastore count --help for an example
Build a new expression using slack datastore count --show --unstable

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

&lt;h2 id=&quot;you-cant-delete-a-slack-workflows-datastore&quot;&gt;You can’t delete a Slack workflow’s datastore.&lt;/h2&gt;

&lt;p&gt;Slack can create a datastore for you, and the CLI provides a nice interface for creating datastores, and adding or removing data. However there is no support for &lt;em&gt;deleting&lt;/em&gt; an entire datastore.&lt;/p&gt;

&lt;p&gt;To remove a datastore, you must remove the datastore from your manifest and force-deploy, then add the datastore back in and re-deploy.&lt;/p&gt;

&lt;h2 id=&quot;jsonl-bulk-put-doesnt-work&quot;&gt;JSONL bulk put doesn’t work?&lt;/h2&gt;

&lt;p&gt;Maybe there’s a way to do this that I haven’t been able to figure out. The docs claim that bulk-put is supported for datastores but I wasn’t able to make this work.&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;https://api.slack.com/automation/datastores&quot;&gt;docs&lt;/a&gt; suggest running this command like so:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;slack datastore bulk-put &apos;{&quot;datastore&quot;: &quot;running_datastore&quot;}&apos; —-from-file /path/to/file.jsonl

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

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--from-file&lt;/code&gt; flag is also documented via the help command in CLI:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ slack datastore bulk-put --help
Create or replace a list of items in a datastore

USAGE
  $ slack datastore bulk-put &amp;lt;expression&amp;gt; [flags]

FLAGS
      --datastore string   the datastore used to store items
      --from-file string   store multiple items from a file of JSON Lines
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;But, it doesn’t work.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ slack datastore bulk-put --datastore &apos;{&quot;datastore&quot;: &quot;response_store&quot;}&apos; --from-file assets/test.jsonl
Check .slack/logs/slack-debug-20240514.log for full error logs

🚫 No expression was provided (invalid_datastore_expression)

Suggestion:

Provide a JSON expression in the command arguments
Use slack datastore bulk-put --help for an example
Build a new expression using slack datastore bulk-put --show --unstable

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

&lt;h2 id=&quot;well-that-sure-was-a-hackathon&quot;&gt;Well, that sure was a hackathon.&lt;/h2&gt;

&lt;p&gt;My coworker and I did eventually get our Slack workflow up and running, and it was even kind of fun! But oof I had trouble figuring out how to get the API working. For being “just” a chat app, Slack is pretty complicated, and unsurprisingly the API surface area is complicated too. Maybe my notes will help you (or, at least, help my future self when I’m debugging this six months from now…).&lt;/p&gt;

  &lt;p&gt;&lt;a href=&quot;http://blog.bonnieeisenman.com/blog/slack-workflows/&quot;&gt;Slack Workflows Gotchas: Problems with Programming Custom Slack Bots&lt;/a&gt; was originally published by Bonnie Eisenman at &lt;a href=&quot;http://blog.bonnieeisenman.com&quot;&gt;Bonnie Eisenman&lt;/a&gt; on May 24, 2024.&lt;/p&gt;</content>
</entry>


<entry>
  <title type="html"><![CDATA[Downloading Patreon videos using yt-dlp]]></title>
  <link rel="alternate" type="text/html" href="http://blog.bonnieeisenman.com/blog/downloading-patreon-videos/" />
  <id>http://blog.bonnieeisenman.com/blog/downloading-patreon-videos</id>
  <published>2023-03-16T00:00:00-04:00</published>
  <updated>2023-03-16T00:00:00-04:00</updated>
  <author>
    <name>Bonnie Eisenman</name>
    <uri>http://blog.bonnieeisenman.com</uri>
    <email>bonnie.eisenman@gmail.com</email>
  </author>
  <content type="html">&lt;p&gt;I subscribe to several Patreons that offer paywalled video content. Some of these are reference materials that I might want to re-watch over and over again, and I find it annoying to have to stream them when I could just download the videos and have them available offline. There are loads of sketchy Chrome and Firefox extensions that purport to let you download these videos, but most of them 1) don’t work and 2) require &lt;em&gt;very&lt;/em&gt; broad permissions. Eventually Reddit led me to a command-line tool that I strongly preferred.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/yt-dlp/yt-dlp&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yt-dlp&lt;/code&gt;&lt;/a&gt; is a CLI tool for downloading video and audio streams. It supports many &lt;a href=&quot;https://github.com/yt-dlp/yt-dlp/blob/master/supportedsites.md&quot;&gt;different sites&lt;/a&gt;, including Vimeo and Patreon. You’ll also need &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ffmpeg&lt;/code&gt; installed for best results. Here are some notes on my personal usage of it.&lt;/p&gt;

&lt;p&gt;My typical command looks like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;yt-dlp --cookies-from-browser firefox &quot;https://www.patreon.com/posts/POST-ID-HERE&quot; -f &quot;bv*[height&amp;lt;=640]+ba&quot; --prefer-free-formats 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Breaking this down:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--cookies-from-browser firefox&lt;/code&gt; does what it sounds like: uses cookies from Firefox. This means, e.g., that if you’re logged in to Patreon, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yt-dlp&lt;/code&gt; will use those cookies.&lt;/p&gt;

&lt;p&gt;The next argument is the URL for the video you’re trying to download. I’ve been downloading videos from individual posts, but supposedly you can also have it download all available videos from a feed. I haven’t bothered figuring that out yet.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-f&lt;/code&gt; flag is used to select which format you want. I found the formatting options to be pretty arcane, to be honest. Most video players these days offer a &lt;em&gt;lot&lt;/em&gt; of different video and audio formats. You can see the available formats by using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--list-formats&lt;/code&gt; flag:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;yt-dlp --cookies-from-browser firefox &quot;https://www.patreon.com/posts/POST-ID-HERE&quot; -v --list-formats

[info] Available formats for 800787707:
ID                                                EXT RESOLUTION FPS │   FILESIZE   TBR PROTO │ VCODEC        VBR ACODEC      ABR ASR MORE INFO
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
hls-akfire_interconnect_quic_sep-audio-high-audio mp4 audio only     │                  m3u8  │ audio only        unknown             audio
hls-fastly_skyfire_sep-audio-high-audio           mp4 audio only     │                  m3u8  │ audio only        unknown             audio
dash-akfire_interconnect_quic_sep-audio-fef31d49  m4a audio only     │ ~ 31.66MiB  191k dash  │ audio only        mp4a.40.2  191k 48k DASH audio, m4a_dash
dash-akfire_interconnect_quic_sep-audio-0ef89a72  m4a audio only     │ ~ 11.11MiB   67k dash  │ audio only        opus        67k 48k DASH audio, m4a_dash
hls-akfire_interconnect_quic-312                  mp4 426x240     30 │ ~ 51.72MiB  312k m3u8  │ avc1.640015  312k video only
dash-fastly_skyfire_sep-video-754f8367            mp4 426x240     30 │ ~ 56.86MiB  343k dash  │ avc1.640015  343k video only          DASH video, mp4_dash
hls-akfire_interconnect_quic-4989                 mp4 1920x1080   30 │ ~827.03MiB 4989k m3u8  │ avc1.64002A 4989k video only

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

&lt;p&gt;Luckily there’s an extensive example section in the &lt;a href=&quot;https://github.com/yt-dlp/yt-dlp/blob/master/README.md#format-selection-examples&quot;&gt;yt-dlp FAQ&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# Download best format that contains video,
# and if it doesn&apos;t already have an audio stream, merge it with best audio-only format
$ yt-dlp -f &quot;bv*+ba/b&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;By using the flag &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-f &quot;bv*[height&amp;lt;=640]+ba&quot;&lt;/code&gt;, we request the best video format with a height &amp;lt;= 640 pixels, and the best audio format. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yt-dlp&lt;/code&gt; will merge them into one file.&lt;/p&gt;

&lt;p&gt;Finally, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--prefer-free-formats&lt;/code&gt; flag is necessary to avoid crashing on an error. This is tracked in issue &lt;a href=&quot;https://github.com/yt-dlp/yt-dlp/issues/4838&quot;&gt;4838&lt;/a&gt; and may just be an intermittent Vimeo problem? Unclear.&lt;/p&gt;

&lt;p&gt;I love finding new CLI tools that just &lt;em&gt;work&lt;/em&gt;.&lt;/p&gt;

  &lt;p&gt;&lt;a href=&quot;http://blog.bonnieeisenman.com/blog/downloading-patreon-videos/&quot;&gt;Downloading Patreon videos using yt-dlp&lt;/a&gt; was originally published by Bonnie Eisenman at &lt;a href=&quot;http://blog.bonnieeisenman.com&quot;&gt;Bonnie Eisenman&lt;/a&gt; on March 16, 2023.&lt;/p&gt;</content>
</entry>


<entry>
  <title type="html"><![CDATA[Don't tolerate -isms in your interviews]]></title>
  <link rel="alternate" type="text/html" href="http://blog.bonnieeisenman.com/blog/dont-tolerate-isms-in-your-interviews/" />
  <id>http://blog.bonnieeisenman.com/blog/dont-tolerate-isms-in-your-interviews</id>
  <published>2020-09-17T00:00:00-04:00</published>
  <updated>2020-09-17T00:00:00-04:00</updated>
  <author>
    <name>Bonnie Eisenman</name>
    <uri>http://blog.bonnieeisenman.com</uri>
    <email>bonnie.eisenman@gmail.com</email>
  </author>
  <content type="html">&lt;p&gt;My organization typically interviews in pairs: two interviewers, one candidate. This is beneficial for several reasons: it helps interviewers calibrate against each other, it frees up one person to take notes while the other asks a question, it helps the candidate meet more potential coworkers during the process. Sometimes it also creates an opportunity for odd observations.&lt;/p&gt;

&lt;p&gt;I’ve had several interviews play out where I ask the technical question….and the candidate directs all of their answers exclusively to my male colleague.&lt;/p&gt;

&lt;p&gt;Oh, dear.&lt;/p&gt;

&lt;p&gt;The first – and most notable – time this happened, my co-interviewer started by verbally nudging the candidate to direct his answers to me. When that didn’t work, my coworker literally picked up his chair, placed it behind mine, and sat back down. The candidate continued to solely address answers to my coworker, craning his neck to avoid looking at me, even as I continued to ask him questions directly.&lt;/p&gt;

&lt;p&gt;Even after that bizarre experience, I was surprised and relieved when my co-interviewer backed me up in the post-interview huddle. The whole time, I’d been questioning myself. This seemed absolutely absurd. Maybe I was making a big deal about something as mundane as eye contact. Maybe I was imagining it! Or, rather, I wished I was imagining it; that would be a nicer world to live in.&lt;/p&gt;

&lt;p&gt;I’ve had several interviews since then where the candidate refuses to address me directly – avoiding eye contact, turning their back to me, sometimes failing to acknowledge that I’ve said anything at all. Invariably, they focus their attention on my (male, white) co-interviewer. It’s infuriating, every time. I’ve also heard enough similar stories, especially from women and Black engineers, that I suspect this is far more common than some people realize.&lt;/p&gt;

&lt;p&gt;This happens notably less often now that I can introduce myself as a staff software engineer. I don’t think our candidate pool has become less sexist. I think that my title means that &lt;a href=&quot;https://leaddev.com/harassers-are-nice-me-and-probably-you&quot;&gt;assholes are more likely to be nice to me&lt;/a&gt;. I’m missing out on an important signal now that I’ve become more senior.&lt;/p&gt;

&lt;p&gt;If your more-junior or less-privileged coworker leads the interview, you might see some interesting behavior from candidates. Have them ask more (or most, or all) of the technical questions. See how the candidate reacts.&lt;/p&gt;

&lt;p&gt;Don’t tolerate discrimination in your interviews. Sexist or racist behavior should be included in your written interview feedback. Condescending or arrogant behavior should be documented. It’s also grounds to end an interview early! I know this feels awkward. I know it’s tempting to come up with reasons to give the candidate the benefit of the doubt. But sexism, racism, and other kinds of discrimination &lt;em&gt;must&lt;/em&gt; be a deal-breaker. Don’t force yourself or your coworkers to endure an hour of flagrant rudeness. And, perhaps most importantly – believe your junior colleagues if and when they report questionable interviewee behavior. They’re the ones most likely to see it, and it’s an important signal.&lt;/p&gt;

  &lt;p&gt;&lt;a href=&quot;http://blog.bonnieeisenman.com/blog/dont-tolerate-isms-in-your-interviews/&quot;&gt;Don't tolerate -isms in your interviews&lt;/a&gt; was originally published by Bonnie Eisenman at &lt;a href=&quot;http://blog.bonnieeisenman.com&quot;&gt;Bonnie Eisenman&lt;/a&gt; on September 17, 2020.&lt;/p&gt;</content>
</entry>


<entry>
  <title type="html"><![CDATA[Is Processing Java?]]></title>
  <link rel="alternate" type="text/html" href="http://blog.bonnieeisenman.com/blog/what-is-processing/" />
  <id>http://blog.bonnieeisenman.com/blog/what-is-processing</id>
  <published>2020-08-21T00:00:00-04:00</published>
  <updated>2020-08-21T00:00:00-04:00</updated>
  <author>
    <name>Bonnie Eisenman</name>
    <uri>http://blog.bonnieeisenman.com</uri>
    <email>bonnie.eisenman@gmail.com</email>
  </author>
  <content type="html">&lt;p&gt;If you’ve delved into programming-for-art before, chances are that you’ve encountered Processing. It’s a phenomenally successful tool for coding for visual art.&lt;/p&gt;

&lt;p&gt;Processing describes itself as “&lt;a href=&quot;https://processing.org/&quot;&gt;a flexible software sketchbook and a language for learning how to code within the context of the visual arts&lt;/a&gt;”. It’s based loosely on Java, but it ships with its own development environment and code editor (the Processing “sketchbook”) and its syntax is not-quite-Java. These changes are primarily aimed at making Processing simpler and more accessible; the target audience has little to no prior programming experience. There’s no need to puzzle over the arcane incantation of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;public static void main(String[] args)&lt;/code&gt; in Processing. Instead, there’s a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;draw()&lt;/code&gt; function that somehow gets magically invoked when you run your “sketch.”&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/processing-gameoflife.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Another simplification: if you use the Processing IDE, you don’t have to deal with imports. All of the files in a given “sketch” directory are somehow available to use. This makes it easy to introduce newcomers to Processing. However, as a programmer, I want to know what’s happening under the hood.&lt;/p&gt;

&lt;p&gt;There’s some light documentation around this (see: &lt;a href=&quot;https://github.com/processing/processing/wiki/FAQ#i-know-java-is-this-java-how-do-i-use-it-that-way&quot;&gt;Is this Java?&lt;/a&gt; from the FAQ) but not a ton of technical detail. For a while, I was still confused – is Processing a separate programming language in its own right, or is it just Java? Can I use a normal Java IDE to write Processing, or do I need to use the Processing IDE?&lt;/p&gt;

&lt;p&gt;As far as I can tell, Processing actually consists of three pieces:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;a Java library&lt;/li&gt;
  &lt;li&gt;a Java pre-processor, which compiles the Processing language into normal Java&lt;/li&gt;
  &lt;li&gt;a coding environment (the “sketchbook” / Processing IDE)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The pre-processor does some interesting things. Most dramatically, imo, it takes all of the code in a single sketch and shoves it into a single class. Any classes you declare in your .pde files are now &lt;em&gt;inner classes&lt;/em&gt;. Also, your files will be concatenated in alphabetical order – so you can end up with e.g. conflicting definitions of variables. All of that content then gets wrapped in more boilerplate, which takes care of things like creating that nice PApplet popup window, running the drawing loop, and importing some classes like PConstants by default. This is neat on some levels (no worrying about imports! You always have access to a PApplet instance!) but can be a real headache if you need to debug something related to the pre-processor’s invisible magic.&lt;/p&gt;

&lt;p&gt;In order to invoke the pre-processor, it seems like you need to either use the Processing IDE, or use one of the existing command-line tools like &lt;a href=&quot;https://github.com/processing/processing/wiki/Command-Line&quot;&gt;processing-java&lt;/a&gt;. There is also a support &lt;a href=&quot;https://py.processing.org/&quot;&gt;Python “mode”&lt;/a&gt; but honestly I don’t know much about how it’s implemented.&lt;/p&gt;

&lt;p&gt;Another option is to use the standalone Java library from any other JVM environment, which means that you could use something like Kotlin, Clojure, or Scala. The tricky bit is that you’ll be missing any magic from the pre-processor or the IDE. Some considerations if you want to use Processing as a standalone library:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;You’ll need to subclass the &lt;a href=&quot;https://processing.github.io/processing-javadocs/core/processing/core/PApplet.html&quot;&gt;PApplet&lt;/a&gt; class.&lt;/li&gt;
  &lt;li&gt;Some of the syntax is slightly different.&lt;/li&gt;
  &lt;li&gt;You’ll need to handle imports on your own. There are magical constants like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PI&lt;/code&gt; that are available in Processing without needing to specify any imports; these are located in the &lt;a href=&quot;https://processing.github.io/processing-javadocs/core/processing/core/PConstants.html&quot;&gt;PConstants&lt;/a&gt; class.&lt;/li&gt;
  &lt;li&gt;The Processing IDE handles a lot of Java classpath issues for you. Processing uses its own version of Java, not whatever Java you have installed on your system. Also, if you want to use the Processing library you’ll need to &lt;a href=&quot;http://pages.cs.wisc.edu/~cs302/labs/EclipseTutorial_Processing/Step_02.html&quot;&gt;add the core.jar file to your classpath&lt;/a&gt;.
&lt;a href=&quot;https://github.com/processing/processing/wiki/Supported-Platforms#java-versions&quot;&gt;Processing 3.x doesn’t support Java 9 or later.&lt;/a&gt; It doesn’t support all Java 8 features, either.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these are insurmountable issues. The most difficult part is that the Processing docs all assume that you’re using the “full” Processing experience of library, pre-processor, and IDE. Luckily, the source code itself is fairly straightforward and well-commented.&lt;/p&gt;

&lt;p&gt;Personally, now that I have a better understanding of the different abstraction layers at play, I’ve really been enjoying using Processing with Scala, using IntelliJ as my IDE and SBT for my build tool. I have a very small and poorly-tested &lt;a href=&quot;https://github.com/bonniee/ProcessingSBTTemplate&quot;&gt;template project&lt;/a&gt; demonstrating this.&lt;/p&gt;

&lt;p&gt;Using Processing outside of the standard “sketchbook” environment means that I can use all of my usual tools. I can write tests for my code, I can organize my code into many classes and files, I can use a full-fledged IDE where I’ve already memorized the keyboard shortcuts. I like using Scala because I already use it at work, so it’s a natural language to think in. You could also use Processing from other JVM languages, like Kotlin or Clojure. As an example, &lt;a href=&quot;https://tylerxhobbs.com/essays&quot;&gt;Tyler Hobbs&lt;/a&gt; uses a Clojure library called &lt;a href=&quot;http://quil.info/&quot;&gt;Quil&lt;/a&gt; for interacting with Processing.&lt;/p&gt;

&lt;p&gt;So, in summary – Processing isn’t Java. It’s a Java library, and some tools on top of that. If you’d rather treat it like a Java library, then as a programmer, we can do that!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Thank you to Michael Noronha and Akiva Leffert for commenting on an early draft of this post.&lt;/em&gt;&lt;/p&gt;

  &lt;p&gt;&lt;a href=&quot;http://blog.bonnieeisenman.com/blog/what-is-processing/&quot;&gt;Is Processing Java?&lt;/a&gt; was originally published by Bonnie Eisenman at &lt;a href=&quot;http://blog.bonnieeisenman.com&quot;&gt;Bonnie Eisenman&lt;/a&gt; on August 21, 2020.&lt;/p&gt;</content>
</entry>


<entry>
  <title type="html"><![CDATA[More than Coding: What is Exploratory Work?]]></title>
  <link rel="alternate" type="text/html" href="http://blog.bonnieeisenman.com/blog/senior-work/" />
  <id>http://blog.bonnieeisenman.com/blog/senior-work</id>
  <published>2020-01-31T00:00:00-05:00</published>
  <updated>2020-01-31T00:00:00-05:00</updated>
  <author>
    <name>Bonnie Eisenman</name>
    <uri>http://blog.bonnieeisenman.com</uri>
    <email>bonnie.eisenman@gmail.com</email>
  </author>
  <content type="html">&lt;p&gt;Software engineering is more than just coding. I hear a lot of questions from early-career engineers about what exactly constitues “work”, and how they’re expected to spend their time. They might say things like:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“I feel like I didn’t get anything done today. I didn’t write any code, I just did a lot of project planning.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“I spent a lot of time today investigating this bug, but I don’t know if I should’ve spent that much time on it.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“I’m not sure what my mentor does. They spend a lot of time in meetings, and it doesn’t seem like they commit much code. What are all those meetings about, anyway?”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“My manager says I’m not really a senior engineer yet, but I don’t know what that means.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This last one might seem unrelated, but I think there’s actually an interesting connection there. Let’s talk about the differences between straightforward, difficult, and exploratory work, and who is expected to do which kinds of work.&lt;/p&gt;

&lt;p&gt;Straightforward work is work where it’s obvious to the team &lt;em&gt;what&lt;/em&gt; needs to be solved and &lt;em&gt;how&lt;/em&gt; it should be done. This is often repetitive; there may be examples of very similar work that can be copied, and the systems involved are probably well documented.&lt;/p&gt;

&lt;p&gt;Difficult work requires a deeper understanding of the systems involved. It may involve undocumented, unusual, or subtly complicated systems. Perhaps it’s a frustratingly elusive bug that needs to be understood and fixed. Or, it may involve implementing a change that doesn’t feel natural given the existing structure of the code. The team is probably clear on &lt;em&gt;what&lt;/em&gt; needs to be solved, but it won’t be obvious &lt;em&gt;how&lt;/em&gt; to solve it.&lt;/p&gt;

&lt;p&gt;Both of the first two are types of &lt;em&gt;implementation&lt;/em&gt; work. In both cases, the team agrees on what needs to be solved.&lt;/p&gt;

&lt;p&gt;Exploratory work is a little different. It’s about framing problems and figuring out what should be done. Initially, both the &lt;em&gt;how&lt;/em&gt; and the &lt;em&gt;what&lt;/em&gt; are unknown; usually you start with a question, or a problem, and see where it takes you. You might start with something like:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“It seems like it takes us a long time to write new features. What’s slowing us down?”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“This system has a lot of bugs and is pretty fragile; it’s making our oncall rotations a nightmare. What should we do about it?”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“The product team wants us to build this new feature, but none of the engineers have worked on this kind of thing before. We need to do some research to figure out what kinds of approaches make sense.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Exploratory work can be extremely impactful - a good recommendation here might save your team weeks, months, or years of wasted effort. It’s also easy to bullshit, because it’s difficult to measure or assess. In order to do this effectively, you generally need deep context on the systems involved as well as your team’s priorities. Figuring out &lt;em&gt;how&lt;/em&gt; to solve the problem might be out of scope entirely - the goal here is to define &lt;em&gt;what&lt;/em&gt; the problem should be.&lt;/p&gt;

&lt;p&gt;I expect successful junior engineers to be able to do straightforward work (#1). Midlevel engineers should be able to tackle difficult work (#2). Senior and staff engineers will be able to do exploratory work (#3).&lt;/p&gt;

&lt;p&gt;(There are other aspects that separate junior/midlevel/senior engineers - for instance, can you work alone? Collaboratively? Can you teach new engineers? Can you lead teams? Can you propose new projects and get buy-in for them? Do you follow through and clean up after yourself? But I’m going to set those aside for now.)&lt;/p&gt;

&lt;p&gt;Depending on your company and your team, the mix of work required will vary wildly between 1/2/3. Some teams will have mostly #1, with some #2, and therefore even the senior engineers will spend most of their time writing code and implementing straightforward projects. My current team is probably a balance of mostly #2 and #3. (The larger/older/more complicated the codebase, the more likely you are to have #2 or #3 work.)&lt;/p&gt;

&lt;p&gt;Exploratory work can overlap strongly with &lt;a href=&quot;https://noidea.dog/glue&quot;&gt;glue work&lt;/a&gt;, as described by Tanya Reilly.&lt;/p&gt;

&lt;p&gt;Much like with glue work, there’s a risk of doing too much exploratory, “problem-framing” early on in your career. It can take a lot of time and not produce a lot of measurable (i.e. promotable) artifacts. Also, your judgment simply won’t be as good, if you haven’t done enough implementation work in your team’s current domain. Often, it’s the pain points encountered while doing implementation work that lead to obvious questions to explore.&lt;/p&gt;

&lt;p&gt;Doing exploratory work rarely helps support a promotion from junior to midlevel engineer, because exploratory work is largely irrelevant to the qualifications of a midlevel engineer. Most companies seem to define midlevel as “can this person accomplish tasks independently, within a reasonable amount of time, with minimal direction?” By contrast, a “senior” engineer is usually roughly defined as “can this person figure out what we should be doing, and make a good argument for it, and implement it?”&lt;/p&gt;

&lt;p&gt;The division between senior and staff is even fuzzier, and depends a lot on what company you work for. As a staff engineer at a large-ish tech company, right now my job consists almost entirely of doing two things. First, I advise other engineers on how to approach implementation tasks, though I don’t usually do implementation myself. This includes a bunch of assorted feedback, mentorship, design review, code review, and brainstorming sessions. Secondly, I do exploratory work for issues that affect multiple teams.&lt;/p&gt;

&lt;p&gt;This means I spend a lot of time learning more about problem domains; learning about individual team’s needs / priorities / motivations; learning about the history of different aspects of the company; and then, finally, synthesizing that into a sufficiently reasonable plan of action and convincing people that my approach makes sense. Sometimes, I do all of this research, only for my recommendation to be “do nothing, for now, for these reasons”. This is weird work! The hardest parts are often invisible, and difficult to verify. It often takes many months, or even years, to evaluate if I’ve done my job well.&lt;/p&gt;

&lt;p&gt;The somewhat surprising takeaway here is that senior engineering work includes a &lt;em&gt;lot&lt;/em&gt; of stuff that doesn’t naturally produce concrete artifacts…but that doesn’t mean it’s unimportant. Exploratory work is real work, too. That doesn’t always mean it’s the best use of your time (implementation has to happen eventually, right?), but don’t discount it as a valuable pursuit.&lt;/p&gt;

  &lt;p&gt;&lt;a href=&quot;http://blog.bonnieeisenman.com/blog/senior-work/&quot;&gt;More than Coding: What is Exploratory Work?&lt;/a&gt; was originally published by Bonnie Eisenman at &lt;a href=&quot;http://blog.bonnieeisenman.com&quot;&gt;Bonnie Eisenman&lt;/a&gt; on January 31, 2020.&lt;/p&gt;</content>
</entry>


<entry>
  <title type="html"><![CDATA[Less-obvious accessibility questions]]></title>
  <link rel="alternate" type="text/html" href="http://blog.bonnieeisenman.com/blog/non-obvious-accessibility/" />
  <id>http://blog.bonnieeisenman.com/blog/non-obvious-accessibility</id>
  <published>2019-05-01T00:00:00-04:00</published>
  <updated>2019-05-01T00:00:00-04:00</updated>
  <author>
    <name>Bonnie Eisenman</name>
    <uri>http://blog.bonnieeisenman.com</uri>
    <email>bonnie.eisenman@gmail.com</email>
  </author>
  <content type="html">&lt;p&gt;Accessibility and ability are both complex spectrums. Many venues that claim to be “ADA accessible” aren’t actually accessible for all attendees with disabilities - or, perhaps, they’re only partly accessible.&lt;/p&gt;

&lt;p&gt;As an example: I have intermittent mobility limitations and chronic pain. When I did a batch at the Recurse Center in its former location, the elevator-accessible door was often deadbolted shut in the morning, long after other participants had arrived. I’d have to balance the tradeoff between taking the stairs (painful, exhausting) versus taking the elevator and then banging on the door until someone let me in (differently painful, exhausting).&lt;/p&gt;

&lt;p&gt;I was involved in a conversation a while back about how venues and event organizers can be more transparent about accessibility. Here are some suggestions I have for less-obvious accessibility questions:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Is there room to sit? Is seating guaranteed?&lt;/li&gt;
  &lt;li&gt;Do chairs have backs?&lt;/li&gt;
  &lt;li&gt;Are attendees permitted to sit? (e.g. many bookstores do not allow customers to sit on the floor)&lt;/li&gt;
  &lt;li&gt;If this is a large venue, what’s the typical distance between activities? Is there a to-scale map available? Can attendees with mobility issues arrive early / leave early?&lt;/li&gt;
  &lt;li&gt;Is there a map of which paths through a space are accessible? Where are there heavy doors, or stairs?&lt;/li&gt;
  &lt;li&gt;What are the acoustics of the space like? Will attendees with sound-processing issues be able to hear? Will there be captioning?&lt;/li&gt;
  &lt;li&gt;Are there outlets available for charging? (e.g. some assistive devices or prosthetics need to be charged)&lt;/li&gt;
  &lt;li&gt;Will attendees be permitted to bring medications? (some music venues have attempted to confiscate my prescription medication)&lt;/li&gt;
  &lt;li&gt;Is there a quiet space available for attendees to relax and recharge?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Obviously, this list doesn’t encompass what I consider the “standard” accessibility questions. (See the &lt;a href=&quot;https://adata.org/publication/temporary-events-guide&quot;&gt;ADA National Network planning guide&lt;/a&gt; for detailed suggestions for event planning.)&lt;/p&gt;

&lt;p&gt;As an organizer, the more information you can provide up-front, the more informed decisions your attendees can make about whether or not a space will be accessible to them at a particular time.&lt;/p&gt;


  &lt;p&gt;&lt;a href=&quot;http://blog.bonnieeisenman.com/blog/non-obvious-accessibility/&quot;&gt;Less-obvious accessibility questions&lt;/a&gt; was originally published by Bonnie Eisenman at &lt;a href=&quot;http://blog.bonnieeisenman.com&quot;&gt;Bonnie Eisenman&lt;/a&gt; on May 01, 2019.&lt;/p&gt;</content>
</entry>


<entry>
  <title type="html"><![CDATA[An Engineer's Formula for Software Estimation]]></title>
  <link rel="alternate" type="text/html" href="http://blog.bonnieeisenman.com/blog/a-software-engineers-guide-to-estimation/" />
  <id>http://blog.bonnieeisenman.com/blog/a-software-engineers-guide-to-estimation</id>
  <published>2018-08-01T00:00:00-04:00</published>
  <updated>2018-08-01T00:00:00-04:00</updated>
  <author>
    <name>Bonnie Eisenman</name>
    <uri>http://blog.bonnieeisenman.com</uri>
    <email>bonnie.eisenman@gmail.com</email>
  </author>
  <content type="html">&lt;p&gt;Software estimation is one of those tasks that can feel like a distraction from the “real” work of engineering. In practice, though, I’ve found that making reasonable estimates for engineering tasks is a really useful - if mysterious - skill. So let’s talk about it!&lt;/p&gt;

&lt;p&gt;I work on a team of ~8 engineers at a large company. Many other teams depend on our work, and we depend on many others. There is always more work to be done than we have time for, so prioritization and communication are crucial. Software estimates are one of the tools we use to help with project planning.&lt;/p&gt;

&lt;p&gt;If I worked at a startup with a single engineering team, like my last job, my estimation process would be really different! But in the kind of environment that I work in today, I find software estimation useful for….&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Sequencing work: What can be parallelized? What work depends on other projects? Do your timelines make sense?&lt;/li&gt;
  &lt;li&gt;Staffing: again, what work can be parallelized? If it’s mostly linear, it doesn’t make sense to assign multiple engineers; conversely, parallelizable work benefits from having multiple engineers implementing it.&lt;/li&gt;
  &lt;li&gt;Forcing prioritization: there’s only so much time in a day (or week, or month), and a limited number of people per team. Having estimates helps you make explicit tradeoffs and identify the highest-impact work.&lt;/li&gt;
  &lt;li&gt;Outward communication: if another team really wants or needs something that I’m going to build, having a rough timeline is useful for setting expectations.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In order to meet the above goals, we don’t need exact estimates. Here’s the granularity that I like to use (also referred to as “t-shirt sizing”):&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;XS: tasks that take less than a day&lt;/li&gt;
  &lt;li&gt;S: a couple of days, no more than a week&lt;/li&gt;
  &lt;li&gt;M: several weeks&lt;/li&gt;
  &lt;li&gt;L: several months&lt;/li&gt;
  &lt;li&gt;XL: several quarters&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The above generally assumes 1 engineer working on a project. In practice, as part of this exercise we’ll identify which pieces can be parallelized. Then we can adjust our wall-clock estimate based on project staffing.&lt;/p&gt;

&lt;p&gt;Of course, some kinds of work can’t be easily estimated. Exploratory work, where you’re working in a new codebase, or hunting for a bug, or doing some kind of novel project, will usually defy estimation. That’s fine! This is more relevant to work where you are already familiar with the systems involved.&lt;/p&gt;

&lt;p&gt;Okay! Now that I’ve gotten the long-winded parts out of the way, let’s break down the formula. Credit to my former coworker &lt;a href=&quot;https://twitter.com/alexcebrian&quot;&gt;Alex Cebrian&lt;/a&gt;, from whom I’m stealing these tips:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;List all the tasks related for this feature. Be as specific as you can, as you would break them into branches.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Assign how much time you think you would need to spend on each task with 1 day minimum. Nothing should be more than 3-4 days, if it is, it should be broken down even further.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Go back to whatever number you assigned on (2) and double it… Really.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Build a dependency graph between tasks. This will help you identify what can be parallelized.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Have you thought about time for code reviews? Go back to (3) and add 30% more, minimum one day.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Have you thought about time for testing? (Both unit tests &amp;amp; manual end-to-end testing.) For features which need end-to-end testing, add at least 1 day each.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;…and that’s it! I can’t stress enough how important steps #3 and #5 are, really, even if the estimate you end up with seems excessive. In practice, I’ve found that this approach does a good job of accounting for all of the unexpected problems that crop up in real software development. Did you need to do a minor refactor to add your new feature? Maybe you discovered a pre-existing bug that needed to get fixed? Or did you CI pipeline break just as you were ready to roll out your feature for testing? Those kinds of issues are entirely normal, so having a buffer for your “unknown unknowns” is really useful.&lt;/p&gt;

&lt;p&gt;Okay. Now you have an estimate. What do you do with it?&lt;/p&gt;

&lt;p&gt;This depends on your organization’s culture around estimation, project planning, and roadmaps, as well as what stakeholders are involved. Maybe you just share this with your manager, or perhaps you need to communicate this with other teams as well. The most nerve-wracking software estimate I’ve been asked to provide ended up being passed along to our CEO.&lt;/p&gt;

&lt;p&gt;That probably sounds a lot scarier than it needs to be. I’ve most often seen these estimates being used to evaluate tradeoffs: “If we build Product A, it might take several months. But we can build Product B in a couple of weeks. Or we could build Product C, which will still take my team a few months, but we can unblock the other teams in about two weeks, and then we can develop in parallel with client engineers….” If your product managers or designers or executives are gung-ho about Product A but would still be happy with B or C, this lets them make more realistic choices about which direction to pursue. Crucially, in order to make those decisions effectively, your estimates don’t need to be precise; they just have to be accurate relative to each other.&lt;/p&gt;

&lt;p&gt;This “formula” definitely isn’t scientific, but it doesn’t need to be. Hopefully, it gives you a starting point the next time someone asks you, “So how long will that take?”&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Thanks to Laura Lindzey, Peter Lyons, Venkatesh Srinivas for their feedback on a draft of this post.&lt;/em&gt;&lt;/p&gt;

  &lt;p&gt;&lt;a href=&quot;http://blog.bonnieeisenman.com/blog/a-software-engineers-guide-to-estimation/&quot;&gt;An Engineer's Formula for Software Estimation&lt;/a&gt; was originally published by Bonnie Eisenman at &lt;a href=&quot;http://blog.bonnieeisenman.com&quot;&gt;Bonnie Eisenman&lt;/a&gt; on August 01, 2018.&lt;/p&gt;</content>
</entry>


<entry>
  <title type="html"><![CDATA[Experiments with Fractal Streets]]></title>
  <link rel="alternate" type="text/html" href="http://blog.bonnieeisenman.com/blog/fractal-streets/" />
  <id>http://blog.bonnieeisenman.com/blog/fractal-streets</id>
  <published>2018-06-17T00:00:00-04:00</published>
  <updated>2018-06-17T00:00:00-04:00</updated>
  <author>
    <name>Bonnie Eisenman</name>
    <uri>http://blog.bonnieeisenman.com</uri>
    <email>bonnie.eisenman@gmail.com</email>
  </author>
  <content type="html">&lt;p&gt;I’ve continued to build on my &lt;a href=&quot;https://blog.bonnieeisenman.com/projects/clojure-puzzles/&quot;&gt;experiments&lt;/a&gt; with two-dimensional generative art using Clojure. Here’s my latest batch of experiments: procedural generation of some squiggly lines. My favorites from this set look sort of like fractal street maps.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/hyphae/14-streets.png&quot; width=&quot;600px&quot; alt=&quot;A blue curve that dips up then down.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;It starts from a handful of seed points, and then grows outward, avoiding collisions and gradually tapering off.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/hyphae/stopmotion.gif&quot; width=&quot;600px&quot; alt=&quot;A blue curve that dips up then down.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Here are some progress shots. To begin with, I drew “lines” of dots; then I actually joined them to make contiguous lines…&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/hyphae/03-branching.png&quot; width=&quot;300px&quot; alt=&quot;A blue curve that dips up then down.&quot; /&gt;
&lt;img src=&quot;/images/hyphae/04-branching.png&quot; width=&quot;300px&quot; alt=&quot;A blue curve that dips up then down.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Experimenting with various parameters leads to interesting differences.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/hyphae/06-factors.png&quot; width=&quot;400px&quot; alt=&quot;A blue curve that dips up then down.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The impact of angle of growth is pretty dramatic.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/hyphae/07-angles.png&quot; width=&quot;300px&quot; alt=&quot;A blue curve that dips up then down.&quot; /&gt;
&lt;img src=&quot;/images/hyphae/08-angles.png&quot; width=&quot;300px&quot; alt=&quot;A blue curve that dips up then down.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Originally I was inspired by &lt;a href=&quot;https://inconvergent.net/generative/hyphae/&quot;&gt;inconvergent’s hyphae&lt;/a&gt; post. Here are some of the more organic-looking images I made.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/hyphae/09-trees.png&quot; width=&quot;300px&quot; alt=&quot;A blue curve that dips up then down.&quot; /&gt;
&lt;img src=&quot;/images/hyphae/10-trees.png&quot; width=&quot;300px&quot; alt=&quot;A blue curve that dips up then down.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;When we smooth things out, we get something pretty interesting.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/hyphae/11-streetlike.png&quot; width=&quot;600px&quot; alt=&quot;A blue curve that dips up then down.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Let’s explore that more.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/hyphae/12-streets.png&quot; width=&quot;600px&quot; alt=&quot;A blue curve that dips up then down.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/hyphae/13-streets.png&quot; width=&quot;600px&quot; alt=&quot;A blue curve that dips up then down.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;If we give them room to grow they fill in quite a bit.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/hyphae/14-streets.png&quot; width=&quot;600px&quot; alt=&quot;A blue curve that dips up then down.&quot; /&gt;
&lt;img src=&quot;/images/hyphae/15-streets.png&quot; width=&quot;600px&quot; alt=&quot;A blue curve that dips up then down.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/hyphae/16-streets.png&quot; width=&quot;600px&quot; alt=&quot;A blue curve that dips up then down.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I’m pretty happy with how this turned out. I don’t know where I’m going to go from here with these sketches, but I think that I want to keep exploring this space of 2D visual, generative art.&lt;/p&gt;

  &lt;p&gt;&lt;a href=&quot;http://blog.bonnieeisenman.com/blog/fractal-streets/&quot;&gt;Experiments with Fractal Streets&lt;/a&gt; was originally published by Bonnie Eisenman at &lt;a href=&quot;http://blog.bonnieeisenman.com&quot;&gt;Bonnie Eisenman&lt;/a&gt; on June 17, 2018.&lt;/p&gt;</content>
</entry>


<entry>
  <title type="html"><![CDATA[What I Know About: Being Sick at Work]]></title>
  <link rel="alternate" type="text/html" href="http://blog.bonnieeisenman.com/blog/being-sick-at-work/" />
  <id>http://blog.bonnieeisenman.com/blog/being-sick-at-work</id>
  <published>2018-03-15T00:00:00-04:00</published>
  <updated>2018-03-15T00:00:00-04:00</updated>
  <author>
    <name>Bonnie Eisenman</name>
    <uri>http://blog.bonnieeisenman.com</uri>
    <email>bonnie.eisenman@gmail.com</email>
  </author>
  <content type="html">&lt;p&gt;Workplace accommodations can be a fraught topic - what are you legally entitled to? How do you figure out what’s reasonable? How do you manage the emotions around &lt;em&gt;needing&lt;/em&gt; help? I’m pretty happy with how I handle my illness / invisible disability / whatever-you-wanna-call-it at work these days, and thought I’d share some notes on what has and hasn’t worked for me.&lt;/p&gt;

&lt;p&gt;My angle on this: I’m American, my experiences are coming from being a software engineer at a tech company. My disability / illness is usually invisible. I appear young and able-bodied. This gives me passing privilege, which is useful in many scenarios, but sometimes a hindrance. (Nobody will offer me their seat on the subway.)&lt;/p&gt;

&lt;p&gt;Specifically, I have an autoimmune disorder, and I deal with chronic pain, mobility limitations, a suppressed immune system, and migraines. Usually this doesn’t impact my work: sitting at a desk is a pretty cushy job. Sometimes it does: I’ve had days where inflammation flare-ups in my joints made me unable to use a keyboard.&lt;/p&gt;

&lt;p&gt;I’ve spent a lot of time working on skills to make it easier for me to exist in a professional environment. This includes figuring out what accommodations I need, how to ask for help, and what to tell my coworkers when they (understandably) have questions.&lt;/p&gt;

&lt;h2 id=&quot;things-that-have-worked-well-for-me&quot;&gt;Things that have worked well for me:&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Stating my limitations / accommodation needs as facts:&lt;/strong&gt;
“I won’t be able to do $ACTIVITY because I have $LIMITATION”. “I’m going to sit down now.” “I need $ACCOMMODATION.”&lt;/p&gt;

&lt;p&gt;My limitations are real, and it’s my responsibility to figure out how to communicate them clearly and succinctly. I try to leave as little room as possible for argument or interpretation, and to use the same tone I’d use for remarking on the weather. Keep things polite, friendly, and impersonal. I’m not making a request when I’m talking about accommodations; I’m stating a need.&lt;/p&gt;

&lt;p&gt;(Unless I’m actually making a request for something that’s not essential to my job, like “Hey, I would love to participate in $EVENT, can you tell me if there will be $TYPE_OF_ACTIVITY? I have $LIMITATION. Thanks!”)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don’t try to hide my limitations&lt;/strong&gt;: because my disability is invisible it’s often tempting to deliberately keep it that way, but this is an antipattern. Example: walking into a very large meeting where all the chairs are taken, and as the room overflows, people have switched to standing in the back of the room. I could also try to stand, but that’s a bad idea. Instead, I will sit on the floor, or - if I think my presence there is actually warranted and relevant to the meeting - I will ask someone to give me their seat. If people look at me funny, that’s fine. I will not pretend to be OK when I am not OK.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Finding compromises:&lt;/strong&gt; it helps if I can find ways to participate in the purpose of an event or task without doing it identically to everyone else. Do I have to stand during standup? No.&lt;/p&gt;

&lt;p&gt;Another example: my department was planning an offsite to a shooting range during a time when I couldn’t comfortably lift…well, anything, really. Definitely not a shotgun. I still wanted to spend social time with my coworkers, though, since I was in a remote office, so I went along on the trip and just didn’t shoot. I didn’t explain myself beforehand to anyone but my manager (maybe I should have? yeah…) and some people were a bit confused. I explained why I wasn’t shooting if they asked. Everyone was good-natured about it. People take their cues from you, if you act like your accommodations are normal other people will tend to react similarly. :)&lt;/p&gt;

&lt;p&gt;Relatedly, this often requires me being okay with making my issues visible.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Being unafraid to ask:&lt;/strong&gt; It’s better to ask for an accommodation and be told “Nope, we can’t do that because $REASON” than to suffer in silence. e.g. I noticed that my back pain was much worse whenever I was oncall, because I had to carry a laptop everywhere for two weeks. The damage I was doing to my back was pretty significant - I’d usually need an entire weekend of bedrest to recover. It took me way too long to realize that I could just…ask…for a second laptop to keep at home, so that I didn’t have to shuttle an extra five pounds back and forth. I felt guilty for asking for a second laptop (??) but once I asked, everything was great. I told my manager I thought it would help, she said “That makes sense!”, I talked to IT, and a week later I had a second laptop. Lo and behold, I no longer injure myself by being oncall.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Participating in planning:&lt;/strong&gt; If I’m the one planning an activity, or running a meeting, or whatever, I can make it suit me. This means: if I’m facilitating standup, we’re doing it in a room with chairs. (I once worked at a place that insisted we do standup meetings with actual standing once…so I would awkwardly drag a barstool over and scowl at the facilitator…that was frustrating.) My manager can’t read minds, and also isn’t nearly as familiar with my problems as I am, so if I can suggest solutions and help with the planning/logistics, everybody wins.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Talking about my disability:&lt;/strong&gt; like I’m doing here! I can pass as able-bodied, which means that if people don’t know that I’m sick, they’ll be pretty confused when I ask for accommodations. It’s better for everyone if it’s not some weird secret that takes them by surprise.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Losing the shame:&lt;/strong&gt; this one is harder, but it’s been important. I’ve cried in front of several of my coworkers; some of them have seen me on the verge of passing out after particularly bad incidents. My illness robs me of the privacy around medical stuff that I might otherwise wish I had. Treating this as matter-of-factly as I can makes it easier for me, and, I suspect, for them as well. When my coworkers ask questions about my condition, I answer as concisely and straightforwardly as possible. This is much easier now that I have a formal diagnosis.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Believing other people:&lt;/strong&gt; similarly, I try to model the behavior I want. I believe people when they describe their own experiences or symptoms. I remember that it took me a decade to get a diagnosis, and many people have confusing and sometimes contradictory stories about their own conditions. When I’m planning an event, I explicit ask for things like accommodation needs, and I try to take everyone’s preferences and needs into account.&lt;/p&gt;

&lt;h2 id=&quot;things-that-have-not-worked-well&quot;&gt;Things that have not worked well:&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Snark:&lt;/strong&gt; I have a really dark sense of humor about my illness that other people don’t share - which makes sense! The line between “totally funny joke about me bleeding out” and “horrifying, triggering story” is really not obvious to people I don’t have a deep personal relationship with. And frankly, it’s probably not professional of me to let that bleed into the workplace. (Heh.)&lt;/p&gt;

&lt;p&gt;I do fall back on snark when people start insisting that I can actually do $THING. Memorable conversation: my company was doing a trip to Six Flags. I love theme parks because roller coasters are awesome, but they involve way too much walking for me. One of my coworkers was really insistent on me going, so I finally snapped, “$NAME, I’ll go if you’ll push my wheelchair the whole day!” This was Very Awkward, but IMO I was just surfacing the existing awkwardness: my very well-meaning coworker was &lt;strong&gt;already making it awkward&lt;/strong&gt; by not believing me about my own abilities. Sometimes a Bad Joke can snap people into realizing what they’re doing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Over-justifying myself:&lt;/strong&gt; like many people, I get nervous about taking sick days, asking for accommodations, etc. “But I’m not REALLY sick,” I’ll think. “If I was just less LAZY I could SUCK IT UP and keep working.” Or, “If I just TRY HARDER, I can TOTALLY stand long enough to last through this meeting.” Sometimes this results in me over-justifying why I’m taking a sick day and sharing personal health details with way too broad an audience. Chill out. If I treat myself with suspicion, other people will too. Be honest, be direct, but also be concise rather than oversharing.&lt;/p&gt;

&lt;p&gt;Speaking of which…&lt;strong&gt;oversharing&lt;/strong&gt;! My idea of “normal mundane detail” is, for some people, “horrifyingly traumatic story”. It’s not fair of me to inflict emotional distress on my coworkers just because they asked how my weekend was.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Trying to pretend nothing’s wrong:&lt;/strong&gt; this is misguided and always screws me over eventually. It’s true that I can’t take a sick day every time I feel bad, but I also need to be honest about my limitations. Powering through will not make me a more productive worker. (No, self, you shouldn’t keep working when you have a migraine. All that code you write will be SHIT.)&lt;/p&gt;

&lt;p&gt;Reframing this problem from “I’m lazy if I stop working” to “I have a responsibility to take care of myself and I will be more productive this way” has been very important to me. This one will vary a lot based on your team culture, your company’s policies around stuff like sick leave, and your particular disability/illness. Being able to say “in order to take care of myself, I need to rest today” is an immense privilege, and one I don’t take for granted. Still, even without super-generous sick-leave, being honest about what’s wrong is helpful. Maybe you can’t take a day off, but can you control something else about your work environment? (Take painkillers when you need them? Take a few minutes to do stretches or physical therapy exercises in your office bathroom? Work in a conference room with the lights off if you’re feeling photosensitive?)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hoping that other people will remember my limitations, and feeling betrayed/sad/offended when they don’t:&lt;/strong&gt; I am the foremost expert on myself and it’s not reasonable to expect other people to 1) remember everything I tell them or 2) to internalize it. Healthy, able-bodied people might not realize that by scheduling a lunch meeting two floors away from the cafeteria, I will now have to plan around how to carry a tray of food through the building, because for me a lunch tray is actually a heavy object. It’s my responsibility to communicate directly when something doesn’t meet my needs, and to not assume fault or malice when someone forgets my needs or - much more common - just hasn’t internalized all of the implications.&lt;/p&gt;

&lt;h2 id=&quot;tldr-communication-is-important-️&quot;&gt;TLDR; communication is important. ❤️&lt;/h2&gt;

&lt;p&gt;A lot of this boils down to: be honest, be direct, be kind (to yourself, and to others). My coworkers &lt;em&gt;want to be nice people&lt;/em&gt;. Being upfront about my needs and limitations makes it easier for them to be kind to me.&lt;/p&gt;

&lt;p&gt;I’ve been really lucky to work with some fantastic people, who have supported me through tough times - and also have done me the huge favor of treating me like a Normal Person. I don’t want to be an Inspirational Cripple, yaknow? Everyone has needs, everyone has preferences, and learning to navigate them with honesty and sensitivity has improved my life a lot.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Thank you to Veit Heller, Julia Evans, Andrew Boot, and Vijith Assar for providing feedback on a draft of this post.&lt;/em&gt;&lt;/p&gt;

  &lt;p&gt;&lt;a href=&quot;http://blog.bonnieeisenman.com/blog/being-sick-at-work/&quot;&gt;What I Know About: Being Sick at Work&lt;/a&gt; was originally published by Bonnie Eisenman at &lt;a href=&quot;http://blog.bonnieeisenman.com&quot;&gt;Bonnie Eisenman&lt;/a&gt; on March 15, 2018.&lt;/p&gt;</content>
</entry>


<entry>
  <title type="html"><![CDATA[Notes on Modifying Unowned Code]]></title>
  <link rel="alternate" type="text/html" href="http://blog.bonnieeisenman.com/blog/unowned-code/" />
  <id>http://blog.bonnieeisenman.com/blog/unowned-code</id>
  <published>2018-02-17T00:00:00-05:00</published>
  <updated>2018-02-17T00:00:00-05:00</updated>
  <author>
    <name>Bonnie Eisenman</name>
    <uri>http://blog.bonnieeisenman.com</uri>
    <email>bonnie.eisenman@gmail.com</email>
  </author>
  <content type="html">&lt;p&gt;Recently, I needed to add a new feature to an unowned codebase at work.&lt;/p&gt;

&lt;p&gt;What do I mean when I say unowned code? I think of unowned codebases as having a few qualities:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;There are no planned improvements or changes to the code.&lt;/li&gt;
  &lt;li&gt;The original author(s) are unavailable for questions. (Perhaps no longer working for the company, or nobody knows who wrote it, etc.)&lt;/li&gt;
  &lt;li&gt;While there may be people who have familiarity with the codebase, nobody is responsible for it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;How does this happen? It’s not such an odd thing. You write some software, it’s useful, it sticks around. But once it’s feature-complete, or close enough—you move on. Not all software needs constant changes. Who owns it? Not your team, because they’re not investing in it anymore. It carries on, stewardless, borne ceaselessly into the future…&lt;/p&gt;

&lt;p&gt;I don’t think it’s a bad thing to have unowned code lying around, but sometimes you do need to make changes! What happens then?&lt;/p&gt;

&lt;p&gt;In my particular case, I poured myself a cup of coffee and settled in to read the code. My change was small in scope; I wanted an existing tool to handle a new kind of file format. The codebase that I needed to dive into was about 1,500 lines of Ruby.&lt;/p&gt;

&lt;p&gt;I was reasonably lucky, because:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;I knew that this code is currently in a good state, i.e., it’s working (and has even been re-deployed recently! amazing!)&lt;/li&gt;
  &lt;li&gt;There is a README, explaining how to get it running locally, and it mostly works&lt;/li&gt;
  &lt;li&gt;There are comments in the code, and the structure is generally reasonable&lt;/li&gt;
  &lt;li&gt;There’s one other person left at the company (!) who’s read the code and figured out how to run &amp;amp; deploy it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That being said, there are some smelly bits. Not surprising, for a single-author, unowned codebase:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;There are TODOs scattered around that obviously will never be cleaned up.&lt;/li&gt;
  &lt;li&gt;You can see the organic growth of this code: there are layers of abstraction that probably built up as the author understood the problem space better. (I think this is a common single-author problem. Peer code review tends to strip this kind of scaffolding away.)&lt;/li&gt;
  &lt;li&gt;It’s difficult to fully onboard a developer - you need to track down one person at the company to get proper permissions setup (for getting a functioning developer environment with staging, deploying permissions, etc).&lt;/li&gt;
  &lt;li&gt;There are no tests.&lt;/li&gt;
  &lt;li&gt;There is a lot of dead code which never gets executed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tackling an existing system like this isn’t so bad, especially when it’s reasonably-sized. Here’s how I handled it:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Developer setup: can I run it in its current state?&lt;/li&gt;
  &lt;li&gt;Look through the git history to figure out if anyone still at the company has modified the code (there’s one other person, perfect)&lt;/li&gt;
  &lt;li&gt;Read the source code to figure out how the code is structured, and where I should add my new feature
    &lt;ul&gt;
      &lt;li&gt;this involves a combination of “start at the beginning” and reading from the entry point inward, as well as judicious use of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grep&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;incidentally, I’m not super familiar with Ruby, so I end up learning a lot about things like blocks&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Add my new feature, implementing it in tiniest-possible pieces and testing frequently on my local machine&lt;/li&gt;
  &lt;li&gt;Discover bugs in the existing code, and fix them&lt;/li&gt;
  &lt;li&gt;Continue implementing my new feature&lt;/li&gt;
  &lt;li&gt;Manually test that my new functionality works&lt;/li&gt;
  &lt;li&gt;Write some tests for the bugs I found, to prove that they haven’t snuck back in&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At this point, I now had Choices! I could clean up this code. In fact, as the most recent editor, I probably had the most context on this codebase, because I’d already dumped it into my head.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;I could write tests for all of it.&lt;/li&gt;
  &lt;li&gt;I could do some cleanup, like deleting the unused code.&lt;/li&gt;
  &lt;li&gt;I could leave it as-is, once I’ve added my new feature.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Option #3 would contribute directly to the poor quality of the codebase, and in fact make it worse (there is now more code in this unowned little codebase, bah).  But! It might be the right choice for the business. I had higher-priority things on my plate, and I could fully justify leaving this alone.&lt;/p&gt;

&lt;p&gt;Buuuuut…..Option #2 isn’t substantially more effort. So that’s what I did: add a couple of tests around the new feature, delete some of the unused code, and call it a day.&lt;/p&gt;

&lt;p&gt;This was a fun detour from my usual job! I enjoyed the process of dumping a bunch of new code into my brain and then poking at it until it did what I wanted, and hopefully I left things in a better state than I found them in. (But really…who knows?)&lt;/p&gt;

&lt;p&gt;Every time I have to do this I remember that new codebases don’t have to be particularly scary; they just require patience, and some time to explore and poke around. Just because there isn’t a map doesn’t mean that you’ll get lost!&lt;/p&gt;


  &lt;p&gt;&lt;a href=&quot;http://blog.bonnieeisenman.com/blog/unowned-code/&quot;&gt;Notes on Modifying Unowned Code&lt;/a&gt; was originally published by Bonnie Eisenman at &lt;a href=&quot;http://blog.bonnieeisenman.com&quot;&gt;Bonnie Eisenman&lt;/a&gt; on February 17, 2018.&lt;/p&gt;</content>
</entry>


<entry>
  <title type="html"><![CDATA[SVG Jigsaw Generation in Clojure]]></title>
  <link rel="alternate" type="text/html" href="http://blog.bonnieeisenman.com/projects/clojure-puzzles/" />
  <id>http://blog.bonnieeisenman.com/projects/clojure-puzzles</id>
  <published>2018-01-13T00:00:00-05:00</published>
  <updated>2018-01-13T00:00:00-05:00</updated>
  <author>
    <name>Bonnie Eisenman</name>
    <uri>http://blog.bonnieeisenman.com</uri>
    <email>bonnie.eisenman@gmail.com</email>
  </author>
  <content type="html">&lt;p&gt;I spent the last week learning Clojure and generating jigsaw puzzles as part of my one-week programming retreat at the &lt;a href=&quot;https://www.recurse.com/&quot;&gt;Recurse Center&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Why jigsaw puzzles? I was motivated by two things: first, I wanted a good-sized language-learning project. Secondly, I was heavily inspired by the amazing, beautiful, intricate jigsaw puzzles produced by &lt;a href=&quot;https://n-e-r-v-o-u-s.com/shop/line.php?code=12&quot;&gt;Nervous Systems&lt;/a&gt; and wanted to experiment with similar-ish generative methods. (I’m a sucker for generative things and hadn’t played with generative algorithms too much before.) If you want a crazy cool puzzle, seriously, go buy one from Nervous.&lt;/p&gt;

&lt;p&gt;I think they turned out well!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/svg-puzzles/11-cat-whimsy.png&quot; width=&quot;350px&quot; alt=&quot;A black-and-white outline of a jigsaw puzzle. There&apos;s a cat shaped piece in the top left.&quot; /&gt;
&lt;img src=&quot;/images/svg-puzzles/puzzle-cat-piece.JPG&quot; width=&quot;250px&quot; alt=&quot;A hand holding the cat-shaped puzzle piece.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Because I have access to &lt;a href=&quot;https://www.nycresistor.com/laser/&quot;&gt;NYC Resistor’s laser cutter&lt;/a&gt;, the obvious thing to do was generate SVGs which I could then laser cut. If you haven’t worked with SVG before, it’s an XML-based format for describing vector graphics. It’s pretty easy to generate “by hand”.&lt;/p&gt;

&lt;p&gt;Here is what an SVG looks like, if you open it up with a text editor:&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; &lt;span class=&quot;nt&quot;&gt;&amp;lt;svg&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;100&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;height=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;100&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;circle&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;cx=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;50&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;cy=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;50&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;r=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;40&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;stroke=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;green&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;stroke-width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;4&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;fill=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;yellow&quot;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This produces a yellow circle, centered at (50, 50), with a radius of 40, and a four-pixel green outline. You can view an SVG file in any web browser, or edit it in an editor like Inkscape.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/svg-puzzles/00-basic-circle.png&quot; alt=&quot;A yellow circle&quot; /&gt;&lt;/p&gt;

&lt;p&gt;See? Easy-peasy.&lt;/p&gt;

&lt;p&gt;I started by generating a “classic” jigsaw puzzle shape, and figuring out how to tile it.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/svg-puzzles/01-simple.jpg&quot; alt=&quot;A single classic jigsaw puzzle piece.&quot; width=&quot;100px&quot; /&gt;
&lt;img src=&quot;/images/svg-puzzles/02-grid.jpg&quot; alt=&quot;A grid of mostly-identical jigsaw puzzles&quot; width=&quot;500px&quot; /&gt;&lt;/p&gt;

&lt;p&gt;But I wanted something more interesting than just a grid of similar puzzle pieces! My next step was to use a &lt;a href=&quot;https://en.wikipedia.org/wiki/Voronoi_diagram&quot;&gt;Voronoi diagram&lt;/a&gt; to draw more irregularly-sized polygons around “seed” points. At first this created some amusing failures:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/svg-puzzles/03-blooper-voronoi.jpg&quot; alt=&quot;A grid of orange dots and some black lines between them. Something looks weird - the lines skew off in random directions!&quot; width=&quot;500px&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Oops. This is what happens when you draw points at (x, x) instead of (x, y). Let’s fix those coordinates.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/svg-puzzles/04-voronoi.jpg&quot; alt=&quot;A grid of orange dots, surrounded by black lines representing voronoi edges. Now they actually are enclosed cells, like they&apos;re supposed to be.&quot; width=&quot;500px&quot; /&gt;&lt;/p&gt;

&lt;p&gt;If we replace those straight lines with puzzle-piece edges, we get something that starts to look like a more interesting puzzle. There are still obviously flaws to be ironed out here (e.g. edge overlap).&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/svg-puzzles/05-simple-voronoi-tiling.jpg&quot; alt=&quot;Similar to the previous image, polygons constructed by voronoi tiling then have their edges deformed using puzzle-piece-like squiggles. There is some overlap between lines so this would not make a good puzzle.&quot; width=&quot;500px&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I wanted to make more novel puzzle piece shapes, though, so I turned to the SVG &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;path&lt;/code&gt; type. You can draw Bézier curves in SVG pretty easily:&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;path&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;d=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;M 0 0 C 0 -100 50 -100 50 0 S 100 100 100 0&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;stroke=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;blue&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;fill=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;transparent&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;transform=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;translate(0 400)&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/images/svg-puzzles/06-01-bezier-blue.png&quot; width=&quot;200px&quot; alt=&quot;A blue curve that dips up then down.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;OK, here’s what it looks like when we replace our puzzle piece shape with some random-ish curves:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/svg-puzzles/07-simple-bezier-tiling.jpg&quot; width=&quot;500px&quot; alt=&quot;More puzzle pieces, now with squiggles.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Add more curves and it gets even better!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/svg-puzzles/08-many-squiggles-tiling.jpg&quot; width=&quot;500px&quot; alt=&quot;Very squiggly pieces.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I also experimented with variations on how to place the seed points for my puzzle generation. Here’s one that’s based on a circular point distribution.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/svg-puzzles/12-circular.png&quot; width=&quot;500px&quot; alt=&quot;A puzzle arranged by concentric circles of squiggles.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Now I had some monstrously-irregular puzzle pieces to play with. Cool! I wanted to take it one step further by implementing whimsy pieces. In jigsaw jargon, a &lt;a href=&quot;https://www.wentworthpuzzles.com/articles/what_is_a_whimsy_piece.htm&quot;&gt;whimsy&lt;/a&gt; piece is a themed, recognizably-shaped puzzle piece. They might be butterflies or people or letters or…you name it!&lt;/p&gt;

&lt;p&gt;I modified my puzzle-generator to clear space for a whimsy piece, first testing it with circular whimsy pieces.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/svg-puzzles/09-01-whimsy-clearing.png&quot; width=&quot;500px&quot; alt=&quot;A puzzle with two circular pieces placed inside it.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Then, using a &lt;a href=&quot;https://en.wikipedia.org/wiki/K-d_tree&quot;&gt;kd-tree&lt;/a&gt;, I identified the whimsy piece’s nearest-neighbors and connected it back to the rest of the puzzle. Here’s a cat!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/svg-puzzles/11-cat-whimsy.png&quot; width=&quot;500px&quot; alt=&quot;Same image as earlier - a puzzle with a cat-shaped piece in it.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;And, finally, I took these files over to NYC Resistor and lasered them.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/svg-puzzles/lasering.gif&quot; alt=&quot;Laser cutting cutting puzzle pieces into white acrylic&quot; /&gt;
&lt;img src=&quot;/images/svg-puzzles/puzzle-cat.jpg&quot; alt=&quot;The completed puzzle, with the cat whimsy removed.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;It took a group of us about ninety minutes to solve the cat puzzle. Not bad for four days’ work!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/svg-puzzles/puzzle-cat-playtesting.jpg&quot; alt=&quot;Several people gathered around the puzzle, working on solving it.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;All of the code is available on Github at &lt;a href=&quot;https://github.com/bonniee/svg-puzzle-gen&quot;&gt;bonniee/svg-puzzle-gen&lt;/a&gt;. (It’s my first Clojure program, so I’m sure there are plenty of non-idiomatic things happening there.)&lt;/p&gt;

&lt;p&gt;Dependencies / thank-yous:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/trystan/voronoi-diagram&quot;&gt;trystan/voronoi-diagram&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/abscondment/clj-kdtree&quot;&gt;abscondment/clj-kdtree&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://n-e-r-v-o-u-s.com/shop/line.php?code=12&quot;&gt;Nervous Systems&lt;/a&gt; and their associated &lt;a href=&quot;https://n-e-r-v-o-u-s.com/projects/albums/generative-puzzle-system/&quot;&gt;blog posts&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://shop.oreilly.com/product/0636920034292.do&quot;&gt;Living Clojure&lt;/a&gt;, the textbook I used to learn Clojure&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.recurse.com/&quot;&gt;Recurse Center&lt;/a&gt;, for providing an inspiring and uplifting space in which to explore/learn/play&lt;/li&gt;
  &lt;li&gt;The various people who pair-programmed with me on this puzzle! You’re all awesome :)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Testimonials from playtesters:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;“This is awesome!”&lt;/li&gt;
  &lt;li&gt;“This is horrible!”&lt;/li&gt;
  &lt;li&gt;“This is amazing! And by amazing I mean terrible!”&lt;/li&gt;
  &lt;li&gt;“Why are all the puzzle pieces the same ???”&lt;/li&gt;
  &lt;li&gt;“Is this supposed to be evil?”&lt;/li&gt;
  &lt;li&gt;“How can I get one?”&lt;/li&gt;
&lt;/ul&gt;


  &lt;p&gt;&lt;a href=&quot;http://blog.bonnieeisenman.com/projects/clojure-puzzles/&quot;&gt;SVG Jigsaw Generation in Clojure&lt;/a&gt; was originally published by Bonnie Eisenman at &lt;a href=&quot;http://blog.bonnieeisenman.com&quot;&gt;Bonnie Eisenman&lt;/a&gt; on January 13, 2018.&lt;/p&gt;</content>
</entry>


<entry>
  <title type="html"><![CDATA[Books 2017]]></title>
  <link rel="alternate" type="text/html" href="http://blog.bonnieeisenman.com/blog/2018-01-01-books-2017/" />
  <id>http://blog.bonnieeisenman.com/blog/2018-01-01-books-2017</id>
  <published>2018-01-01T00:00:00-05:00</published>
  <updated>2018-01-01T00:00:00-05:00</updated>
  <author>
    <name>Bonnie Eisenman</name>
    <uri>http://blog.bonnieeisenman.com</uri>
    <email>bonnie.eisenman@gmail.com</email>
  </author>
  <content type="html">&lt;p&gt;This year I read 44 books by 24 different authors, including one 15-book series. Most importantly, though - this year I really dove back into reading for &lt;em&gt;fun&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/books/books-2017-pt1.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/books/books-2017-pt2.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/books/books-2017-pt3.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Fifteen of those authors were female, and nine were male; 75% of the books I read were by female authors. When I first started tracking my numbers on this I was habitually reading works almost exclusively by male authors, so I’m happy that this happened without any intentional action on my part! I’m doing much worse when it comes to other sorts of diversity; only 11% of the books I read were by authors of color.&lt;/p&gt;

&lt;p&gt;While the vast majority of the books I read were science fiction, I also expanded a bit beyond my usual zone: I read comics, graphic novels, poetry, business books, relationship and communication books, and a couple of other nonfiction books.&lt;/p&gt;

&lt;p&gt;Eighteen of the books I read were a direct result of my boyfriend’s recommendations. (Predictable side effect of dating an amateur librarian, I guess.) One of the books was literally handed to me by someone on the street near my apartment, when I stumbled into the &lt;a href=&quot;http://www.brooklynbookfestival.org/&quot;&gt;Brooklyn Book Festival&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There’s another pattern that I noticed.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/books/books-2013-2017-books.png&quot; /&gt;
&lt;img src=&quot;/images/books/books-2013-2017-pages.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;2016 was, incidentally, the year that reading became &lt;em&gt;pleasant&lt;/em&gt; again, as the neurological impacts of my illness started to recede.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://media.giphy.com/media/xUySTUZ8A2RJBQitEc/giphy.gif&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;would-recommend&quot;&gt;Would recommend&lt;/h2&gt;

&lt;p&gt;Of the books I read in 2017, here are some of my favorites / the ones I’d recommend to others:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The Body Keeps Score&lt;/em&gt; is a painfully fascinating account of the physical impact of trauma on the body, including the many-years-long aftereffects that can persist long after trauma has passed. It’s accessible to a general reader, but it’s not really a pop-sci book; it draws on a mixture of clinical stories and current research. It was a very emotional read for me, and should come with plenty of trigger/content warnings, but I highly recommend it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The Vorkosigan Saga&lt;/em&gt;: a crippled, overachieving kid on a backwater planet accidentally becomes a space pirate; shenanigans ensue. The books get progressively better-written as the series progresses, and several of the later ones have won Hugo awards. I recommend beginning with &lt;em&gt;The Warrior’s Apprentice&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;All Systems Read&lt;/em&gt;: a cyborg has feelings. Mostly, it wants to be left alone to watch its soap operas.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Never Split the Difference&lt;/em&gt;: a bombastic, entertaining book on negotiation.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The Lifespan of a Fact&lt;/em&gt;: This one gets recommended for sheer novelty of format. It’s a dialogue on fact-checking and “literary” non-ficiton, played out in the commentary on a dubiously-factual essay.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Nimona&lt;/em&gt;: It’s a funny graphic novel about a monster-girl and the “villain” she teams up with.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The Impossible Fairy Tale&lt;/em&gt;: a surreal horror story involving elementary school children, neglect, and anger. The imagery reminded me of Nabokov’s early works. It’s a translation from the Korean; I wonder what it’s like in the original.&lt;/p&gt;

&lt;h2 id=&quot;onward&quot;&gt;Onward&lt;/h2&gt;

&lt;p&gt;Some things I want to do in 2018:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Keep reading. God, it feels good to have spoons to do this again.&lt;/li&gt;
  &lt;li&gt;Read at least 1 technical book (i.e. finish Land of Lisp? Maybe?)&lt;/li&gt;
  &lt;li&gt;Read at least 1 foreign-language book. (Kid’s books count.)&lt;/li&gt;
  &lt;li&gt;Use the library more.&lt;/li&gt;
  &lt;li&gt;Don’t finish any books out of a sense of obligation. Keep it fun.&lt;/li&gt;
&lt;/ul&gt;


  &lt;p&gt;&lt;a href=&quot;http://blog.bonnieeisenman.com/blog/2018-01-01-books-2017/&quot;&gt;Books 2017&lt;/a&gt; was originally published by Bonnie Eisenman at &lt;a href=&quot;http://blog.bonnieeisenman.com&quot;&gt;Bonnie Eisenman&lt;/a&gt; on January 01, 2018.&lt;/p&gt;</content>
</entry>


<entry>
  <title type="html"><![CDATA[Lisp Learning, Part 1: Generating a Circular SVG]]></title>
  <link rel="alternate" type="text/html" href="http://blog.bonnieeisenman.com/blog/lisp-learning/" />
  <id>http://blog.bonnieeisenman.com/blog/lisp-learning</id>
  <published>2017-12-29T00:00:00-05:00</published>
  <updated>2017-12-29T00:00:00-05:00</updated>
  <author>
    <name>Bonnie Eisenman</name>
    <uri>http://blog.bonnieeisenman.com</uri>
    <email>bonnie.eisenman@gmail.com</email>
  </author>
  <content type="html">&lt;p&gt;I’m going to be participating in the &lt;a href=&quot;https://www.recurse.com/&quot;&gt;Recurse Center&lt;/a&gt; one week mini-batch in January. Recurse Center describes itself as &lt;em&gt;“a self-directed, community-driven educational retreat for programmers in New York City.”&lt;/em&gt; For the mini-batch, my proposed project was to 1. learn some lisp and 2. generate SVGs of jigsaw puzzles, for laser cutting.&lt;/p&gt;

&lt;p&gt;Why these projects?&lt;/p&gt;

&lt;p&gt;I’ve been wanting to learn Lisp for a while, because I thought that it would be a different kind of problem-solving experience - I like making my brain bend and the Lispers I know have different problem-solving instincts than me. I was also curious about it from a historical POV. Apparently it’s been around &lt;a href=&quot;https://en.wikipedia.org/wiki/Lisp_(programming_language)&quot;&gt;since the 1950s&lt;/a&gt;? Though I guess that depends on which dialect we’re talking about; Common Lisp, which is what I’m learning, dates from the &lt;a href=&quot;https://en.wikipedia.org/wiki/Common_Lisp&quot;&gt;1980s&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Why generate SVGs? Because it’s relatively straightforward (I’ve done SVG generation &lt;a href=&quot;http://twitter.com/dailyoblo&quot;&gt;before&lt;/a&gt;) and learning how to print things is a standard newbie-programmer task. Also, I adore generative art, and I love puzzles. I was heavily inspired by the work of &lt;a href=&quot;http://inconvergent.net/&quot;&gt;inconvergent&lt;/a&gt; and &lt;a href=&quot;https://n-e-r-v-o-u-s.com/projects/albums/generative-puzzle-system/&quot;&gt;nervous systems&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This post isn’t about jigsaw puzzles, though, it’s about my first experiences learning Lisp. Here are some jumbled thoughts!&lt;/p&gt;

&lt;p&gt;I’m following &lt;a href=&quot;http://landoflisp.com/&quot;&gt;&lt;em&gt;The Land of Lisp&lt;/em&gt;&lt;/a&gt;. The book is…hrm. Interesting. I &lt;a href=&quot;https://twitter.com/brindelle/status/944037633117900805&quot;&gt;grumbled about it&lt;/a&gt; on Twitter. First of all, the illustrations are adorable.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://www.safaribooksonline.com/library/view/land-of-lisp/9781593272814/httpatomoreillycomsourcenostarchimages780406.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The pedagogical approach of the book also works really well for me - I enjoyed following along with the beginning exercises. Ideologically, though, I’m a bit annoyed. The book spends a lot of time assuring you that Lisp is “beautiful” and “pure” and “elegant”, and also warning you that Lispers can be judgmental assholes, though it doesn’t use those words. Arrrgh.&lt;/p&gt;

&lt;p&gt;The clisp REPL also includes this somewhat baffling ASCII art, which I kind of love:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/lisping/clisp.jpeg&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Anyway, here are some unordered musings:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;In Lisp, you can get the head of a list with a function named CAR (Contents of the Address Register) and the remainder with CDR (Contents of the Decrement Register). Look, they even have a &lt;a href=&quot;https://en.wikipedia.org/wiki/CAR_and_CDR&quot;&gt;Wiki page&lt;/a&gt;. I’m given to understand that many modern Lisps just call these FIRST and REST or some such English-language thing. I think it’s pretty interesting that 1. they compose well and 2. you can’t just get the nth element of a list.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Relatedly, implementing my own &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;randselect&lt;/code&gt; function looked like this:&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-common-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;randselect&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;somelist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;length&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;somelist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;car&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;nthcdr&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;random&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;somelist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;How on earth does performance work in this language? I’m super curious.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I want to make tongue twisters out of cadddraddadar. No, seriously, it’s adorable that you would name a function after the &lt;em&gt;actual registers involved&lt;/em&gt; but I think this is one of those, uh, historical quirks that shows Lisp’s age. Someone sent me a link to this &lt;a href=&quot;https://news.ycombinator.com/item?id=16009280&quot;&gt;HackerNews discussion&lt;/a&gt; which actually seemed quite reasonable all-around.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I’m dying to know how you write maintainable, shareable code in Lisp that lots of people can work on who aren’t all inside each other’s heads. The way that &lt;em&gt;Land of Lisp&lt;/em&gt; is encouraging me to write code - where you basically have lots and lots of unnamed heterogeneous lists where each element does Something Special and functions expect things in some weird undocumented order - is EXACTLY how I like to write python scripts for my side projects. As came up in my RC pairing interview, however, that approach is extraordinarily bug prone. It also takes forever for someone new to learn it. So … I’m curious. But so far at least it matches how I think :) (Fast to write, infuriating to debug…)&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;These function names are a little weird. MAPCAR, really ??? I get it but also I’m coming in with pleeeenty of my own biases and this feels weird.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Evidently using APPLY is dangerous ?? But it’s so useful! Gah! I can’t wait til we get to tail recursion.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Someone suggested I switch to using &lt;a href=&quot;http://www.sbcl.org/&quot;&gt;sbcl&lt;/a&gt; for my REPL; apparently it is More Efficient. However it lacks clisp’s awesome autocomplete in the REPL, at least on Mac.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;The usage of &lt;a href=&quot;https://www.gnu.org/software/emacs/manual/html_node/elisp/Association-Lists.html&quot;&gt;association lists&lt;/a&gt; (alists?) confuses me. Is there anything that distinguishes an alist? How do you tell if something can be used as an alist?&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Figuring out how to print stuff is weird. String interpolation is confusing. I thought that FORMAT was the answer but then it always prints to screen instead of returning a value? Unless you pass “nil” as the first argument, apparently. Should I be using CONCATENATE? I ended up finding a useful resource &lt;a href=&quot;http://cl-cookbook.sourceforge.net/strings.html#concat&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&quot; If the string will be constructed out of (the printed representations of) arbitrary objects, (symbols, numbers, characters, strings, ...), you can use FORMAT with an output stream argument of NIL. This directs FORMAT to return the indicated output as a string. &quot;
&lt;/blockquote&gt;

&lt;ul&gt;
  &lt;li&gt;Random apparently doesn’t have a time-based seed ?? At least not by default. You have to seed it yourself, laŭ &lt;a href=&quot;https://stackoverflow.com/questions/4034042/random-in-common-lisp-not-so-random&quot;&gt;StackOverflow&lt;/a&gt;:&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-cl highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;setf&lt;/span&gt; &lt;span class=&quot;vg&quot;&gt;*random-state*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;make-random-state&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;The syntax for LET is weird. I was confused about how to set up a variable that depends on another variable, e.g. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;a = 5, b = a + 1&lt;/code&gt;. It turns out that you need to use LET* for this? I got this from &lt;a href=&quot;http://www.gigamonkeys.com/book/variables.html&quot;&gt;&lt;em&gt;Practical Common Lisp&lt;/em&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-cl highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
       &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;At the end of my very early flailings about, I’ve made it through six out of twenty chapters of &lt;em&gt;Land of Lisp&lt;/em&gt; and written my very first basic script. It’s adapted from my &lt;a href=&quot;https://github.com/bonniee/bot-experiments/tree/master/dailyoblo&quot;&gt;SET bot Python code&lt;/a&gt;, and right now it generates an SVG with a randomly-colored circle.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/lisping/foo.svg&quot; /&gt;&lt;/p&gt;

&lt;div class=&quot;language-cl highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;;;;; sbcl --script setsvg.lisp &amp;gt; foo.svg&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;;;; Creates an SVG with a randomly-colored circle.&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;; Initialize the global random state.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;; https://stackoverflow.com/questions/4034042/random-in-common-lisp-not-so-random&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;setf&lt;/span&gt; &lt;span class=&quot;vg&quot;&gt;*random-state*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;make-random-state&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defparameter&lt;/span&gt; &lt;span class=&quot;vg&quot;&gt;*begin-svg*&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&amp;lt;svg xmlns=\&quot;http://www.w3.org/2000/svg\&quot;
  xmlns:xlink=\&quot;http://www.w3.org/1999/xlink\&quot;
  width=\&quot;500\&quot; height=\&quot;500\&quot;&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defparameter&lt;/span&gt; &lt;span class=&quot;vg&quot;&gt;*end-svg*&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&amp;lt;/svg&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;ellipse&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;concatenate&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;&apos;string&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&amp;lt;ellipse
      cx=\&quot;250.0\&quot;
      cy=\&quot;250.0\&quot;
      rx=\&quot;100.0\&quot;
      ry=\&quot;100.0\&quot;
      stroke=\&quot;#fc8d62\&quot;
      stroke-width=\&quot;5\&quot;
      fill=\&quot;#&quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;color&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;\&quot;
    /&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defparameter&lt;/span&gt; &lt;span class=&quot;vg&quot;&gt;*colors*&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;66c2a5&quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;fc8d62&quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;8da0cb&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;randselect&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;somelist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;length&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;somelist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;car&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;nthcdr&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;random&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;somelist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;randcolor&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;randselect&lt;/span&gt; &lt;span class=&quot;vg&quot;&gt;*colors*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;svg&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;~d&quot;&lt;/span&gt; &lt;span class=&quot;vg&quot;&gt;*begin-svg*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;~d&quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;~d&quot;&lt;/span&gt; &lt;span class=&quot;vg&quot;&gt;*end-svg*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;randcircle&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;svg&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ellipse&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;randcolor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;randcircle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

  &lt;p&gt;&lt;a href=&quot;http://blog.bonnieeisenman.com/blog/lisp-learning/&quot;&gt;Lisp Learning, Part 1: Generating a Circular SVG&lt;/a&gt; was originally published by Bonnie Eisenman at &lt;a href=&quot;http://blog.bonnieeisenman.com&quot;&gt;Bonnie Eisenman&lt;/a&gt; on December 29, 2017.&lt;/p&gt;</content>
</entry>


<entry>
  <title type="html"><![CDATA[For want of a sandwich]]></title>
  <link rel="alternate" type="text/html" href="http://blog.bonnieeisenman.com/blog/for-want-of-a-sandwich/" />
  <id>http://blog.bonnieeisenman.com/blog/for-want-of-a-sandwich</id>
  <published>2017-12-21T00:00:00-05:00</published>
  <updated>2017-12-21T00:00:00-05:00</updated>
  <author>
    <name>Bonnie Eisenman</name>
    <uri>http://blog.bonnieeisenman.com</uri>
    <email>bonnie.eisenman@gmail.com</email>
  </author>
  <content type="html">&lt;p&gt;Today I made myself a sandwich. Two slices of toasted rye, fig jam, turkey, swiss, pickles.&lt;/p&gt;

&lt;p&gt;The ingredients arrived from FreshDirect this morning. I had to open the door. Unbox the groceries. Put them in the fridge. Open the bag of sliced bread. Plug in the toaster. Put two slices of bread into the toaster. Open a cabinet door, take down a plate. Open a drawer, pick up a knife…&lt;/p&gt;

&lt;p&gt;And so on.&lt;/p&gt;

&lt;p&gt;This time last year, each of those steps was notably taxing. I generally couldn’t make myself a sandwich in one go; I’d have to sit down, catch my breath, drink some water. It might take me an hour to make lunch. Doable, but difficult. Maddeningly difficult.&lt;/p&gt;

&lt;p&gt;(And that, lest we forget, was a step forward from the year prior, where even “unbox groceries” would have expended all of my spoons for the day, and I would instead retreat to my bed to drink Soylent.)&lt;/p&gt;

&lt;p&gt;Recovery feels wonderful. I’m keenly aware of all of these small pleasures.&lt;/p&gt;

&lt;p&gt;I made myself a goddamned sandwich.&lt;/p&gt;

  &lt;p&gt;&lt;a href=&quot;http://blog.bonnieeisenman.com/blog/for-want-of-a-sandwich/&quot;&gt;For want of a sandwich&lt;/a&gt; was originally published by Bonnie Eisenman at &lt;a href=&quot;http://blog.bonnieeisenman.com&quot;&gt;Bonnie Eisenman&lt;/a&gt; on December 21, 2017.&lt;/p&gt;</content>
</entry>


<entry>
  <title type="html"><![CDATA[Underscores in Scala]]></title>
  <link rel="alternate" type="text/html" href="http://blog.bonnieeisenman.com/blog/underscores-in-scala/" />
  <id>http://blog.bonnieeisenman.com/blog/underscores-in-scala</id>
  <published>2017-12-18T00:00:00-05:00</published>
  <updated>2017-12-18T00:00:00-05:00</updated>
  <author>
    <name>Bonnie Eisenman</name>
    <uri>http://blog.bonnieeisenman.com</uri>
    <email>bonnie.eisenman@gmail.com</email>
  </author>
  <content type="html">&lt;p&gt;This post brought to you by my shiny new drawing tablet! I decided to gift myself a tablet for my birthday. I like making doodles of things - I remember information better that way - and I’m also always envying &lt;a href=&quot;https://drawings.jvns.ca/&quot;&gt;@b0rk’s drawings&lt;/a&gt;. So, this is me, giving it a test run.&lt;/p&gt;

&lt;p&gt;When I first started learning Scala, the underscores everywhere were one of the things I found most confusing. Why were there underscores everywhere??? I was coming from a background of JavaScript/Python/Java/Golang/Ruby, so I had enough polyglot experience that &lt;em&gt;most&lt;/em&gt; of Scala syntax made sense at a glance. But those underscores. Man.&lt;/p&gt;

&lt;p&gt;Of course when I tried Googling my problem I ended up on the relevant-sounding page &lt;a href=&quot;http://docs.scala-lang.org/tutorials/FAQ/finding-symbols.html&quot;&gt;“How do I find out what some symbol means or does?”&lt;/a&gt;. Helpfully, the page informs you that the underscore has “many different meanings”, and neglects to expand further on the topic. *headexplode*&lt;/p&gt;

&lt;p&gt;Other, more useful explanations include &lt;a href=&quot;http://ananthakumaran.in/2010/03/29/scala-underscore-magic.html&quot;&gt;Scala _ [underscore] magic&lt;/a&gt; and, for reference (&lt;em&gt;not&lt;/em&gt; explanation), this StackOverflow post: &lt;a href=&quot;https://stackoverflow.com/questions/8000903/what-are-all-the-uses-of-an-underscore-in-scala&quot;&gt;What are all the uses of underscore in Scala?&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;OK, enough linkspam. Here’s my doodle:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/zines/underscore.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;TLDR; I think of the underscore as the “meh” operator. :)&lt;/p&gt;

&lt;p&gt;…also, I really need to get better at drawing.&lt;/p&gt;

  &lt;p&gt;&lt;a href=&quot;http://blog.bonnieeisenman.com/blog/underscores-in-scala/&quot;&gt;Underscores in Scala&lt;/a&gt; was originally published by Bonnie Eisenman at &lt;a href=&quot;http://blog.bonnieeisenman.com&quot;&gt;Bonnie Eisenman&lt;/a&gt; on December 18, 2017.&lt;/p&gt;</content>
</entry>


<entry>
  <title type="html"><![CDATA[Reading Papers as a Working Programmer]]></title>
  <link rel="alternate" type="text/html" href="http://blog.bonnieeisenman.com/blog/reading-papers-as-a-working-programmer/" />
  <id>http://blog.bonnieeisenman.com/blog/reading-papers-as-a-working-programmer</id>
  <published>2017-12-13T00:00:00-05:00</published>
  <updated>2017-12-13T00:00:00-05:00</updated>
  <author>
    <name>Bonnie Eisenman</name>
    <uri>http://blog.bonnieeisenman.com</uri>
    <email>bonnie.eisenman@gmail.com</email>
  </author>
  <content type="html">&lt;p&gt;When talking about the computer science research papers with other software engineers, I get a lot of comments to the effect of, “Neat! I wish I read papers, &lt;em&gt;but&lt;/em&gt; … (I don’t).” Many of these comments were followed by “…and I feel some imposter syndrome over the fact that I don’t.”&lt;/p&gt;

&lt;h2 id=&quot;do-software-engineers-andor-professional-programmers-read-cs-research-papers&quot;&gt;Do software engineers and/or professional programmers read CS research papers?&lt;/h2&gt;

&lt;p&gt;After having a couple of these conversations, I was curious, so I &lt;a href=&quot;https://twitter.com/brindelle/status/734757812530741248?ref_src=twsrc%5Etfw&quot;&gt;polled my Twitter followers&lt;/a&gt;. The results were pretty even, at 45% yes / 55% no.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/research-paper-poll.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I have a lot of followers who are JavaScript-only developers, or frontend- and mobile-centric, thanks to my work with React Native. I also have a good number of Twitter employees following me. (Hi, coworkers!) Obviously, this is not a representative sample, but I still found it interesting.&lt;/p&gt;

&lt;p&gt;tldr; I think that &lt;strong&gt;most programmers don’t read CS research papers.&lt;/strong&gt; This makes sense! Research papers are their own genre, and reading papers is a skill that requires time, effort, and practice.&lt;/p&gt;

&lt;p&gt;One of my professors assigned us this &lt;a href=&quot;http://www.albany.edu/spatial/WebsiteFiles/ResearchAdvices/how-to-read-a-paper.pdf&quot;&gt;How to Read a Paper&lt;/a&gt; paper. It’s really useful and I’d recommend it to anyone who struggles with the research paper format.&lt;/p&gt;

&lt;p&gt;Anyway, this got me thinking about whether or not working programmers read computer science research, and why.&lt;/p&gt;

&lt;h2 id=&quot;why-is-it-useful-to-read-papers&quot;&gt;Why is it useful to read papers?&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Get new ideas.&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Build up confidence critiquing ideas.&lt;/strong&gt; Sometimes you read a paper and think, “why did they do that?” Discussing papers can be great practice for learning how to critique others’ proposals or designs.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Get a sense for the “state of the art”.&lt;/strong&gt; Some people will tell you that papers are usually several years behind “today’s” tech. But that doesn’t mean they aren’t useful - reading papers can give you a sense of what currently-understood thinking in a field is.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Build up knowledge in a new area.&lt;/strong&gt; If you don’t know much about a given topic area, beginning with a literature survey can be fun.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Learn the history of a field.&lt;/strong&gt; There are some papers that are historically significant, and reading them will give you more context.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;why-isnt-it-useful-to-read-papers&quot;&gt;Why &lt;em&gt;isn’t&lt;/em&gt; it useful to read papers?&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Industry moves too fast / research papers are usually outdated.&lt;/strong&gt; This is probably true a lot of the time! But then it begs the question: where are you positioned, industry-wide? If you’re working on cutting-edge stuff at your day job, cool. But you’re just one person, working at one organization. How are you keeping track of other companies’ cutting-edge work? (Conferences? Meetups? Twitter? Blog posts?)&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Reading papers is hard / not worth my time.&lt;/strong&gt; Legit! I can’t really argue with this one.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;There isn’t useful research in my field.&lt;/strong&gt; Also possible, if your particular kind of work isn’t addressed by academia.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;should-you-read-papers&quot;&gt;Should you read papers?&lt;/h2&gt;

&lt;p&gt;For the general “you”, as usual, I think the answer is “you’re not obligated to do anything”.&lt;/p&gt;

&lt;p&gt;In general I like to always be learning things, and studying what other people are doing. Papers are one source of this kind of information, but far from the only source, or even the best one. I try to fill my feed with a diversity of sources for this kind of thing: Twitter, RSS feeds from individual blogs, recommendations from my friends, conversations at meetups, conference talks, etc, etc…&lt;/p&gt;

&lt;p&gt;(I’m not recommending that anyone spend all their free time on this, either. IMO trying to learn more things is a legitimate use of work-time!)&lt;/p&gt;

&lt;p&gt;By that metric, &lt;em&gt;I&lt;/em&gt; should be reading papers because it’s one more source of information, and I personally find it to be interesting/valuable from time to time. Should &lt;em&gt;you&lt;/em&gt; read papers? I dunno! I think it’s something that depends a lot on your individual goals, preferences, and interests.&lt;/p&gt;

&lt;p&gt;Some links if you’re interested in reading more papers:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.acolyer.org/&quot;&gt;the morning paper&lt;/a&gt; - a blog that describes + links to one CS paper per weekday&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://paperswelove.org/&quot;&gt;Papers We Love&lt;/a&gt; - a repo of computer science papers, and a community of people who love reading them&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And some papers I’ve read lately:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://research.fb.com/wp-content/uploads/2016/11/scuba-diving-into-data-at-facebook.pdf&quot;&gt;Scuba: Diving into Data at Facebook&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://n-e-r-v-o-u-s.com/education/papers/nervousSystem_2012-jigsaw-puzzle-paper.pdf&quot;&gt;Multiphase Numerical Modeling of Dendritic Solidification for Jigsaw Puzzle
Generation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.microsoft.com/en-us/research/wp-content/uploads/2016/12/The-Byzantine-Generals-Problem.pdf&quot;&gt;The Byzantine Generals Problem&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;🎉&lt;/p&gt;

  &lt;p&gt;&lt;a href=&quot;http://blog.bonnieeisenman.com/blog/reading-papers-as-a-working-programmer/&quot;&gt;Reading Papers as a Working Programmer&lt;/a&gt; was originally published by Bonnie Eisenman at &lt;a href=&quot;http://blog.bonnieeisenman.com&quot;&gt;Bonnie Eisenman&lt;/a&gt; on December 13, 2017.&lt;/p&gt;</content>
</entry>


<entry>
  <title type="html"><![CDATA[Quitting Facebook / this is just to say]]></title>
  <link rel="alternate" type="text/html" href="http://blog.bonnieeisenman.com/blog/quitting-facebook/" />
  <id>http://blog.bonnieeisenman.com/blog/quitting-facebook</id>
  <published>2017-12-13T00:00:00-05:00</published>
  <updated>2017-12-13T00:00:00-05:00</updated>
  <author>
    <name>Bonnie Eisenman</name>
    <uri>http://blog.bonnieeisenman.com</uri>
    <email>bonnie.eisenman@gmail.com</email>
  </author>
  <content type="html">&lt;blockquote&gt;&lt;pre&gt;
this is just to say

I have deleted
my account
from facebook

which you probably
noticed
and worried
about

please,
celebrate with me -
I feel so free
and
so calm.

&lt;/pre&gt;
&lt;/blockquote&gt;

&lt;p&gt;…I’ve received several concerned messages from folks who noticed that I’m not on Facebook anymore. I deleted my account! It felt really good. If you need to get in touch, you can probably find a way. (Hint: my email address is listed on this site.)&lt;/p&gt;

  &lt;p&gt;&lt;a href=&quot;http://blog.bonnieeisenman.com/blog/quitting-facebook/&quot;&gt;Quitting Facebook / this is just to say&lt;/a&gt; was originally published by Bonnie Eisenman at &lt;a href=&quot;http://blog.bonnieeisenman.com&quot;&gt;Bonnie Eisenman&lt;/a&gt; on December 13, 2017.&lt;/p&gt;</content>
</entry>


<entry>
  <title type="html"><![CDATA[What I know about debugging circuits]]></title>
  <link rel="alternate" type="text/html" href="http://blog.bonnieeisenman.com/blog/what-i-know-about-debugging-circuits/" />
  <id>http://blog.bonnieeisenman.com/blog/what-i-know-about-debugging-circuits</id>
  <published>2017-12-09T00:00:00-05:00</published>
  <updated>2017-12-09T00:00:00-05:00</updated>
  <author>
    <name>Bonnie Eisenman</name>
    <uri>http://blog.bonnieeisenman.com</uri>
    <email>bonnie.eisenman@gmail.com</email>
  </author>
  <content type="html">&lt;p&gt;I’m a trained software engineer, but I am decidedly an amateur when it comes to working with physical hardware and building circuits. I’m juuuust teetering on the edge of self-sufficiency when it comes to physical computing projects and circuit-building: I can comfortably program an Arduino; I can stitch together existing tutorials and remix them into my own projects; I can read and replicate simple circuit diagrams. I still tend to rely on microcontrollers for anything complicated because, let’s be honest, version control is the &lt;em&gt;best&lt;/em&gt; and I’m much more comfortable writing code.&lt;/p&gt;

&lt;p&gt;As I’m working on my current project, I’m noticing that I’m getting better at debugging circuits. Here are some things that help me!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Double-check things with a multimeter.&lt;/strong&gt; You can use a multimeter to double-check that things are connected how you think they should be. You can also use a multimeter to read values, like current/resistance, to confirm that e.g. your sensors are working. This feels pretty cool!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It’s worth the time to solder carefully.&lt;/strong&gt; Soldering is one of those really basic skills that pay dividends. For a while I felt really nervous about soldering (what if I did it wrong??). Being able to double-check my connections with a multimeter made me a lot less afraid of that.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Just because it worked yesterday doesn’t mean it will work today.&lt;/strong&gt; You don’t have version control, so it’s quite possible that you changed something when you put your project away and then took it back out again. Did you plug something in backwards? Did something break when you moved it?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Document, document, document.&lt;/strong&gt; See again: not having version control. This is one reason why I write tutorials: they are gifts to my future self.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How’s your power?&lt;/strong&gt; Over- or under-powering your circuit is a pretty common reason behind “hey this works but only sorta???”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Are you grounded?&lt;/strong&gt; This is really common when powering projects from e.g. your laptop. Make sure your laptop is plugged in with a three-prong plug. I do a lot of capacitative sensing projects and they’re affected by things as simple as someone touching the metal case of my computer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Read the datasheet.&lt;/strong&gt; Ughhhh I wish I was better at reading datasheets! They contain all sorts of useful information, like “how much voltage does this component need to function properly”. See above.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test every step.&lt;/strong&gt; Never assume that you did it right. Whenever you have a chance to verify that a component is working, or that you built part of a circuit correctly, do it. And take notes.&lt;/p&gt;

&lt;p&gt;Debugging circuits is a lot like debugging software in terms of the questions you should ask. (&lt;em&gt;What am I seeing? How do I know that? What can I measure? What do I expect to see? How do I confirm that?&lt;/em&gt;) But the tools are really different and also it takes way more time to fix your mistakes.&lt;/p&gt;

  &lt;p&gt;&lt;a href=&quot;http://blog.bonnieeisenman.com/blog/what-i-know-about-debugging-circuits/&quot;&gt;What I know about debugging circuits&lt;/a&gt; was originally published by Bonnie Eisenman at &lt;a href=&quot;http://blog.bonnieeisenman.com&quot;&gt;Bonnie Eisenman&lt;/a&gt; on December 09, 2017.&lt;/p&gt;</content>
</entry>

</feed>
