<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
 
 <title>Continuously Deployed</title>
 <link href="https://www.mayerdan.com/atom.xml" rel="self"/>
 <link href="https://www.mayerdan.com"/>
 <updated>2026-05-20T01:30:04+00:00</updated>
 <id>https://www.mayerdan.com</id>
 <author>
   <name>Dan Mayer</name>
   <email>dan@mayerdan.com</email>
 </author>

 
 <entry>
   <title>Ruby Caches - Improving Caching Via Tooling</title>
   <link href="https://www.mayerdan.com/ruby/2025/02/10/caches-driving-improvements-via-tooling"/>
   <updated>2025-02-10T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/ruby/2025/02/10/caches-driving-improvements-via-tooling</id>
   <content type="html">
&lt;p&gt;A series of posts will explore and detail some of the current Rails caching code. This post takes a deeper look at the network requests involved in caching.&lt;/p&gt;

&lt;p&gt;These posts attempt to explain in more detail, but please do read the official &lt;a href=&quot;https://guides.rubyonrails.org/caching_with_rails.html#cache-stores&quot;&gt;Rails Caching Guide&lt;/a&gt; docs, which are also very good.&lt;/p&gt;

&lt;p&gt;Rails Cache Posts:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;/ruby/2024/10/17/caches-rails-initialization&quot;&gt;Rails Cache Initialization&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/ruby/2024/10/20/caches-rails-comparisons&quot;&gt;Rails Cache Comparisons&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/ruby/2024/10/24/caches-rails-workflows&quot;&gt;Rails Cache Workflows&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/ruby/2024/11/07/caches-network-requests&quot;&gt;Rails Cache Network Requests&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/ruby/2024/12/17/caches-local-cache-strategy&quot;&gt;Rails Cache Local Cache Strategy&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Improving the Cache Layer Via Tooling&lt;/li&gt;
&lt;/ol&gt;

&lt;h1 id=&quot;ruby-cache-improvements&quot;&gt;Ruby Cache Improvements&lt;/h1&gt;

&lt;p&gt;If you want to drive performance improvements for a library, you will need to understand some of the use cases and the structure of the code. We have covered how to use the Rails cache, and we took a deep look at the &lt;a href=&quot;/ruby/2024/10/24/caches-rails-workflows&quot;&gt;caching code workflow&lt;/a&gt;, including where time is most often spent, as well as digging into &lt;a href=&quot;/ruby/2024/11/07/caches-network-requests&quot;&gt;cache network requests&lt;/a&gt; to see where network IO time is spent. Now, if you want to start to improve caching, you can look at adding features to help avoid issues like cache stampede, or you can focus on reducing the latency for using the cache. This post will look at lowering latency when using the cache.&lt;/p&gt;

&lt;h1 id=&quot;understanding-latency-with-benchmarks-and-profiles&quot;&gt;Understanding latency with benchmarks and profiles&lt;/h1&gt;

&lt;p&gt;If we want to make something faster we want data to help drive the improvements, often the best way to speed something up is using benchmarks and profilers. When I reach for one of these tools, I usually want both. I can use the benchmarks to see where I am in a range of options and conditions, then use the profiler to dig into specific use cases that don’t match my needs or expectations to target specific improvements. After fixing the profile, the increased performance is usually evident in the relevant benchmarks. Tooling like this can be a great feedback loop for driving improvements. It can also help avoid regressions. This is why, on many of the projects I work on, my team and I set up benchmarks and profiling as part of the CI pipeline. We have a complete history of performance improvements, and we can fail CI for significant regressions or at least investigate the regression, which is sometimes required for a bug fix or new feature.&lt;/p&gt;

&lt;p&gt;A &lt;a href=&quot;https://github.com/petergoldstein/dalli/pull/1028&quot;&gt;PR adding the benchmark and profile scripts to Dalli&lt;/a&gt;.&lt;/p&gt;

&lt;h1 id=&quot;benchmarking-dalli-with-github-actions&quot;&gt;Benchmarking Dalli with GitHub Actions&lt;/h1&gt;

&lt;p&gt;Let’s examine how we can set up a suite of benchmarks for the Dalli Rubygem, which powers the Memcached flavor of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rails.cache&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;create a new GitHub action with the content below &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.github/workflows/benchmarks.yml&lt;/code&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;name: Benchmarks

on: [push, pull_request]

jobs:
 build:
 runs-on: ubuntu-latest

 steps:
 - uses: actions/checkout@v4
 - name: Install Memcached 1.6.23
 working-directory: scripts
 env:
 MEMCACHED_VERSION: 1.6.23
 run: |
 chmod +x ./install_memcached.sh
 ./install_memcached.sh
 memcached -d
 memcached -d -p 11222
 - name: Set up Ruby
 uses: ruby/setup-ruby@v1
 with:
 ruby-version: 3.2
 bundler-cache: true # &apos;bundle install&apos; and cache
 - name: Run Benchmarks
 run: RUBY_YJIT_ENABLE=1 BENCH_TARGET=all bundle exec bin/benchmark
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This adds a new action that will install everything needed, run two instances of Memcached, and then run our benchmark script.&lt;/p&gt;

&lt;p&gt;add the benchmark script &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bin/benchmark&lt;/code&gt; this file is large, so you can get it from the &lt;a href=&quot;https://github.com/Shopify/dalli/blob/main/bin/benchmark&quot;&gt;Dalli repo&lt;/a&gt;, some brief snippets below&lt;/p&gt;

&lt;div class=&quot;language-ruby 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;#!/usr/bin/env ruby&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# frozen_string_literal: true&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# This helps benchmark current performance of Dalli&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# run with:&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# bundle exec bin/benchmark&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# RUBY_YJIT_ENABLE=1 BENCH_TARGET=get bundle exec bin/benchmark&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;bundler/inline&apos;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;json&apos;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;gemfile&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;https://rubygems.org&apos;&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;benchmark-ips&apos;&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;logger&apos;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;require_relative&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;../lib/dalli&apos;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;benchmark/ips&apos;&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bunch&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;more&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;%w[all set]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;include?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bench_target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;Benchmark&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ips&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;warmup: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bench_warmup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;time: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bench_time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;suite: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;suite&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;report&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;client set&apos;&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;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;key&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;payload&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;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;report&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;raw sock set&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;ms sock_key &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bytesize&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; T3600 MS&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\r\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\r\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;flush&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;readline&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# clear the buffer&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;compare!&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The benchmark lets us measure the iterations per second of all the command cache actions &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;get,&lt;/code&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;set,&lt;/code&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;get_multi,&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;read_multi.&lt;/code&gt; For the benchmark, I compare against extremely simplified memcached access via a raw Ruby socket. This makes it easier to see what we can still improve, ensuring the abstractions in the Dalli gem are very performant and offer very low overhead compared to the fastest pure Ruby implementation you could expect.&lt;/p&gt;

&lt;p&gt;This benchmark runs on all PRs and outputs the current iterations per second of all the methods we have configured. We can then make PRs and include the before-and-after expectations of the benchmark data.&lt;/p&gt;

&lt;h1 id=&quot;profiling-dalli-with-github-actions&quot;&gt;Profiling Dalli with GitHub Actions&lt;/h1&gt;

&lt;p&gt;We also set up a similar CI action for profiling, so we are always capturing profiles and can look back if a PR has some unexpected change after we role it out. The same profiling tooling, while nice in CI, is also part of the very fast feedback loop along with the benchmark script when doing performance work.&lt;/p&gt;

&lt;p&gt;create a new GitHub action for profiling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.github/workflows/profile.yml&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;and add this content to the new file&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;name: Profiles

on: [push, pull_request]

jobs:
 build:
 runs-on: ubuntu-latest

 steps:
 - uses: actions/checkout@v4
 - name: Install Memcached 1.6.23
 working-directory: scripts
 env:
 MEMCACHED_VERSION: 1.6.23
 run: |
 chmod +x ./install_memcached.sh
 ./install_memcached.sh
 memcached -d
 - name: Set up Ruby
 uses: ruby/setup-ruby@v1
 with:
 ruby-version: 3.4
 bundler-cache: true # &apos;bundle install&apos; and cache
 - name: Run Profiles
 run: RUBY_YJIT_ENABLE=1 BENCH_TARGET=all bundle exec bin/profile
 - name: Upload profile results
 uses: actions/upload-artifact@v4
 with:
 name: profile-results
 path: |
 client_get_profile.json
 socket_get_profile.json
 client_set_profile.json
 socket_set_profile.json
 client_get_multi_profile.json
 socket_get_multi_profile.json
 client_set_multi_profile.json
 socket_set_multi_profile.json
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Similar to the benchmark, this installs the required things, gets the Memcached server up and running, and then kicks off our profile script. The action then uploads all of the profiling results, which can be retrieved after any CI run.&lt;/p&gt;

&lt;p&gt;The profiling script is long, so you can get that from the &lt;a href=&quot;https://github.com/Shopify/dalli/blob/main/bin/profile&quot;&gt;Dalli repo&lt;/a&gt;, but below are some snippets&lt;/p&gt;

&lt;div class=&quot;language-ruby 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;#!/usr/bin/env ruby&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# frozen_string_literal: true&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# This helps profile specific call paths in Dalli&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# finding and fixing performance issues in these profiles should result in improvements in the dalli benchmarks&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# run with:&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# RUBY_YJIT_ENABLE=1 bundle exec bin/profile&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;bundler/inline&apos;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;json&apos;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;gemfile&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;https://rubygems.org&apos;&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;benchmark-ips&apos;&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;vernier&apos;&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;logger&apos;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;require_relative&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;../lib/dalli&apos;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;benchmark/ips&apos;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;vernier&apos;&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lots&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;more&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;setup&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;%w[all set]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;include?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bench_target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;Vernier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;profile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;out: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;client_set_profile.json&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;start_time&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;now&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dalli_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3600&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;raw: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;now&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start_time&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bench_time&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;no&quot;&gt;Vernier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;profile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;out: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;socket_set_profile.json&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;start_time&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;now&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;now&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start_time&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bench_time&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;sock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;ms sock_key &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bytesize&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; T3600 MS&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\r\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;sock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;sock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\r\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;sock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;flush&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;sock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;readline&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# clear the buffer&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Again, like the benchmarking script we can target all the most important caching actions, in many we can also compare the profile of the raw socket to our Dalli implementation to help find differences.&lt;/p&gt;

&lt;h1 id=&quot;developer-workflow-leveraging-the-benchmark-and-profiling-tools&quot;&gt;Developer workflow leveraging the benchmark and profiling tools&lt;/h1&gt;

&lt;p&gt;It depends on your needs, but let’s say we want to improve writing to the cache; perhaps we have noticed that some write-heavy workloads are not performing as well with Dalli as other cache implementations. Beyond having the benchmarks as part of CI, several applications include benchmarking scripts so we can compare benchmarks across various caching backends, which helps us spot unexpected differences.&lt;/p&gt;

&lt;h2 id=&quot;capture-a-benchmark-baseline&quot;&gt;Capture a Benchmark Baseline&lt;/h2&gt;

&lt;p&gt;We can capture a baseline by running the benchmark script and targetting the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;set&lt;/code&gt; method.&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;Warming up --------------------------------------
 client set   304.000 i/100ms
 raw sock set   827.000 i/100ms
Calculating -------------------------------------
 client set      2.998k (± 8.9%) i/s  (333.52 μs/i) -     29.792k in  10.009832s
 raw sock set      8.254k (± 2.8%) i/s  (121.15 μs/i) -     82.700k in  10.027442s
Comparison:
 raw sock set:     8254.3 i/s
 client set:     2998.4 i/s - 2.75x  slower
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;profile-the-target-method&quot;&gt;Profile the Target method&lt;/h2&gt;

&lt;p&gt;Then, we can profile the set method and look for unexpected spikes.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/dalli_old_set.jpg&quot; alt=&quot;flamegraph before fixing&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;on this flame graph an important section with string manipulation jumps out&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/dalli_string_profile.jpg&quot; alt=&quot;flamegraph before fixing&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;Why are we spending more than 6% of our time modifying a string?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The profiles help you track down where time is spent and possibly where time is wasted.&lt;/p&gt;

&lt;h2 id=&quot;refactor-iterate-fix-hot-spots&quot;&gt;Refactor, iterate, fix hot spots&lt;/h2&gt;

&lt;p&gt;Now you have a good loop, make a small change, generate another profile run, and attempt to fix hotspots.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/git_diff.png&quot; alt=&quot;diff fixing set&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;primary win is by writing the value directly to the IO object
    &lt;ul&gt;
      &lt;li&gt;This avoids copying a potentially very large string, which can cause numerous allocations when resizing the buffer&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;some smaller wins avoiding some other string manipulation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After each code change, you can look at the new profile flame graph and see if it fixes the issue you were targeting.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/dalli_new_set.jpg&quot; alt=&quot;flamegraph after fixing&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Then, finally, run your benchmarks again to see if fixing what you saw in the profile data improves your microbenchmark.&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;Warming up --------------------------------------
 client set   724.000 i/100ms
 raw sock set   828.000 i/100ms
Calculating -------------------------------------
 client set      7.434k (± 2.4%) i/s  (134.51 μs/i) -     74.572k in  10.037368s
 raw sock set      8.209k (± 4.5%) i/s  (121.82 μs/i) -     81.972k in  10.011659s
Comparison:
 raw sock set:     8208.7 i/s
 client set:     7434.2 i/s - 1.10x  slower
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;rerun-the-benchmark&quot;&gt;Rerun the benchmark&lt;/h2&gt;

&lt;p&gt;When you have fixed all the issues in the profile, you rerun the benchmark, and hopefully, the improvement makes a noticeable difference. While things are different in real-world applications, often, what shows up in your microbenchmark will translate into real-world improvements.&lt;/p&gt;

&lt;h1 id=&quot;ship-your-performance-improvement&quot;&gt;Ship your performance Improvement&lt;/h1&gt;

&lt;p&gt;You can now ship a PR that includes the before-and-after benchmark data and screenshots showing the improvement in the flame graph.&lt;/p&gt;

&lt;p&gt;This basic workflow was followed for a real &lt;a href=&quot;https://github.com/Shopify/dalli/pull/39&quot;&gt;PR improving the set performance for dalli&lt;/a&gt;, which you can check out for complete details.&lt;/p&gt;

&lt;h1 id=&quot;invest-in-the-toolchain&quot;&gt;Invest in the toolchain&lt;/h1&gt;

&lt;p&gt;Since we invested in making these benchmark and profile scripts both available to run locally for developers and as part of CI, it makes it easier for anyone to make performance improvements. It also simplifies any bugs reported around performance as we should be able to review it within our tool chain or we can add the new use case as a path to optimize in our scripts.&lt;/p&gt;

&lt;p&gt;Over time, this can help bring consistent improvement to a library and help avoid accidental regressions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; At the moment, these improvements are on a research fork of Dalli, but we have talked with the Dalli maintainer and will start porting various improvements into the released gem.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Ruby Caches - Rails Local Cache Strategy</title>
   <link href="https://www.mayerdan.com/ruby/2024/12/17/caches-local-cache-strategy"/>
   <updated>2024-12-17T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/ruby/2024/12/17/caches-local-cache-strategy</id>
   <content type="html">
&lt;p&gt;A series of posts will explore and detail some of the current Rails caching code. This post takes a deeper look at the network requests involved in caching.&lt;/p&gt;

&lt;p&gt;These posts attempt to explain in more detail, but please do read the official &lt;a href=&quot;https://guides.rubyonrails.org/caching_with_rails.html#cache-stores&quot;&gt;Rails Caching Guide&lt;/a&gt; docs, which are also very good.&lt;/p&gt;

&lt;p&gt;Rails Cache Posts:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;/ruby/2024/10/17/caches-rails-initialization&quot;&gt;Rails Cache Initialization&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/ruby/2024/10/20/caches-rails-comparisons&quot;&gt;Rails Cache Comparisons&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/ruby/2024/10/24/caches-rails-workflows&quot;&gt;Rails Cache Workflows&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/ruby/2024/11/07/caches-network-requests&quot;&gt;Rails Cache Network Requests&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Rails Cache Local Cache Strategy&lt;/li&gt;
&lt;/ol&gt;

&lt;h1 id=&quot;rails-cache-local-cache-strategy&quot;&gt;Rails Cache Local Cache Strategy&lt;/h1&gt;

&lt;p&gt;A semi hidden feature of Rails is the ability to use a local cache strategy along with rails cache. You might already be using it in your app without realizing it.&lt;/p&gt;

&lt;p&gt;Let’s take a look at how it works. When you want to use it and when you might want to avoid it.&lt;/p&gt;

&lt;h2 id=&quot;what-is-active-support-local-cache-strategy&quot;&gt;What is Active Support Local Cache Strategy?&lt;/h2&gt;

&lt;p&gt;There are some abstractions to support cache strategies in Rails, but sadly the only one actually in Rails is the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActiveSupport::Cache::LocalCacheStore&lt;/code&gt;. Although, I am aware of code that leverages cache strategies in private code repositories to many various layered cache strategies.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Local Cache Strategy&lt;/p&gt;

  &lt;p&gt;Caches that implement LocalCache will be backed by an in-memory cache for the
duration of a block. Repeated calls to the cache for the same key will hit the
in-memory cache for faster access.
–&lt;a href=&quot;https://github.com/rails/rails/blob/main/activesupport/lib/active_support/cache/strategy/local_cache.rb&quot;&gt;Local Cache Strategy&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Basically, this is fancy memoization that tries to avoid making repeated network calls to a remote cache store. It is a bit fancier because it tries to handle some of the complexities for you. For example, in a Rails web app, it has Rack middleware to clear the memoized cache when the request is finished. Also, it is implemented in a way that is thread safe.&lt;/p&gt;

&lt;p&gt;It isn’t used in all of the Rails cache stores as it doesn’t make sense for the local &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MemoryStore&lt;/code&gt; to use it as they are both in memory stores. It was also removed from &lt;a href=&quot;https://github.com/rails/rails/pull/42626&quot;&gt;Rails FileStore&lt;/a&gt; when it had to use serialization to avoid mutation related bugs, which we will describe more a bit later.&lt;/p&gt;

&lt;h2 id=&quot;how-strategylocalcache-works&quot;&gt;How Strategy::LocalCache works&lt;/h2&gt;

&lt;p&gt;You will see it used in both the Redis and Memcached cache stores. It is included in those classes like so:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;prepend Strategy::LocalCache&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This will mean that every time you write to the cache, it will also write to the memory local cache. And every time you read from the cache, it will first check the local cache.  It will try to serialize the value to the local cache and deserialize it when you read from the local cache, this avoids an issue where you can modify a value and possibly not get what you expected from the cache, due to runtime modifications of the original object stored in the cache.&lt;/p&gt;

&lt;p&gt;It clears the local cache when the request is finished, this is done by the Rack middleware. which is included like so:&lt;/p&gt;

&lt;div class=&quot;language-ruby 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;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Strategy&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;autoload&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:LocalCache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;active_support/cache/strategy/local_cache&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;While the idea is simple, the local cache has evolved over time to handle some of the complexities around mutation, memory usage, and serialization.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TODO: update my mermaid diagram to include flow with local cache strategy&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;local-cache-history&quot;&gt;Local Cache History&lt;/h2&gt;

&lt;p&gt;Originally, it was just a hash of the in memory objects being used in the cache. This was simple, but it had issues with mutations. First there was an &lt;a href=&quot;https://github.com/rails/rails/pull/36656&quot;&gt;issue with mutation on the value read from the cache&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;language-ruby 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;n&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;key&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# =&amp;gt; &quot;foobar&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;bar&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;key&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# =&amp;gt; &quot;foobarbar&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After that was fixed, there was a similar &lt;a href=&quot;https://github.com/rails/rails/pull/37587&quot;&gt;issue with modifiying the value written to the cache&lt;/a&gt; as seen in this example.&lt;/p&gt;

&lt;div class=&quot;language-ruby 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;n&quot;&gt;my_string&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;foo&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;key&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;my_string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;my_string&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;bar&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;key&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# =&amp;gt; &quot;foobar&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The various fixes were introduced using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dup_value!&lt;/code&gt; which was a deep dup of the value. These fixes did solved the mutation issue, but it had a larger performance hit, as it was effectively calling Marshal on every cache write and read &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@value = Marshal.load(Marshal.dump(@value))&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The performance problems ended up being reported as a Rails issue, &lt;a href=&quot;https://github.com/rails/rails/issues/42611&quot;&gt;large performance regression&lt;/a&gt; of the local cache strategy. It had a decent reproducible benchmark, and investigation resulted in a number of changes for the local store and a good explanation of the challenges. the&lt;/p&gt;

&lt;p&gt;A few fixes such as &lt;a href=&quot;https://github.com/rails/rails/pull/42014&quot;&gt;avoiding as much of the performance hit and avoiding use of Marshal&lt;/a&gt; as possible were put into Rails. The cache moved away from the duplicate serialization and added some hot path optimizations for dupable entries, but still requires some serialization overhead to protect against mutation. The improvements helped significantly especially for simpler cache objects, but for large serialized object the overhead was still significantly worse, like 300X slower&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;the local cache will only save the network round trip (and internal Redis/memcached latency).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After the bug fixes and performance fixes were in, the &lt;a href=&quot;https://github.com/rails/rails/pull/42626&quot;&gt;local cache no longer made sense for the file store&lt;/a&gt; as it wasn’t just a memoized value in a hash, but required serialization to avoid mutation bugs.&lt;/p&gt;

&lt;p&gt;In the discussion it was pointed out that there are some reasons to side step the local cache strategy and just use request local memoization. Especially for larger cache objects where the serialization overhead is more significant.&lt;/p&gt;

&lt;h2 id=&quot;when-to-not-use-local-cache&quot;&gt;When to not use local cache?&lt;/h2&gt;

&lt;p&gt;If you are going to read the value multiple times in the same request, you can wrap the value from the cache and memoize it in a local variable. This can be done around either the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fetch&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;read&lt;/code&gt; method. This is often valuable if many different parts of the codebase are trying to get from the cache through different call paths.&lt;/p&gt;

&lt;p&gt;Depending on your needs you can have a simple memoized method that is an accessor or a memoized hash accepting cache keys to handle related caches. Something like below if your class has the correct lifecycle. If you need kepe the cache tied to the request lifecycle, you can use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActiveSupport::CurrentAttributes&lt;/code&gt; to keep the memoized value in the request local scope.&lt;/p&gt;

&lt;div class=&quot;language-ruby 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;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SomeCacheableClass&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;memoized_cache_fetch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;vi&quot;&gt;@memoized_hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&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;yield&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you are using memoization you are back to extremely fast performance because it skips the serialization penalty and hash lookups are very fast, but you need to be careful to not modify the value you are memoizing, or at least be aware that it can be mutated. As you can see, returning a memoized value will be orders of magnitude faster than anything that requires serialization or network calls.&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;Comparison:
cache memcached 1x memoized_cache_fetch: 11633145.8 i/s
cache memcached 1x fetch:     4209.4 i/s - 2763.63x  slower
cache memcached 1x fetch bypass local store:     2023.2 i/s - 5749.82x  slower
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So if you see significant slowdown when repeatly getting the same data out of the cache don’t be afraid to memoize it, if you look at profiling data or a flame graph you will see duplicate deserialization is slowing you down and can be avoided.&lt;/p&gt;

&lt;h2 id=&quot;other-rails-cache-strategies&quot;&gt;Other Rails Cache Strategies?&lt;/h2&gt;

&lt;p&gt;Since the current local cache strategy is the only one in Rails, maybe we could show how to build a not mutation safe but faster hash lookup strategy. Basically re-introducing the original hash strategy. Sometimes the trade offs are worth it, when it is clear what the pain and cost would be. We can look at how to build that in a future post.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Ruby Caches - Rails Cache Network Requests</title>
   <link href="https://www.mayerdan.com/ruby/2024/11/07/caches-network-requests"/>
   <updated>2024-11-07T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/ruby/2024/11/07/caches-network-requests</id>
   <content type="html">
&lt;p&gt;A series of posts will explore and detail some of the current Rails caching code. This post takes a deeper look at the network requests involved in caching.&lt;/p&gt;

&lt;p&gt;These posts attempt to explain in more detail, but please do read the official &lt;a href=&quot;https://guides.rubyonrails.org/caching_with_rails.html#cache-stores&quot;&gt;Rails Caching Guide&lt;/a&gt; docs, which are also very good.&lt;/p&gt;

&lt;p&gt;Rails Cache Posts:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;/ruby/2024/10/17/caches-rails-initialization&quot;&gt;Rails Cache Initialization&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/ruby/2024/10/20/caches-rails-comparisons&quot;&gt;Rails Cache Comparisons&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/ruby/2024/10/24/caches-rails-workflows&quot;&gt;Rails Cache Workflows&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Rails Cache Network Requests&lt;/li&gt;
&lt;/ol&gt;

&lt;h1 id=&quot;rails-cache-network-requests&quot;&gt;Rails Cache Network Requests&lt;/h1&gt;

&lt;p&gt;Most of the larger Rails applications end up using a cache server that is accessed over the network. This is because many of the simpler caches, like filestore and memory, do not work for a horizontally scaling Rails deployment. In contrast, you can set up a localhost Redis or Memcached server, which is generally just for development or small-scale applications. Let’s look at what happens with a typical Rails cache configured with a memcached server.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/img/cache_read.webp&quot;&gt;&lt;img src=&quot;/assets/img/cache_read.webp&quot; alt=&quot;Rails.cache.read&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s not look at serialization or compression but at what is going on to get data over the network.&lt;/p&gt;

&lt;h2 id=&quot;rails-cache-network-calls&quot;&gt;Rails Cache Network Calls&lt;/h2&gt;

&lt;p&gt;In the previous post, &lt;a href=&quot;/ruby/2024/10/24/caches-rails-workflows&quot;&gt;Rails cache workflows&lt;/a&gt;, we covered how a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rails.cache.read&lt;/code&gt; will make a network request to the memcached Server to get the data.&lt;/p&gt;

&lt;div class=&quot;language-ruby 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;no&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;key&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;my cached value&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;key&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;my cached value&quot;&lt;/span&gt;
&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;Rails.cache.read&lt;/code&gt; call will use the Dalli Ruby client to request data from a Memcached server. What does that look like? Let’s look at the code to use Dalli directly to better understand what is happening. The Rails memcached store just provides helpers for working with Dalli.&lt;/p&gt;

&lt;div class=&quot;language-ruby 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;n&quot;&gt;dalli_key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;dalli_key&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;dalli&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Dalli&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;localhost:11211&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;dalli&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dalli_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;my cached value&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dalli&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dalli_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;my cached value&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Usually, when interacting with a cache, you will use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rails.cache&lt;/code&gt;; outside of Rails, you might use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dalli&lt;/code&gt; (or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Redis&lt;/code&gt;) directly. While these gems are great, it is good to know that they are simple network clients under the hood. For example, instead of using Dalli, we could do all of this with far less robust error handling (IE don’t use this code in production). Hopefully, this shows the layers of abstraction between Rails, the underlying caching library, and the Ruby code used to make network requests. Each layer of abstraction adds value and convience, but understanding all the layers can be helpful. For example, when you want to help performance optimize something, knowing which layer is slowing things down can be helpful.&lt;/p&gt;

&lt;div class=&quot;language-ruby 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;n&quot;&gt;sock&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;TCPSocket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;127.0.0.1&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;11211&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;connect_timeout: &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;sock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;setsockopt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Socket&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;IPPROTO_TCP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Socket&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;TCP_NODELAY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;sock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;setsockopt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Socket&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;SOL_SOCKET&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Socket&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;SO_KEEPALIVE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# set a cache value via a raw socket&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;sock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;set sock_key 0 3600 15&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\r\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;sock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;my cached value&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;sock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\r\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;sock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;flush&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;sock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;readline&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# clear the buffer&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# read a cache value via a raw socket&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;sock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;get sock_key&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\r\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;sock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;readline&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;my cached value&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;caches-and-network-failures&quot;&gt;Caches and Network Failures&lt;/h2&gt;

&lt;p&gt;Simple enough, but as with anything over the network, what happens if the network goes down?&lt;/p&gt;

&lt;div class=&quot;language-ruby 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;n&quot;&gt;dalli&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;dalli_key&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;No&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;available&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Dalli&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;RingError&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;That isn’t good; we can’t have trying to use a cache raise exceptions on misses. Well, Rails actually doesn’t. While it uses Dalli under the hood, it has a nicer API that helps avoid things like that.&lt;/p&gt;

&lt;div class=&quot;language-ruby 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;no&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;some_non_existing_key&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;A miss for a key not found in the cache and a miss because the cache was down will both return &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nil&lt;/code&gt;. So, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rails.cache&lt;/code&gt; API makes it a little simpler to work with network errors. It is still worth considering how your code handles these errors. You often don’t want a nil but instead want to get the data from a different source.&lt;/p&gt;

&lt;h2 id=&quot;testing-network-errors-with-toxiproxy&quot;&gt;Testing Network Errors with Toxiproxy&lt;/h2&gt;

&lt;p&gt;OK, we don’t want errors. How do we test what our code will do and how to handle it properly? There are several ways to test network error conditions, but one I like to use is &lt;a href=&quot;https://github.com/Shopify/toxiproxy&quot;&gt;toxiproxy&lt;/a&gt;. It can simulate network and system conditions for chaos and resiliency testing, making it easy to test things like the endpoint has gone down.&lt;/p&gt;

&lt;p&gt;You can read the &lt;a href=&quot;https://github.com/Shopify/toxiproxy-ruby&quot;&gt;toxiproxy ruby docs&lt;/a&gt; for instructions on how to get started, but the example code below should provide the basics.&lt;/p&gt;

&lt;div class=&quot;language-ruby 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;# add to Gemfile&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;toxiproxy&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Also, update your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;test_helper.rb&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-ruby 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;no&quot;&gt;Toxiproxy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;populate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([{&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt; &lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;name: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;memcached&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt; &lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;listen: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;localhost:11222&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt; &lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;upstream: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;localhost:11211&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then you can add toxiproxy code to any of your tests, in this case, to target what happens when memcached isn’t available.&lt;/p&gt;

&lt;div class=&quot;language-ruby 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;# in book.rb&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;save_to_cache&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt; &lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;key_for_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;ttl: &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;600&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get_from_cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt; &lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key_for_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;key_for_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt; &lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Model.book.&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# in book_test.rb&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;test&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;uses cache get&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt; &lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;save!&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt; &lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;save_to_cache&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt; &lt;/span&gt; &lt;span class=&quot;n&quot;&gt;assert_equal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get_from_cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt; &lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Note: toxi proxy is not parallel test safe&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt; &lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Toxiproxy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/memcached/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;down&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt; &lt;/span&gt; &lt;span class=&quot;err&quot;&gt; &lt;/span&gt; &lt;span class=&quot;n&quot;&gt;assert_equal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get_from_cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt; &lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The test above makes it easy to see that the Rails cache won’t raise and will just return &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nil&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The example above also shows why you don’t often see a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rails.cache.read&lt;/code&gt; in isolation. I often use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rails.cache.fetch&lt;/code&gt;. Let’s add some code and write a test to see how that works.&lt;/p&gt;

&lt;div class=&quot;language-ruby 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;# in book.rb&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fetch_from_cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt; &lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key_for_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;id&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;no&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;id&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;end&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# in book_test.rb&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;test&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;uses db on cache fetch failure&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt; &lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;save!&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt; &lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;save_to_cache&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt; &lt;/span&gt; &lt;span class=&quot;n&quot;&gt;assert_equal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get_from_cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt; &lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# toxi proxy is not parallel test safe&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt; &lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Toxiproxy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/memcached/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;down&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt; &lt;/span&gt; &lt;span class=&quot;err&quot;&gt; &lt;/span&gt; &lt;span class=&quot;n&quot;&gt;assert_equal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fetch_from_cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt; &lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In this case, you can see that using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rails.cache.fetch&lt;/code&gt; will more cleanly handle any network issues to your cache server. It also makes it easy to implement read-through caching on any query.&lt;/p&gt;

&lt;h2 id=&quot;cache-request-details&quot;&gt;Cache Request Details&lt;/h2&gt;

&lt;p&gt;While I have some other details I want to get into with Toxiproxy, and how to optimize caching code. This post has gotten long enough, in this post we dug through all the layers and exposed some of the details, and also showed some tooling that can help test and verify network failure conditions. We can build on this further in the next time.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Ruby Caches - Rails Cache Workflows</title>
   <link href="https://www.mayerdan.com/ruby/2024/10/24/caches-rails-workflows"/>
   <updated>2024-10-24T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/ruby/2024/10/24/caches-rails-workflows</id>
   <content type="html">
&lt;p&gt;A series of posts will explore and detail some of the current Rails caching code. This post explores the steps and type of work involved in caching.&lt;/p&gt;

&lt;p&gt;These posts attempts to explain in a bit more details, but please do read the official &lt;a href=&quot;https://guides.rubyonrails.org/caching_with_rails.html#cache-stores&quot;&gt;Rails Caching Guide&lt;/a&gt; docs, which are also very good.&lt;/p&gt;

&lt;p&gt;Rails Cache Posts:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;/ruby/2024/10/17/caches-rails-initialization&quot;&gt;Rails Cache Initialization&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/ruby/2024/10/20/caches-rails-comparisons&quot;&gt;Rails Cache Comparisons&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Rails Cache Workflows&lt;/li&gt;
&lt;/ol&gt;

&lt;h1 id=&quot;rails-cache-workflows&quot;&gt;Rails Cache Workflows&lt;/h1&gt;

&lt;p&gt;What happens as part of caching… Like most software, it isn’t magic. This post will break things down a bit so we can talk about and dig in deeper on some of the specifics. I mostly work with and cache about low-level usage of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rails.cache&lt;/code&gt; while much of the &lt;a href=&quot;https://guides.rubyonrails.org/caching_with_rails.html#low-level-caching&quot;&gt;Rails Caching Guide&lt;/a&gt; focuses on view layer caching. My experience has always required more complex caching at the data layer, not just a lazily deferred view layer. Rails view layer caching is built on these same primitives, so when we talk about low-level caching, we mean the Active Support Cache API for interacting with the cache directly. While it supports many things, we are going to dig in on the most commonly used features:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rails.cache.read&lt;/code&gt;:&lt;/strong&gt; this method gets data from the cache&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rails.cache.write&lt;/code&gt;:&lt;/strong&gt; this method puts data in the cache&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rails.cache.fetch&lt;/code&gt;:&lt;/strong&gt; this method either get from the cache or from the backing data store and then sets it in the cache for the next time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s look at what is happening with these methods so we can see how they help abstract away and handle many shared operations. We will see common patterns like:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;serialization (and deserialization)&lt;/li&gt;
  &lt;li&gt;compression (and decompression)&lt;/li&gt;
  &lt;li&gt;cache store API specifics&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;railscacheread&quot;&gt;Rails.cache.read&lt;/h2&gt;

&lt;p&gt;When calling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rails.cache.read,&lt;/code&gt; the application code is trying to find some piece of data in the cache.&lt;/p&gt;

&lt;div class=&quot;language-ruby 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;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&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;What does this do? It depends, but let’s assume we have the Rails MemCache Store configured as our cache store and a valid Memcached server, which does contain the data stored for the cache &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;key.&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/rails/rails/blob/7750d64a65e5b2641d87ef45e6e65ace193d9a27/activesupport/lib/active_support/cache.rb#L498&quot;&gt;ActiveSupport::Cache::Store.read&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;normalize the key&lt;/li&gt;
      &lt;li&gt;normalize cache key versions (supports recycling keys)&lt;/li&gt;
      &lt;li&gt;instruments the cache call (for tracing, logging, testing, stats)&lt;/li&gt;
      &lt;li&gt;set &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;entry&lt;/code&gt; equal to the response from the configured &lt;a href=&quot;https://github.com/rails/rails/blob/7750d64a65e5b2641d87ef45e6e65ace193d9a27/activesupport/lib/active_support/cache.rb#L498&quot;&gt;ActiveSupport::Cache::MemCacheStore.read_entry&lt;/a&gt; call
        &lt;ul&gt;
          &lt;li&gt;this method does two things &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;deserialize_entry(read_serialized_entry(key, **options), **options)&lt;/code&gt;
            &lt;ul&gt;
              &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;read_serialized_entry&lt;/code&gt;: this will use the Cache store-specific way to get the serialized data out of the cache
                &lt;ul&gt;
                  &lt;li&gt;for the MemCacheStore this means it will use the &lt;a href=&quot;https://github.com/petergoldstein/dalli&quot;&gt;Dalli Memcached Client gem&lt;/a&gt; to request the data over a socket&lt;/li&gt;
                &lt;/ul&gt;
              &lt;/li&gt;
              &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;deserialize_entry&lt;/code&gt;: is back in the base class and uses the coder to get the cache entry and metadata
                &lt;ul&gt;
                  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@coder.load(payload)&lt;/code&gt;: this will first have the coder decompress the raw cache data if needed, then it will deserialize it&lt;/li&gt;
                &lt;/ul&gt;
              &lt;/li&gt;
            &lt;/ul&gt;
          &lt;/li&gt;
          &lt;li&gt;checks meta data on the entry to handle things like cleaning up and not returning expired entries&lt;/li&gt;
          &lt;li&gt;returns the cache &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;entry.value&lt;/code&gt; back to the caller of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cache.read&lt;/code&gt;&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All the above can occur in &lt;strong&gt;less than 1ms&lt;/strong&gt; for a remote network cache call when on a fast cloud regional deployment. It is a lot to take in, let’s &lt;a href=&quot;https://mermaid.live/edit#pako:eNp9Us1ugzAMfpUo1wEPkAMTguukae0RqfKCaREJYfmZxKq--wx0G2OwXOI434-d-MqlqZAL7vAtYCexaOBsQZcdo5X1PYvT9OEFGuUSCfKCiUWoBGtxEEwPJ9pn6Boy0p5Q52Pi4I2dsyfsvB026HvQUaYApZrkjP435Tt9t5q8qbRj_syckS16RpfsdfDoWG3s2m_Grwwm9Lb-TisLxn9N5PTMNlFmfLwKpdG9Reciih3aBlTzgUupH3gcp_G--9TEfNjqcbscqif--6UUydY9LiWTd1ABdz54VKEBEazIjhmPuEaroalolq4jo-T-ghpLLiissIagfMnL7kZQCN4chk5y4W3AiFsTzhcualCOTqGvwH8N4j17-wSb-unY&quot;&gt;diagram it&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/img/cache_read.webp&quot;&gt;&lt;img src=&quot;/assets/img/cache_read.webp&quot; alt=&quot;Rails.cache.read&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;railscachewrite&quot;&gt;Rails.cache.write&lt;/h2&gt;

&lt;p&gt;When calling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rails.cache.write&lt;/code&gt; the application code is trying to store some piece of data in the cache.&lt;/p&gt;

&lt;div class=&quot;language-ruby 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;n&quot;&gt;book&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;first&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# some active record book object&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;ttl: &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3600&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;What does this do? It depends, but let’s assume we have the Rails MemCache Store configured as our cache store and a valid Memcached server.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/rails/rails/blob/7750d64a65e5b2641d87ef45e6e65ace193d9a27/activesupport/lib/active_support/cache.rb#L660&quot;&gt;ActiveSupport::Cache::Store.write&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;normalize the key&lt;/li&gt;
      &lt;li&gt;normalize cache key versions (supports recycling keys)&lt;/li&gt;
      &lt;li&gt;instruments the cache call (for tracing, logging, testing, stats)&lt;/li&gt;
      &lt;li&gt;builds the CacheEntry object (meta data and value wrapper)&lt;/li&gt;
      &lt;li&gt;calls &lt;a href=&quot;https://github.com/rails/rails/blob/7750d64a65e5b2641d87ef45e6e65ace193d9a27/activesupport/lib/active_support/cache/mem_cache_store.rb#L193&quot;&gt;ActiveSupport::Cache::MemCacheStore.write_entry&lt;/a&gt; to set the value
        &lt;ul&gt;
          &lt;li&gt;this method does two things ` write_serialized_entry(key, serialize_entry(entry, **options), **options)`&lt;/li&gt;
          &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;serialize_entry&lt;/code&gt;: is back in the base class and uses the coder to serialize the data
            &lt;ul&gt;
              &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@coder.dump_compressed(payload)&lt;/code&gt;: (or just dump if no compression needed) This will have coder serialize and then compress the data&lt;/li&gt;
            &lt;/ul&gt;
          &lt;/li&gt;
          &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;write_serialized_entry&lt;/code&gt;: this will use the Cache store-specific way to set the serialized data to the cache
            &lt;ul&gt;
              &lt;li&gt;for the MemCacheStore this means it will use the &lt;a href=&quot;https://github.com/petergoldstein/dalli&quot;&gt;Dalli Memcached Client gem&lt;/a&gt; to send the data over a socket&lt;/li&gt;
            &lt;/ul&gt;
          &lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;returns true if set successfully&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All the above can occur in &lt;strong&gt;less than 1ms&lt;/strong&gt; for a remote network cache call when on a fast cloud regional deployment. It is a lot to take in, let’s &lt;a href=&quot;https://mermaid.live/edit#pako:eNqNkk1uwyAQha-C2NbxAVhEqpxdVbVqurQUTWEco_DjwqDKjXL3Qu0mUuOoZQMM73szMBy59Aq54BHfEzqJGw37ALZ1LI_7YWCr9fruBbSJtQTZY_0RNKFgBxwFs-MuzxVTQDARV8qCP6JtSmRLPszhHToKiw43xcWpycWGWiU7CBYxaDD6Exk4xaS3Q8AY_-u0AWN0HZEuNbyNhHFCz6dz-d8XUoK9Ns8senlAyulpIljnw-xxzjvpf-V5elg2v_U2FBL-dZNssVrozgW9bkhBcmMnEa-4xWBBq_wHjgVpOfVoseUiLxV2kAy1vHWnLIVEfjs6yUVhKx582vdcdGBi3qUhv_3PB5qjpy8eWNGO&quot;&gt;diagram it&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/img/cache_write.webp&quot;&gt;&lt;img src=&quot;/assets/img/cache_write.webp&quot; alt=&quot;Rails.cache.write&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1 id=&quot;railscachefetch&quot;&gt;Rails.cache.fetch&lt;/h1&gt;

&lt;p&gt;A very common pattern with caching is to try to get a piece of data from the cache, and if you can’t find it, get the data from another data store and save it to the cache so you will get it next time. Rails has the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rails.cache.fetch&lt;/code&gt; helper to simplify this.&lt;/p&gt;

&lt;div class=&quot;language-ruby 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;no&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;ttl: &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3600&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;err&quot;&gt; &lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;first&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# some active record book object&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;It is easiest to think of fetch in terms of both the previous read and write. While it duplicates some of the underlying code, it does not always directly differ from those methods. Let’s break this down for the Memcache store in the case where there is a miss on the cache read.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/rails/rails/blob/7750d64a65e5b2641d87ef45e6e65ace193d9a27/activesupport/lib/active_support/cache.rb#L444&quot;&gt;Rails.cache.fetch&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;entry = read_entry&lt;/code&gt;: this does most of the same as read, and we will in this case, assume it was a miss and return nil&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;save_block_result_to_cache(name, key, options, &amp;amp;block)&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;call the block, which in this case is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Book.first&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;then it is like cache.write with that value&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While I very briefly covered fetch, it can do many other interesting things that help with the complexity and edge cases when using a cache, such as race conditions, nil handling, forcing updates, etc.&lt;/p&gt;

&lt;p&gt;The most common cache call in most of the Rails apps I work in is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rails.cache.fetch&lt;/code&gt; it is extremely powerful for very basic caching.&lt;/p&gt;

&lt;h1 id=&quot;notes-on-using-rails-cache&quot;&gt;Notes on Using Rails Cache&lt;/h1&gt;

&lt;p&gt;The purpose of this post was to get a bit deeper into the foundational concepts and steps related to caching with the active support cache store API. So, we can further explore some of the important concepts like serialization, compression, and specific cache store implementations. It is important to realize that for each different store type, Rails relies on a client; in this case, we talked about Dalli being the Memcached client, but if you use the Redis store, Rails will end up using the Redis gem. While Rails will default to using compression and serialization built into Ruby, other options (often better options) also rely on the gem ecosystem outside of Rails.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;In my example, I cached an active record object. The Rails caching guide rightly warns against this, as we get into serialization and versioning, we can cover solutions to problems with caching Active Record objects&lt;/li&gt;
  &lt;li&gt;These examples should also make it clear that much is happening behind the scenes for such a simple API (can’t get much easier than &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;read(&apos;key&quot;)&lt;/code&gt;), it is important to understand the different pieces when wanting to optimize performance further when working on caching
    &lt;ul&gt;
      &lt;li&gt;for complex data structures, serialization can take longer than network calls&lt;/li&gt;
      &lt;li&gt;for certain size data and data formats, avoiding compression can make sense&lt;/li&gt;
      &lt;li&gt;knowing how caching breaks down into these steps can help when benchmarking and profiling caching approaches that work best for your data&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We will dig in a bit more in a future post.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Creating Consistent Characters Across Images</title>
   <link href="https://www.mayerdan.com/programming/2024/10/22/consistent-ai-book-characters"/>
   <updated>2024-10-22T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/programming/2024/10/22/consistent-ai-book-characters</id>
   <content type="html">
&lt;p&gt;I have been creating children’s books with my kids using AI for a while now. I recently talked to someone, and they were doing the same thing but using just the ChatGPT bot and creating a single image for a short story. I will cover some of my recent progress working on this side project, as well as some of the methods I use.&lt;/p&gt;

&lt;h2 id=&quot;the-text-isnt-the-problem&quot;&gt;The Text Isn’t the Problem&lt;/h2&gt;

&lt;p&gt;Early on, generating amusing enough stories for the kids was super easy. My daughter would request something like, “I want a book about me being a puppy doctor.” Some simple prompt trial and error and parsing would easily make a cute book of any desired length and could even adjust the vocabulary to the appropriate reading level. One could even go for specific styles with rhymes, humor, first person, etc… I will cover the text generation in another post sometime, but once I have an excellent short story, I will try to generate images.&lt;/p&gt;

&lt;h2 id=&quot;early-image-problems&quot;&gt;Early Image Problems&lt;/h2&gt;

&lt;p&gt;The first book’s images were hilariously bad. They were so bad that it was part of the humor. We would all laugh at the incredibly silly images, but immediately, my kids would start to point out all the inconsistencies of the images on each page.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Why doesn’t the book’s Sasha look at all like her?&lt;/li&gt;
  &lt;li&gt;Why does she have long brown hair on one page, short on another, and orange hair somewhere else?&lt;/li&gt;
  &lt;li&gt;Why do the clothes change on every single page?&lt;/li&gt;
  &lt;li&gt;Why do the age, height, etc, vary constantly?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/early_sasha_book.webp&quot; alt=&quot;Images From Sasha and the 3 little pigs&quot; /&gt;
&lt;img src=&quot;/assets/img/a_theo2_squ.png&quot; alt=&quot;Image From Theo and the Squirrels&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;These images were from Dalli-2 and early stable diffusion. I covered some of the processes in a post, &lt;a href=&quot;fun/2022/12/20/ai-book-building&quot;&gt;Ai Book Building&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;early-attempts-to-improve-the-images&quot;&gt;Early Attempts to Improve the Images&lt;/h2&gt;

&lt;p&gt;I started trying a few methods to improve the quality of the images created.&lt;/p&gt;

&lt;h3 id=&quot;improved-and-consistent-image-prompts&quot;&gt;Improved and Consistent Image Prompts&lt;/h3&gt;

&lt;p&gt;My first attempts to resolve this were just improved prompts. I would add a trick that would regularize the image prompt with additional details describing the character whenever they were to be in the illustration. This was slightly better, but not by much. Most of the quality improvements were from improved Stable Diffusion models.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/theo_promots.webp&quot; alt=&quot;Theo The Astronaut&quot; /&gt;
&lt;img src=&quot;/assets/img/purple_wizard.webp&quot; alt=&quot;A Purple Wizard&quot; /&gt;
&lt;img src=&quot;/assets/img/whale_mountains.webp&quot; alt=&quot;Wilbur the Whale&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;Prompts included consistent characters Theo the astronaut, a purple good wizard, and Wilbur the Whale&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;image2image-support&quot;&gt;Image2Image Support&lt;/h3&gt;

&lt;p&gt;I next allowed the book creator to select and upload images that would be used along with image2image and prompts to generate semi-recognizable images that were stylized across the book. The Img2Img technique had several problems:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;You end up wanting a unique image per page&lt;/li&gt;
  &lt;li&gt;The closer the initial image is to the actual desired content, the better quality&lt;/li&gt;
  &lt;li&gt;If the photos aren’t consistent, neither is the output&lt;/li&gt;
  &lt;li&gt;The output quality was hit-and-miss&lt;/li&gt;
  &lt;li&gt;It was extremely time-consuming, now resulting in photo shoots just to seed the book generation&lt;/li&gt;
  &lt;li&gt;I don’t want to upload a bunch of pictures of my kids to any of the cloud AI companies, so I run it all locally on my own machine&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I was stuck at this for a while, so I made a post about the process of using &lt;a href=&quot;/software/2023/02/27/making-book-covers-with-img2img&quot;&gt;img2img to make book covers&lt;/a&gt;. Combining this with different Stable Diffusion models and styles can get good results, but sometimes it takes many iterations or is difficult to get a cartoon or children’s book illustration-like style while also still being recognizable as the character.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/dr_sasha-COLLAGE.webp&quot; alt=&quot;img2img results&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;While time-consuming, this technique allowed me to put my kids into their books finally&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;training-lora-models&quot;&gt;Training LoRA models&lt;/h1&gt;

&lt;blockquote&gt;
  &lt;p&gt;“A LoRA diffusion model is a type of model that uses Low-Rank Adaptation (LoRA) to fine-tune a diffusion model for a specific domain, character, style, or concept”
– &lt;a href=&quot;https://civitai.com/articles/2099/lora-models-and-how-to-use-them-with-stable-diffusion-by-thinkdiffusion&quot;&gt;LoRA Models can be trained in the cloud&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Eventually, I learned how to take a small collection of high-quality images of one of my kids and train a LoRA model. The tools for local training are a bit hard to work with, but they also can be run in the cloud fairly quickly. Making a LoRA model for use to generate images on a local machine takes about one hour of machine time on my RTX 480. After some trial and error, I can build Stable Diffusion 1.5 or XL models that could fairly frequently build recognizable characters in any situation. The quality of any given image varied a lot, and the older Stable Diffusion models couldn’t set scenes in the books very well. It was close, but not good enough to take the quality of the books to the next level. Combining this technique and custom Stable Diffusion models that better match the scene/theme of the book would help, but only a little, and it was a lot of work. These could occasionally produce very good results, but a large number of image generations per page were required.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/lora-COLLAGE.webp&quot; alt=&quot;img2img results&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;Training local LoRA models, was a pretty big breakthrough in terms of more easily making repeatable recognizable characters&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I will post on how to train LoRAs on your local machine sometime easily, but you can check out &lt;a href=&quot;https://github.com/cocktailpeanut/fluxgym&quot;&gt;Flux Gym&lt;/a&gt; to get started.&lt;/p&gt;

&lt;h1 id=&quot;flux-lora-models&quot;&gt;Flux LoRA Models&lt;/h1&gt;

&lt;p&gt;The next big breakthrough came when Flux dev was released. This image model was far superior in building scenes from prompts, so you could put characters in any scene the book would require… The trick of detailed descriptions of a character and a scene also went a long way. However, now I have seen how recognizable and good a trained LoRA model could look with image models. I needed a way to generate Flux LoRA models, but on my RTX 4080 graph card with 16GB of RAM (or I could get it all running in the cloud). The problem was that tooling for training local Flux LoRAs required 24GB, but folks kept tweaking things and layering various improvements. It is slow, but folks are training models on 8GB graphic cards these days.&lt;/p&gt;

&lt;p&gt;I can now take a small, high-quality collection of photos of my subject and generate a pretty good Flux LoRA in about 1 hour on my local machine. Example images for input are below.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/sasha_images.webp&quot; alt=&quot;Sasha Images&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;Example training set of images, this trains a person model with a focus mostly on the face, but will capture basic size / age&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;consistent-characters&quot;&gt;Consistent Characters&lt;/h1&gt;

&lt;p&gt;Now, after spending a bit more time and getting better at training LoRA models and improving the tools, I can fairly easily build a model for any character I want. I generally just need 30-40 images and a bit of time cropping and adjusting the image inputs. You can do this quickly, but the better your input images are, the better your model turns out; I am sure I can improve my process of collecting the input images, but the output quality is already good enough for making fun and easily recognizable consistent characters across one of my kid’s book ideas. You can get consistent outfits with a few details in the prompt, you can get wildly imaginative scenes. It is easy to generate various styles: 2d, 3d, photo-realistic, cartoon, watercolor, etc.&lt;/p&gt;

&lt;p&gt;I now have LoRA models of the whole family and can generate funny and silly images. The kids enjoy just requesting silly images, not even full books now. The styles for the image outputs are very flexible, from oil paintings to movie animation styles.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/fun_family.webp&quot; alt=&quot;Random Lora Images&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;A bunch of random LoRA images showing the variety&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;a-prompt-across-lora-models&quot;&gt;A prompt across LoRA Models&lt;/h1&gt;

&lt;p&gt;To give a bit more of an idea of how the training works and the output it produces, here are a few sample prompts and the output images showing my family inserted into book themes.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/ai_cowboy.webp&quot; alt=&quot;img2img results&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;Prompt “ultra-realistic photo of ((MODEL_TARGET_NAME dressed in a cowboy hat and jeans)), hyper detail, cinematic lighting, magic neon, Canon EOS R3, Nikon, f/1.4, ISO 200, 1/160s, 8K, RAW, unedited, symmetrical balance, in-frame, 8K”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/family_space.webp&quot; alt=&quot;img2img results&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;Prompt “a colorful cartoon image of ((MODEL_TARGET_NAME as an astronaut in space)), hyper detail, cinematic lighting, colorful, 8K”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I build all of these books using a custom Rails app that can use local or remote text and image generators. I will like share some more posts on some of the scripts and code that makes it easier to automate all these processes.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Ruby Caches - Rails Cache Comparisons</title>
   <link href="https://www.mayerdan.com/ruby/2024/10/20/caches-rails-comparisons"/>
   <updated>2024-10-20T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/ruby/2024/10/20/caches-rails-comparisons</id>
   <content type="html">
&lt;p&gt;A series of posts will explore and detail some of the current Rails caching code. This post compares a few of the cache store options.&lt;/p&gt;

&lt;p&gt;These posts attempts to explain in a bit more details, but please do read the official &lt;a href=&quot;https://guides.rubyonrails.org/caching_with_rails.html#cache-stores&quot;&gt;Rails Caching Guide&lt;/a&gt; docs, which are also very good.&lt;/p&gt;

&lt;p&gt;Rails Cache Posts:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;/ruby/2024/10/17/caches-rails-initialization&quot;&gt;Rails Cache Initialization&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Rails Cache Comparisons&lt;/li&gt;
&lt;/ol&gt;

&lt;h1 id=&quot;rails-caching-comparisons&quot;&gt;Rails Caching Comparisons&lt;/h1&gt;

&lt;p&gt;Rails offers many caching options, most of the difference will matter more  as you scale your system architecture. The performance difference of the various options for small scale systems and single box deployments is not that significant. For example, using the Rails in-memory cache works very well for single process applications, it however doesn’t scale to the most common Rails deployments which is Puma with a few workers and threads. At that point you will often see much higher performance with a cache like Memcached or Redis, at that point to maintain extremely low latency you might choose an on box Memcached, which removes the network latency of a remote cache. That will work well until you need to horizontally scale your web workers (or background jobs). At that point you will likely consider remote cache solutions like Memcached, Redis, or the latest SolidCache.&lt;/p&gt;

&lt;h2 id=&quot;rails-cache-stores&quot;&gt;Rails Cache Stores&lt;/h2&gt;

&lt;p&gt;We can cover a quick overview of the various cache stores and their trade offs. Also, note you can make a custom store that builds off any of these or offers interesting options like layered caching combining both a machine local cache and a remote cache.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Store&lt;/th&gt;
      &lt;th&gt;Serializes&lt;/th&gt;
      &lt;th&gt;Compression&lt;/th&gt;
      &lt;th&gt;Local Store&lt;/th&gt;
      &lt;th&gt;Cross Proccess&lt;/th&gt;
      &lt;th&gt;Node Local&lt;/th&gt;
      &lt;th&gt;Remote&lt;/th&gt;
      &lt;th&gt;Cluster&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;a href=&quot;https://guides.rubyonrails.org/caching_with_rails.html#activesupport-cache-memorystore&quot;&gt;memory store&lt;/a&gt;&lt;/td&gt;
      &lt;td&gt;✓&lt;/td&gt;
      &lt;td&gt;No&lt;/td&gt;
      &lt;td&gt;No&lt;/td&gt;
      &lt;td&gt;No&lt;/td&gt;
      &lt;td&gt;No&lt;/td&gt;
      &lt;td&gt;No&lt;/td&gt;
      &lt;td&gt;No&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;a href=&quot;https://guides.rubyonrails.org/caching_with_rails.html#activesupport-cache-filestore&quot;&gt;file store&lt;/a&gt;&lt;/td&gt;
      &lt;td&gt;✓&lt;/td&gt;
      &lt;td&gt;✓&lt;/td&gt;
      &lt;td&gt;No&lt;/td&gt;
      &lt;td&gt;✓&lt;/td&gt;
      &lt;td&gt;✓&lt;/td&gt;
      &lt;td&gt;No&lt;/td&gt;
      &lt;td&gt;No&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;a href=&quot;https://guides.rubyonrails.org/caching_with_rails.html#activesupport-cache-memcachestore&quot;&gt;memcached store&lt;/a&gt;&lt;/td&gt;
      &lt;td&gt;✓&lt;/td&gt;
      &lt;td&gt;✓&lt;/td&gt;
      &lt;td&gt;✓&lt;/td&gt;
      &lt;td&gt;✓&lt;/td&gt;
      &lt;td&gt;✓&lt;/td&gt;
      &lt;td&gt;✓&lt;/td&gt;
      &lt;td&gt;✓&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;a href=&quot;https://guides.rubyonrails.org/caching_with_rails.html#activesupport-cache-rediscachestore&quot;&gt;redis store&lt;/a&gt;&lt;/td&gt;
      &lt;td&gt;✓&lt;/td&gt;
      &lt;td&gt;✓&lt;/td&gt;
      &lt;td&gt;✓&lt;/td&gt;
      &lt;td&gt;✓&lt;/td&gt;
      &lt;td&gt;✓&lt;/td&gt;
      &lt;td&gt;✓&lt;/td&gt;
      &lt;td&gt;✓&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;a href=&quot;https://github.com/rails/solid_cache&quot;&gt;solid cache store&lt;/a&gt;&lt;/td&gt;
      &lt;td&gt;✓&lt;/td&gt;
      &lt;td&gt;✓&lt;/td&gt;
      &lt;td&gt;✓&lt;/td&gt;
      &lt;td&gt;✓&lt;/td&gt;
      &lt;td&gt;Maybe&lt;/td&gt;
      &lt;td&gt;✓&lt;/td&gt;
      &lt;td&gt;✓&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Another point that is harder to list in the table is what is memory limited vs disk space limited. The majority of the cache systems are memory limited, but the file store and solid cache (which is backed by a database) is disk space limited.&lt;/p&gt;

&lt;p&gt;If there are other considerations or trade offs that should be added to the table let me know.&lt;/p&gt;

&lt;h2 id=&quot;compression&quot;&gt;Compression&lt;/h2&gt;

&lt;p&gt;The Rails cache for a long time has supported gzip compression, but recently added support passing in your own compression library. This makes it easier to use faster compression libraries like zstd, snappy, and l4z_flex. In general, so long as you are maintaining another dependency already, using a more modern compression library will yield better performance. Compression is even more valuable when you consider the network overhead of transmitting the cache value bytes over the network.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;the compression was done against an active record object including a large number of associations with some random string data&lt;/li&gt;
  &lt;li&gt;the data was serialized using the default Marshal serializer&lt;/li&gt;
  &lt;li&gt;then we just compressed the serialized bytes&lt;/li&gt;
&lt;/ul&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;original_value = Book.all.to_a
cache_value =  Marshal.dump(original_value)

rows &amp;lt;&amp;lt; [&quot;No Compression&quot;, cache_value.bytesize]
rows &amp;lt;&amp;lt; [&quot;BrotliCompressor&quot;, Compressors::BrotliCompressor.deflate_byte_size(cache_value)]
rows &amp;lt;&amp;lt; [&quot;SnappyCompressor&quot;, Compressors::SnappyCompressor.deflate_byte_size(cache_value)]
rows &amp;lt;&amp;lt; [&quot;ZstdCompressor&quot;, Compressors::ZstdCompressor.deflate_byte_size(cache_value)]
rows &amp;lt;&amp;lt; [&quot;L4zCompressor&quot;, Compressors::L4zCompressor.deflate_byte_size(cache_value)]
rows &amp;lt;&amp;lt; [&quot;LZ4FlexCompressor&quot;, Compressors::LZ4FlexCompressor.deflate_byte_size(cache_value)]

puts &quot;benchmarking Rails #{@cache} cache compression...&quot;
table = Terminal::Table.new :headings =&amp;gt; [&apos;Compressor&apos;, &apos;Bytesize&apos;], :rows =&amp;gt; rows
puts table
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This shows the byte size of the cache value after compression using each of the various compression libraries.&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;+-------------------+----------+
| Compressor        | Bytesize |
+-------------------+----------+
| No Compression    | 1023931  |
| BrotliCompressor  | 577629   |
| SnappyCompressor  | 895496   |
| ZstdCompressor    | 587085   |
| L4zCompressor     | 879179   |
| LZ4FlexCompressor | 878160   |
+-------------------+----------+
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Obviously, the final bytesize isn’t the only consideration, you also have to consider the CPU overhead of compressing and decompressing the data. The tradeoff on final size also matters less if you aren’t making cache calls over the network. So let’s also benchmark the speed of the compressors, in this case we are just taking the serialized bytes and compressing them, and measuring the speed of the compression (we aren’t decompressing them).&lt;/p&gt;

&lt;p&gt;Below is the output of the benchmark, you can see that snappy is the fastest compression library, followed by lz4_flex (a rust based compression library).&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;Warming up --------------------------------------
                Gzip     3.000 i/100ms
              Brotli     3.000 i/100ms
                Zstd    26.000 i/100ms
              Snappy   316.000 i/100ms
                 LZ4   111.000 i/100ms
             LZ4Flex   196.000 i/100ms
Calculating -------------------------------------
                Gzip     35.028 (± 2.9%) i/s -    177.000 in   5.054752s
              Brotli     35.899 (± 2.8%) i/s -    180.000 in   5.015287s
                Zstd    278.536 (± 1.8%) i/s -      1.404k in   5.042192s
              Snappy      3.087k (± 2.9%) i/s -     15.484k in   5.020421s
                 LZ4      1.101k (± 2.5%) i/s -      5.550k in   5.042399s
             LZ4Flex      1.959k (± 3.5%) i/s -      9.800k in   5.008819s

Comparison:
              Snappy:     3086.8 i/s
             LZ4Flex:     1959.3 i/s - 1.58x  slower
                 LZ4:     1101.4 i/s - 2.80x  slower
                Zstd:      278.5 i/s - 11.08x  slower
              Brotli:       35.9 i/s - 85.98x  slower
                Gzip:       35.0 i/s - 88.12x  slower
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I honestly recommend benchmarking this on the system you deploy on as I have seen some variability in performance between different systems.&lt;/p&gt;

&lt;h2 id=&quot;serialization&quot;&gt;Serialization&lt;/h2&gt;

&lt;p&gt;While Marshal has been the default serializer for Rails for a long time, it is not the fastest serializer. The fastest serializer is MessagePack, which has been recently supported in Rails for serialization in a number of places, not just the cache.&lt;/p&gt;

&lt;p&gt;I recommend using MessagePack for serialization, not only is it faster it avoids a lot fo the downsizes of Marshal and is far less verbose than JSON. There is not really any good reason to not use MessagePack for serialization.&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;benchmarking serializers (serialize + deserialize)...
ruby 3.3.4 (2024-07-09 revision be1089c8ec) [arm64-darwin23]
Warming up --------------------------------------
             Marshal     6.000 i/100ms
                JSON     6.000 i/100ms
            AS::JSON     6.000 i/100ms
         MessagePack     7.000 i/100ms
Calculating -------------------------------------
             Marshal     62.984 (± 4.8%) i/s -    318.000 in   5.067421s
                JSON     61.427 (± 3.3%) i/s -    312.000 in   5.082445s
            AS::JSON     61.546 (± 3.2%) i/s -    312.000 in   5.075183s
         MessagePack     78.024 (± 2.6%) i/s -    392.000 in   5.028982s

Comparison:
         MessagePack:       78.0 i/s
             Marshal:       63.0 i/s - 1.24x  slower
            AS::JSON:       61.5 i/s - 1.27x  slower
                JSON:       61.4 i/s - 1.27x  slower

Just serialization:
Warming up --------------------------------------
             Marshal    11.000 i/100ms
                JSON     7.000 i/100ms
            AS::JSON     7.000 i/100ms
         MessagePack    15.000 i/100ms
Calculating -------------------------------------
             Marshal    114.629 (± 3.5%) i/s -    583.000 in   5.090881s
                JSON     74.783 (± 2.7%) i/s -    378.000 in   5.058664s
            AS::JSON     76.329 (± 2.6%) i/s -    385.000 in   5.047530s
         MessagePack    155.636 (± 3.2%) i/s -    780.000 in   5.017039s

Comparison:
         MessagePack:      155.6 i/s
             Marshal:      114.6 i/s - 1.36x  slower
            AS::JSON:       76.3 i/s - 2.04x  slower
                JSON:       74.8 i/s - 2.08x  slower
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;There are a few objects that are harder to serialize with MsgPack, but generally it is easy to add a custom helper to support them, there are a few odd cases where you can technically find Message Pack is slower than Marshal, but I have not seen this in real world data.&lt;/p&gt;

&lt;p&gt;Read more on moving to message pack:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://shopify.engineering/caching-without-marshal-part-one&quot;&gt;Caching without Marshal, Part One&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://shopify.engineering/caching-without-marshal-part-two&quot;&gt;Caching without Marshal, Part Two&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.saeloun.com/2023/11/15/rails-7-1-message-pack-as-message-serializer/&quot;&gt;Moving to MessagePack&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/rails/rails/tree/a1f6a13f691e0929d40b7e1b1e0d31aa69778128/activesupport/lib/active_support/message_pack&quot;&gt;MessagePack Serializer&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://rubykaigi.org/2022/presentations/shioyama.html&quot;&gt;Caching with MsgPack talk&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;how-to-optimize-and-benchmark-for-network-architecture-and-latency&quot;&gt;How to optimize and benchmark for network architecture and latency&lt;/h2&gt;

&lt;p&gt;I will make another post soon that gets more into how to optimize and benchmark for network architecture and latency. I originally tried some techniques that were not the best and wanted to share what worked and what didn’t. If you are just adding caching to a small application, hopefully you can avoid needing to be highly optimized at this level. When starting to look at remote caching, it will be important to consider things like the latency between app servers and the cache server, for example if you have regionally distributed app servers you may want co-located caches in each region, vs a global cache across regions. As this topic opens up many more questions, we can devote a full post to the topic.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;We can combine all of the benchmarks to benchmark end to end with any given serializer, compression library, and cache store. The end to end benchmarks make it a bit harder to see the impact of the various changes and most of the time ends up disappearing into the call to the cache store, so the data normalizes the results.&lt;/p&gt;

&lt;p&gt;I would recommend starting new applications (or new caching setups) with:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Serialization:&lt;/strong&gt; Message Pack&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Compression:&lt;/strong&gt; one of the modern compression libraries (Snappy or LZ4Flex)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Cache Store:&lt;/strong&gt; I would then recommend using the memcached store as it works well even on a local machine installation, and scales across load balanced clusters for extremely large deployments with very simple deployment hassle.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A note on why not Redis, it can be a great choice, but the api for redis is more complex than memcached. The large scale deployments can be a bit more complex. The real issue is that if you make a redis available for caching, you will likely find folks starting to use it for other things. For example trying to share it across background jobs, or using it for other parts of the system architecture. The configuration you want for a caching redis is different than for a background jobs redis, then folks make compromises like having high availability redis as it is needed for jobs, and then using it for caching, which is a far more expensive and not needed configuration for caching. Redis also supports Lua scripts and more complex data types, which can all be great solutions when properly discussed and considered, but you don’t want to find them becoming hidden complexity that folks build on top of what was supposed to be a simple caching solution. Memcached avoids most of these problems by offering a very robust, but more restrictive API.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Ruby Caches - Rails Cache Initialization</title>
   <link href="https://www.mayerdan.com/ruby/2024/10/17/caches-rails-initialization"/>
   <updated>2024-10-17T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/ruby/2024/10/17/caches-rails-initialization</id>
   <content type="html">
&lt;h1 id=&quot;rails-cache-initialization&quot;&gt;Rails Cache Initialization&lt;/h1&gt;

&lt;p&gt;A series of posts will explore and detail some of the current Rails caching code. A good place to start is how the Rails cache is configured and loaded.&lt;/p&gt;

&lt;p&gt;This attempts to explain in a bit more details, but please do read the official &lt;a href=&quot;https://guides.rubyonrails.org/caching_with_rails.html#cache-stores&quot;&gt;Rails Caching Guide&lt;/a&gt; docs, which are also very good.&lt;/p&gt;

&lt;h1 id=&quot;the-rails-cache-configuration&quot;&gt;The Rails Cache Configuration&lt;/h1&gt;

&lt;p&gt;Often, folks use different Rails cache configurations in different environments. It isn’t uncommon for folks to have a NullCacheStore in a test or a memory-only cache-store. Then, in development, they have a Memcache or Redis cache with short-lived TTLs, and then a production cluster of cache servers. Let’s see how Rails accomplishes letting folks load different caching configurations.&lt;/p&gt;

&lt;p&gt;The Rails cache configuration code by default lives in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;project/config/environments/&amp;lt;ENV&amp;gt;.rb&lt;/code&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;project
│   README.md
│   ...    
│
└───config
│   │   ...
│   │
│   └───environments
│       │   test.rb
│       │   development.rb
│       │   production.rb
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;A basic development configuration might look something like:&lt;/p&gt;

&lt;div class=&quot;language-ruby 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;err&quot;&gt; &lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Enable/disable caching. By default, caching is disabled.&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt; &lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Run rails dev:cache to toggle caching.&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt; &lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;tmp/caching-dev.txt&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;exist?&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;action_controller&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;perform_caching&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;action_controller&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;enable_fragment_cache_logging&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;cache_store&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:redis_cache_store&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;err&quot;&gt; &lt;/span&gt; &lt;span class=&quot;err&quot;&gt; &lt;/span&gt; &lt;span class=&quot;err&quot;&gt; &lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;connect_timeout: &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;err&quot;&gt; &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# Defaults to 1 second&lt;/span&gt;
    &lt;span class=&quot;err&quot;&gt; &lt;/span&gt; &lt;span class=&quot;err&quot;&gt; &lt;/span&gt; &lt;span class=&quot;err&quot;&gt; &lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;read_timeout: &lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Defaults to 1 second&lt;/span&gt;
    &lt;span class=&quot;err&quot;&gt; &lt;/span&gt; &lt;span class=&quot;err&quot;&gt; &lt;/span&gt; &lt;span class=&quot;err&quot;&gt; &lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;write_timeout: &lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Defaults to 1 second&lt;/span&gt;
    &lt;span class=&quot;err&quot;&gt; &lt;/span&gt; &lt;span class=&quot;err&quot;&gt; &lt;/span&gt; &lt;span class=&quot;err&quot;&gt; &lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;reconnect_attempts: &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;err&quot;&gt; &lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Defaults to 1&lt;/span&gt;
    &lt;span class=&quot;err&quot;&gt; &lt;/span&gt; &lt;span class=&quot;err&quot;&gt; &lt;/span&gt; &lt;span class=&quot;err&quot;&gt; &lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;expires_in: &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;day&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;

    &lt;span class=&quot;err&quot;&gt; &lt;/span&gt; &lt;span class=&quot;err&quot;&gt; &lt;/span&gt; &lt;span class=&quot;err&quot;&gt; &lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;error_handler: &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;returning&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exception&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;err&quot;&gt; &lt;/span&gt; &lt;span class=&quot;err&quot;&gt; &lt;/span&gt; &lt;span class=&quot;err&quot;&gt; &lt;/span&gt; &lt;span class=&quot;err&quot;&gt; &lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Redis error: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exception&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;, method: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;, returning: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;returning&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&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;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;public_file_server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;headers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;err&quot;&gt; &lt;/span&gt; &lt;span class=&quot;err&quot;&gt; &lt;/span&gt; &lt;span class=&quot;err&quot;&gt; &lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Cache-Control&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;public, max-age=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;days&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_i&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt; &lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;action_controller&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;perform_caching&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;cache_store&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:null_store&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt; &lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;cache-initialization&quot;&gt;Cache Initialization&lt;/h2&gt;

&lt;p&gt;The configuration sets up all the basic options and tells the application the type of cache to build for a given environment. During Application startup, Rails will build the cache, so that you get your &lt;a href=&quot;https://api.rubyonrails.org/v7.1/classes/ActiveSupport/Cache/Store.html&quot;&gt;active support cache store&lt;/a&gt; when you call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rails.cache&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;How does it build that cache store?&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The &lt;a href=&quot;https://github.com/rails/rails/blob/6e284f339bb123575a61852e25dff278a764d9b3/railties/lib/rails/application/bootstrap.rb#L82&quot;&gt;application bootstrap railtie, initialize_cache&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;This bootstrap will read the cache config, then feed it into &lt;a href=&quot;https://apidock.com/rails/v7.1.3.4/ActiveSupport/Cache/lookup_store/class&quot;&gt;ActiveSupport::Cache.lookup_store&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Which will load any Rails cache, including custom ones, so long as the symbol you pass can be required like so &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;require &quot;active_support/cache/#{store}&quot;&lt;/code&gt;
    * the pattern should be a symbol of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:redis_cache_store&lt;/code&gt; will look for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;active_support/cache/redis_cache_store.rb&lt;/code&gt;
    * it will then call new on your cache store, passing in all the additional options you provided in your config&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;make-your-own&quot;&gt;Make Your Own&lt;/h2&gt;

&lt;p&gt;Given all this, you should be able to create your own &lt;a href=&quot;https://www.mobomo.com/2012/03/writing-a-custom-rails-cache-store/&quot;&gt;custom Active Support Cache Store&lt;/a&gt;. Or you can configure the standard stores and set up multiple caches beyond just &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rails.cache&lt;/code&gt;. If you have different caching needs, you can make a new cache the same way Rails does.&lt;/p&gt;

&lt;div class=&quot;language-ruby 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;no&quot;&gt;AnotherCache&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveSupport&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Cache&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;RedisCacheStore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt; &lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;connect_timeout: &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;read_timeout: &lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;write_timeout: &lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;reconnect_attempts: &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;err&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;expires_in: &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;day&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;h2 id=&quot;what-happens-when-a-cache-store-initializes&quot;&gt;What happens when a Cache Store Initializes&lt;/h2&gt;

&lt;p&gt;It can depend on the implementation, but let’s take a closer look at &lt;a href=&quot;https://github.com/rails/rails/blob/6e284f339bb123575a61852e25dff278a764d9b3/activesupport/lib/active_support/cache/mem_cache_store.rb#L77&quot;&gt;MemCacheStore#new&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It will:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;creates Dalli client(s)
  * either a pool with the option &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pool: true&lt;/code&gt; or with specific pool configuration options
  * or a single client&lt;/li&gt;
  &lt;li&gt;It can handle a single cache host or a collection of hosts to be treated as a load-balanced cluster&lt;/li&gt;
  &lt;li&gt;It initializes most of the objects in memory&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It does not immediately create connections to Memcached; something in the code will need to call a cache method that requires an active connection, and it will lazily initialize the connection. Some folks do this purposefully to warm the connection, while others prefer a lazy connection. Either way, when you have a custom cache store, be aware of this and how it relates to forking web servers; if you lazily connect, that can happen after fork. If you eagerly connect, you likely need to ensure your cache store is fork-safe (memcache and redis are fork-safe).&lt;/p&gt;

&lt;p&gt;After this initialization is complete, you are ready to call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rails.cache.method&lt;/code&gt; wherever you need it in your code.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Gardyn 2.0 four years later</title>
   <link href="https://www.mayerdan.com/fun/2024/10/11/gardyn-review-four-seasons-later"/>
   <updated>2024-10-11T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/fun/2024/10/11/gardyn-review-four-seasons-later</id>
   <content type="html">
&lt;h1 id=&quot;gardyn-20-years-later&quot;&gt;Gardyn 2.0 years later&lt;/h1&gt;

&lt;p&gt;I posted my first &lt;a href=&quot;/fun/2022/11/10/gardyn-review&quot;&gt;review of the Gardyn 2.0&lt;/a&gt; a few years ago. I had had the Gardyn for 2 seasons at that point. Now it has been another couple of seasons, but more importantly, it has required some additional repairs.&lt;/p&gt;

&lt;p&gt;Since then, I have wanted to cover the Gardyn and some of my basement grow setup.&lt;/p&gt;

&lt;h1 id=&quot;gardyn-20-still-worth-it-years-later&quot;&gt;Gardyn 2.0 still worth it years later?&lt;/h1&gt;

&lt;p&gt;The simple answer is yes, I am happy with it. I occasionally get a subscription for a few months. Because of my blog post and my surprisingly popular &lt;a href=&quot;https://photos.app.goo.gl/MLEoP7HsQ8z98H886&quot;&gt;Google Photos garden photo gallery&lt;/a&gt;, I have even gotten a few months free from referrals. Thanks, folks.&lt;/p&gt;

&lt;p&gt;At this point, I have continued to maintain and use my Gardyn system. I do that seasonally, shutting it down in the summer to focus on my outdoor gardening. Also, I keep some of my bubbled buckets and my lettuce rack going all year. The Gardyn is a bit more maintenance because it grows 30 plants, and I am busy harvesting outside.&lt;/p&gt;

&lt;p&gt;The reasons I like the gardyn and still recommend it, it is silly easy and just will make you successful and get you into growing your own greens, it can motivate you to get into the hobby and ensure you really can’t mess it up and kill all your plants. You can always make it more challenging by seeding your own pods, growing things outside of their recommended plants, etc. It inspired me to get bubbler buckets and retrofit an old AeroGarden to just grow with the LED grow light I bought for the bubblers.&lt;/p&gt;

&lt;p&gt;The Gardyn is the perfect entry into this hobby, with a nice setup and beyond the small AeroGardens.&lt;/p&gt;

&lt;h1 id=&quot;gardyn-maintenance-and-repairs&quot;&gt;Gardyn Maintenance and Repairs&lt;/h1&gt;

&lt;p&gt;I don’t do all the required deep cleanings and water dumps that Gardyn recommends… I try to clean out the water tank and such occasionally, and I do one deep cleaning a year where I disassemble and clean and reassemble the entire Gardyn. That is part of the reason I don’t run it in the summer, so I can take it apart and clean it. I do recommend Gardyn hydroboost or a similar hydroponic additive to reduce cleanings.&lt;/p&gt;

&lt;p&gt;I have twice had most of my plants die because the pump died… The first time, it took me a long time to sort things out, request the official part, repair, and get back up and running. It was not a fun repair, and I lost a lot of plants… The second time which was less than a year after the first repair the pump gave out in late spring, so I just shut the Gardyn down for the summar a bit early, and I had already cleared many of my plants. So it was no big deal, but I wanted to be able to more easily fix it going forward… So I bought a small external water pump and timer, which was cheaper than the official pump and looked more robust… I drilled holes and ran the cables (grey in the image). This works great as I am starting off my new season. Replacing this pump will now only cost about $20m, and if the Gardyn LEDs go out, I might replace them with more external LED grow lights, while I keep using the tub towers and base for growing.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/gardyn_repairs.webp&quot; alt=&quot;How I Gardyn&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I have read that the newer Gardyns are easier to clean and disassemble, and have made the pump less likely to have water get in an corroding the connections, which is the problem I have had.&lt;/p&gt;

&lt;p&gt;Given my Gardyn is over four years old, while this is a bit too many repairs for one to be entirely happy about it, I am pretty happy as it eased me into all this and now I am comfortable and confident to go much further in a DIY fashion because I have learned so much with the initial hand holding of the Gardyn. Also, anything with electronics, water, lights, and various nutrients is a rough environment and repairs are to be expected at some point.&lt;/p&gt;

&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h1&gt;

&lt;p&gt;I still need to find out where all the traffic and folks that find my Google Photos album show up from, but I wanted to update my original review with some of these details, and I will continue to add some new photos and updates to the photo album. As the 5th year winter basement garden project season kicks off, I am looking forward to lots of greens and peppers, while my daughter Sasha absolutely loves picking and eating the small cucumbers we grow in the basement.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Rails World 2024</title>
   <link href="https://www.mayerdan.com/software/2024/10/01/rails-world-2024"/>
   <updated>2024-10-01T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/software/2024/10/01/rails-world-2024</id>
   <content type="html">
&lt;h1 id=&quot;conferences-for-remote-developers&quot;&gt;Conferences for Remote Developers&lt;/h1&gt;

&lt;p&gt;I posted a quick note on &lt;a href=&quot;https://ruby.social/@danmayer/113215689993891363&quot;&gt;ruby.social&lt;/a&gt; that got some notice and a few folks pinged me.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;If you work remotely, going to a conference with a large number of other remote coworkers is a great way to build and refresh in-person relationships. I got to talk in person with 20+ coworkers who were previously only Slack icons and discuss technology and project decisions with the CEO in person. If you work remotely, it is worth finding these IRL touch points from time to time.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I wanted to add some more thoughts, but it gets long on a thread, so moving to a blog post.&lt;/p&gt;

&lt;h1 id=&quot;conferences-for-remote-developers-long-version&quot;&gt;Conferences for Remote Developers (long version)&lt;/h1&gt;

&lt;p&gt;It has been a few years since I visited many Ruby conferences. Attending the most recent RailsWorld was a reminder of some of their value.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;I work remotely, so it was a really great way to connect with co-workers, as my employer, Shopify, was the city sponsor in Toronto. This meant that hundreds of employees, both remote and local, attended pre-events, conferences, and outside-of-conference meetings. &lt;/li&gt;
  &lt;li&gt;It was a reminder that these are real people, many of whom I have seen only through GitHub icons, PRs, and issues for years.&lt;/li&gt;
  &lt;li&gt;It is fun to have a user of an open-source library I maintain find me and chat about how much they enjoy the project and how they are using it in unique ways.&lt;/li&gt;
  &lt;li&gt;It is fun to see some speakers’ growth over the years. One of the speakers at the conference gave a keynote talk; I also happened to be in attendance years ago when she gave her very first conference talk. Another easy giving their final talk after ten years of conference talks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Talking about the work others do and how we use it inspired us to write and discuss how to use these tools and help share which tools are valuable to our work. We should also try to share more of the work we do that isn’t public or shared and could be interesting to the community. Non-private things that are locked away for no particular reason are closed source code.&lt;/p&gt;

&lt;p&gt;I hope to make a few more posts about some of the tools we use in the coming months. I will also revisit my open source repos, respond to issues, and update them, ensuring they are all tested and work well with the latest Rails releases. Finally, I will clean up and get my current side project back online so I can share some of the fun things I play around with when learning.&lt;/p&gt;

&lt;p&gt;All of that is to say that the conference did exactly what it should have. It inspired me and made me happy to be a part of the Ruby community, and it was great to put faces and real people to the great work I have been enjoying and following along with online for years.&lt;/p&gt;

&lt;h1 id=&quot;rails-world-2024-summary&quot;&gt;Rails World 2024 Summary&lt;/h1&gt;

&lt;p&gt;As usual, DHH kicked things off with his keynote address, setting the theme for many of the more detailed follow-up talks.&lt;/p&gt;

&lt;p&gt;Keynote tl;dr; DHH wants folks to have a fast onramp but be proud to learn and dig into the details. Driving home, a new tag line from “Hello World” to “IPO” was repeated throughout many of the talks and is clearly a theme.&lt;/p&gt;

&lt;h2 id=&quot;what-is-rails-going-for&quot;&gt;What is Rails going for?&lt;/h2&gt;

&lt;p&gt;Many great Rails companies have been around and are still growing,  but Rails lost some of the newcomer momentum and is trying to get that back. As Rails became more complex and built more complexity into the base system, it became much harder to set up a brand new app, deploy it, and reliably use all of the features out of the box. Generally, we need a managed DB and Redis out of the box, along with multiple processes for job workers and grinding small single deployments (cheap Heroku dynos) to a halt.&lt;/p&gt;

&lt;p&gt;Rails is investing in ensuring out of the box, all the initial solutions can start with extremely simple to-manage defaults that can deploy to single boxes without hassle, and using the same code can scale to much larger databases or purpose-built technologies like Memcache and Redis. Many of the talks filled in the details.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://rubyonrails.org/world/2024/day-1/rosa-gutierrez&quot;&gt;Solid Queue&lt;/a&gt; revisits Action Bobs, building out and active job adapter using a data base backed queue that can ship at a starting point with an sqlite DB.&lt;/li&gt;
  &lt;li&gt;This was a great talk and also gets into some very cool new DB  features like skip lock where modern DBs can unlock extremely performant queuing solutions&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/rails/solid_cache&quot;&gt;Solid Cache&lt;/a&gt;, another option for Rails.cache and again brand new simple apps can start with sqlite, early scaling can share the production DB, and this scales all the way up to very large distributed shared caches using collections of multiple DBs.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/rails/solid_cable&quot;&gt;Solid Cable&lt;/a&gt;, action cable on the DB starting with sqlite for new apps.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So what does this mean after Rails new? You can start out with 4 DBs that are each single SQLlite files. As you grow in usage you can start to migrate each of those to the best fit for your needs. Also, please don’t underestimate how much it can scale on modern multi-core and SSD hardware.&lt;/p&gt;

&lt;h2 id=&quot;rails-world-2024-theme&quot;&gt;Rails World 2024 Theme&lt;/h2&gt;

&lt;p&gt;The theme was clear and then sprinkled with talks on how this useful Rails as a one-person framework can make it easy for a single dev or small team to get a lot of leverage. Speaker &lt;a href=&quot;https://rubyonrails.org/world/2024/day-1/justin-searls&quot;&gt;justin-searls, giving his final Rails talk&lt;/a&gt;, showed how to build low code but very modern feeling apps with low maintenance costs. Diving into optimizing performance, profiling, and how to write fast Ruby with less allocations.&lt;/p&gt;

&lt;p&gt;Not all talks were only about the technology as &lt;a href=&quot;https://rubyonrails.org/world/2024/day-2/opening-keynote-eileen&quot;&gt;Eileen Uchitelle&lt;/a&gt; reminds us how many of the largest and oldest Rails apps have struggled either with microservices or componentizing a modular monolith. Pointing out, rightly in my opinion, that neither solves what is a people problem, not a tech problem. The problem comes from prioritizing features and new code over good maintenance, refactoring, and constantly driving to improve code quality. Also, we often have folks not trained in rails writing a significant amount of code on a team without others familiar with rails, and companies don’t incentivize training and maintenance, often giving promotions to new high-visibility projects vs reducing toil and simplicity.&lt;/p&gt;

&lt;p&gt;While the talks were good, the best part for me was seeing and chatting with many developers I interact with online and contributing to projects I use, contribute to, and learn from. It was great to meet @jhawthorn and tell them about how I started using their &lt;a href=&quot;https://github.com/jhawthorn/vernier&quot;&gt;new profiler vernier&lt;/a&gt; (please check it out and see how it can finally untangle performance issues across threads), asking the author of Solid cache about how it may work with a particular architecture setup, or even discussing with colleges if we can perhaps open source some libraries that might help push the community ahead a bit further as folks are facing similar challenges as we see internally.&lt;/p&gt;

&lt;p&gt;It was great meeting old friends and talking to some new friends at Rails World. Thanks to everyone who helped make it a nice event.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Increasing Collaboration Confidence with bin/setup</title>
   <link href="https://www.mayerdan.com/software/2023/06/12/binsetup"/>
   <updated>2023-06-12T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/software/2023/06/12/binsetup</id>
   <content type="html">
&lt;h1 id=&quot;a-repo-quick-start&quot;&gt;A Repo Quick Start&lt;/h1&gt;

&lt;p&gt;As many organizations with many microservices and employee churn, you can and should expect to often step into a new codebase that needs some smaller updates. It can be really helpful to be able to very quickly go from cloning a repo to being able to run tests and check things in a developer environment. The goal of a number of different helper scripts is to help reduce the total maintenance cost of repositories over their lifetime and accelerate anyone onboarding into the repository.&lt;/p&gt;

&lt;h1 id=&quot;enter-binsetup&quot;&gt;enter bin/setup&lt;/h1&gt;

&lt;p&gt;The goal of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bin/setup&lt;/code&gt; is to go from git clone to being able to run the test suite and boot the app in development mode. The workflow should allow someone to be running any repository very quickly.&lt;/p&gt;

&lt;div class=&quot;language-shell 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;o&quot;&gt;&amp;gt;&lt;/span&gt; git clone someproject.git
&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;someproject
&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; bin/setup
&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; Bootstrapping someproject
&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; ...
&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;# checks various depenencies and installs what is missing&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;# ... redis, postgres, gems, etc...&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;# checks if DBs exist if not creates them and sets them up&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;# ... db create, db migrate, db seed, etc...&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; ...
&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; All &lt;span class=&quot;nb&quot;&gt;complete&lt;/span&gt;, now you can run &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;bin/start&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;a webserver or &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;bin/test&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt; to run the tests
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The script might have a few assumptions depending on where you work. We have some that if on a Mac will require Homebrew, we have some that might bail if you don’t have Docker. The required dependencies are very small and the script can point you at the documentation to get the minimum requirements up and running.&lt;/p&gt;

&lt;h1 id=&quot;language-agnostic-and-composable-scripts&quot;&gt;Language Agnostic and Composable Scripts&lt;/h1&gt;

&lt;p&gt;While I think &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bin/setup&lt;/code&gt; is the most valuable. You can see I already referenced a few other scripts. Some of the scripts are just one-line aliases, but it still keeps the concept language agnostic. I can get up and running on a Go, Ruby, and Node app on the same day to upgrade a feature touching on the frontend and a number of internal services. In Ruby, for example, the scripts can be very simple.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;example &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bin/test&lt;/code&gt; for a Ruby repository:&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-shell 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;c&quot;&gt;#!/bin/sh&lt;/span&gt;
bundle &lt;span class=&quot;nb&quot;&gt;exec &lt;/span&gt;rspec 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;example &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bin/start&lt;/code&gt; for a Ruby repository:&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-shell 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;c&quot;&gt;#!/bin/sh&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;PORT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;PORT&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;:-&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;3000&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;RACK_ENV&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;development &lt;span class=&quot;nv&quot;&gt;RAILS_ENV&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;development foreman start &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; Procfile.dev &lt;span class=&quot;nt&quot;&gt;--color&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bin/setup&lt;/code&gt; script is by far the most complicated, but normally looks something like below:&lt;/strong&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-Ruby&quot;&gt;#!/usr/bin/env ruby

require &quot;fileutils&quot;

APP_ROOT = File.expand_path(&quot;..&quot;, __dir__)

def system!(*args)
  system(*args) || abort(&quot;Command failed: #{args}&quot;)
end

def executable?(command)
  ENV[&quot;PATH&quot;].split(File::PATH_SEPARATOR).map do |path|
    (ENV[&quot;PATHEXT&quot;] ? ENV[&quot;PATHEXT&quot;].split(&quot;;&quot;) : [&quot;&quot;]).map do |extension|
      File.executable?(File.join(path, &quot;#{command}#{extension}&quot;))
    end
  end.flatten.any?
end

# WARNING: not portable (macOS and Linux only)
def running?(process_name)
  system(&quot;{ ps -ef; docker ps 2&amp;gt;/dev/null; } | grep #{process_name} | grep -v grep &amp;gt; /dev/null 2&amp;gt;&amp;amp;1&quot;)
end

def database_exists?(database_name)
  puts %(running database_exists? check for #{database_name})
  system(%(psql -lqt | cut -d \| -f 1 | grep -qw #{database_name}))
end

def create_database!
  system!(&quot;bundle exec rails db:create&quot;)
end

def check_dependencies
  # Check all dependencies first, and guide the user to the right place
  # to learn more if any are missing.
  #
  # you can add anything you might need here like redis, rabbit, kafka, etc
  # you can either warn or try to detect and install
  unless executable?(&quot;psql&quot;)
    abort &amp;lt;&amp;lt;~WARNING
      ############################ WARNING ############################
      PostgreSQL is required for using this setup, for tests, the
      server, or the console! For more information, see
      https://internal-wiki.com/psql
    WARNING
  end

  unless running?(&quot;postgres&quot;)
    abort &amp;lt;&amp;lt;~WARNING
      ############################ WARNING ############################
      PostgreSQL must be running for using this setup, for tests, the
      server, or the console! For more information, see
      https://internal-wiki.com/postgres
    WARNING
  end

  unless require &quot;bundler&quot;
    abort &amp;lt;&amp;lt;~WARNING
      ############################ WARNING ############################
      Bundler is required for using this setup, for tests, the server,
      or the console! For more information, see
      See https://internal-wiki.com/bundler
    WARNING
  end
end

def run_setup
  # This section will go through the basic steps to setup the app to the point specs or server start would work
  puts &quot;Checking dependencies using Bundler&quot;
  system(&quot;bundle check&quot;) || system!(&quot;bundle install&quot;)

  puts &quot;Copying example files into place if needed&quot;
  FileUtils.cp &quot;config/database.yml.example&quot;, &quot;config/database.yml&quot; unless File.exist?(&quot;config/database.yml&quot;)

  puts &quot;Creating tmp folder if needed&quot;
  FileUtils.mkdir &quot;tmp&quot; unless File.exist?(&quot;tmp&quot;)

  create_database!(&quot;app_name_development&quot;) if !database_exists?(&quot;app_name_development&quot;)
  if database_exists?(&quot;app_name_development&quot;)
    puts &quot;Migrating development database&quot;
    system!({&quot;RAILS_ENV&quot; =&amp;gt; &quot;development&quot;}, &quot;bin/rails db:migrate&quot;)
        puts &quot;seeding development database&quot;
    system!({&quot;RAILS_ENV&quot; =&amp;gt; &quot;development&quot;}, &quot;bin/rails db:seed&quot;)
  end

  create_database!(&quot;app_name_test&quot;) if !database_exists?(&quot;app_name_test&quot;)
  if database_exists?(&quot;app_name_test&quot;)
    puts &quot;Migrating test database&quot;
    system!({&quot;RAILS_ENV&quot; =&amp;gt; &quot;test&quot;}, &quot;RAILS_ENV=test bin/rails db:migrate&quot;)
  end

  puts &quot;Installing foreman&quot;
  system! &quot;gem install foreman&quot;
end

FileUtils.chdir APP_ROOT do
  check_dependencies
  run_setup

  puts &amp;lt;&amp;lt;~SUCCESS
    The app is setup
    run tests try:
        bin/test
    To run as a server, try:
        bin/start
  SUCCESS
end
&lt;/code&gt;&lt;/pre&gt;

&lt;h1 id=&quot;verify-the-scripts-on-ci&quot;&gt;Verify The Scripts on CI&lt;/h1&gt;

&lt;p&gt;If these scripts aren’t run often and kept up to date, they are bound to break and go un-noticed. It is helpful to verify the scripts on CI. It also generally had the dual purpose of helping keep the script working on both Docker and Macs. Let’s take a look at the things we can run on CI to help verify our app can be setup and run for folks immediately.&lt;/p&gt;

&lt;p&gt;While the examples I am giving are for CircleCI, they should easily integrate into any continuous integration toolchain.&lt;/p&gt;

&lt;h2 id=&quot;verifying-binsetup--binstart-on-ci&quot;&gt;Verifying &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bin/setup&lt;/code&gt; &amp;amp; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bin/start&lt;/code&gt; on CI&lt;/h2&gt;

&lt;pre&gt;&lt;code class=&quot;language-YAML&quot;&gt;  check_bin_setup:
    &amp;lt;&amp;lt;: *base-job
    steps:
      - checkout
      - restore_cache:
          key: *gem_cache_key
      - run:
          name: Run setup
          command: bin/setup
      - run:
          name: Run setup again (idempotency check)
          command: bin/setup
      - run:
          name: Boot dev server in the background
          command: bin/start
          background: true
      - run:
          name: Start the rails server, wait for it to be available, then make a request and verify the response.
          command: |
            dockerize -wait tcp://localhost:3000/ -timeout 1m
            STATUS_CODE=`curl -s -o /dev/null -w &quot;%{http_code}&quot; -H &quot;Accept: application/json;&quot; -H &quot;Content-Type: application/json&quot;&apos; http://localhost:3000/app_name/healthcheck`
            if [ $STATUS_CODE != &quot;200&quot; ]; then
                echo &quot;Server failed to return a 200&quot;
                exit 2
            fi
          environment:
            RAILS_ENV: development
            RACK_ENV: development
&lt;/code&gt;&lt;/pre&gt;

&lt;h1 id=&quot;bonus-points-verify-eager-load-on-ci&quot;&gt;Bonus Points Verify Eager Load on CI&lt;/h1&gt;

&lt;p&gt;Long as we are verifying things work as expected on CI, another thing that can help avoid an incident is verifying the app works correctly with eager loading. We add a Rake task like below and have our CI testing script call it.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-Ruby&quot;&gt;desc &quot;Loads the Rails Application with &apos;eager_loading=true&apos; as our production deployment eagerloads and we don&apos;t want to find out about a missing depedency that late.&quot;
task :eager_load_check do
  require File.expand_path(&apos;./config/environment&apos;, File.dirname(__FILE__))
  Rails.application.eager_load!
end
&lt;/code&gt;&lt;/pre&gt;

&lt;h1 id=&quot;enable-any-developer-to-quickly-jump-into-any-repo&quot;&gt;Enable Any Developer to Quickly Jump Into Any Repo&lt;/h1&gt;

&lt;p&gt;Suppose your teams make it easy to jump into any repository. In that case, you will help reduce siloing and encourage understanding not only the systems that teams own but working in and helping contribute to the systems in the ecosystem they participate in. It will promote collaboration and exploration and reduce the friction when folks move between teams and projects. There are other steps teams can take to reduce the friction of getting started, but maintaining these few scripts is a low-cost and high-value return for teams managing many repositories.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Making Book Covers with Img2Img</title>
   <link href="https://www.mayerdan.com/software/2023/02/27/making-book-covers-with-img2img"/>
   <updated>2023-02-27T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/software/2023/02/27/making-book-covers-with-img2img</id>
   <content type="html">
&lt;p&gt;I have been having fun making &lt;a href=&quot;https://www.mayerdan.com/fun/2022/12/20/ai-book-building&quot;&gt;children’s books with my kids using AI&lt;/a&gt;. I have been working on some techniques to make them even more personalized. I have recently been building some of the images for the books using image-to-image. I will describe a bit of the process and some of the options I use.&lt;/p&gt;

&lt;h1 id=&quot;examples&quot;&gt;Examples&lt;/h1&gt;

&lt;p&gt;Below are some examples of the kinds of image options I get when working from photos to make images for the books.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.amazon.com/dp/B0BW3G1491&quot;&gt;&lt;img src=&quot;/assets/img/aDocSashaCollage.jpeg&quot; alt=&quot;A young girl as a puppy doctor&quot; /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.amazon.com/dp/B0BV3732Y8&quot;&gt;&lt;img src=&quot;/assets/img/aboywizard-collage.png&quot; alt=&quot;A young boy wizard&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1 id=&quot;book-cover-process&quot;&gt;Book Cover Process&lt;/h1&gt;

&lt;p&gt;If you want to make a similar stylized book cover or image, my basic outline is below.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Get a good description of the image you want. In my example, “A young boy pointing a magic wand and casting a spell.”&lt;/li&gt;
  &lt;li&gt;Capture a photo that will fit the theme
  * This is a fun part with my kiddos. We might dress up with Halloween costumes, setup some scenery or backgrounds
  * the closer the original image is to what you are looking for the better you will be able to bring your kid into the book&lt;/li&gt;
  &lt;li&gt;either use a local stable diffusion img2img script or an online free tool
  * I have been using Ruby scripts to run stable diffusion batch jobs locally
  * You can also often find a &lt;a href=&quot;https://huggingface.co/spaces/DucHaiten/webui&quot;&gt;free hugging face stable diffusion web-ui model and use the img2img tab&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Generate several images adjusting the “Denoising strength” to be more or less like your original image&lt;/li&gt;
  &lt;li&gt;Photoshop or edit to adjust any other details&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/awizardhuggingface.png&quot; alt=&quot;Example using huggingface web-ui&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;example of generating options while using the huggingface free web ui models&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;what-does-adjusting-denoising-strength-do&quot;&gt;What does adjusting Denoising strength do?&lt;/h1&gt;

&lt;p&gt;Stable Diffusion Denoising Strength is documented as:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Determines how little respect the algorithm should have for image’s content. At 0, nothing will change, and at 1 you’ll get an unrelated image. With values below 1.0, processing will take less steps than the Sampling Steps slider specifies.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As it states, the closer to zero the more like the original image it will be, by the time it is 1, you are just getting images based on the text prompt you provide. The input image will be ignored. I personally find that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0.4 - 0.6&lt;/code&gt; is the best range for my use cases.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/wizard_progress.png&quot; alt=&quot;The impact of a wizard image when increasing the enoising Strength&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;the original image with a few examples of images generated while adjusting the denoising strength&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;whats-next&quot;&gt;What’s next?&lt;/h1&gt;

&lt;p&gt;I am looking at ways that I can improve the continuity of the style and look of the characters across pages. If I could seed a book with 3-4 photos of my kids possibly in custom for the theme, could I have all pages make not just a little boy who is an astronaut, but make them all look like the same little boy, etc?&lt;/p&gt;

&lt;p&gt;If you want to see any of the completed books, the links below are to the amazon pages for the books. As I am still new to this, the later books, I believe to be better than the first few. I have a couple more that are nearly ready to be published.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.amazon.com/dp/B0BPG9GNFL&quot;&gt;Theo &amp;amp; The Three Squirrels&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.amazon.com/dp/B0BHSZF1QG&quot;&gt;Sasha &amp;amp; The Three Little Pigs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.amazon.com/dp/B0BV3732Y8&quot;&gt;Theo the Great: The World At Sleep&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.amazon.com/dp/B0BW3G1491&quot;&gt;Sasha The Puppy Doctor&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>Github Organization Repositories by Ruby Version</title>
   <link href="https://www.mayerdan.com/software/2022/12/28/github-org-repos-by-ruby-version"/>
   <updated>2022-12-28T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/software/2022/12/28/github-org-repos-by-ruby-version</id>
   <content type="html">
&lt;p&gt;Every year as Ruby releases a new version at Christmas, and many folks adjust their supported versions of Ruby. Where I work adjusting the supported versions of Ruby, deprecating older versions.
This means folks have a window of time to find and update the offending older gems and microservices that would no longer be supported. This primarily relies on teams that have ownership of apps following the guidance and eventually folks nudging teams before we deprecate the Docker container that enables deployment of those versions.&lt;/p&gt;

&lt;p&gt;I thought it would be nice to pull this out as a burndown list or a chart that can show all the repositories and group them by the Ruby version. This isn’t something I could figure out how to easily do via GitHub code search, but with a bit of work, I could get the data I needed via Github’s GraphQL API. Let’s take a look at how to do that and how we can visualize it.&lt;/p&gt;

&lt;h1 id=&quot;displaying-repositories-grouped-by-ruby-version&quot;&gt;Displaying Repositories Grouped By Ruby Version&lt;/h1&gt;

&lt;p&gt;This is what is looks like when you have a valid &lt;a href=&quot;https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token&quot;&gt;github personal access token (PAT)&lt;/a&gt; added to the notebook.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://observablehq.com/d/c046053c399b40b6&quot;&gt;&lt;img src=&quot;/assets/img/repos_by_version.png&quot; alt=&quot;Repositories Grouped by Ruby Version&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also filter in to find only repos matching a specific Ruby version.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/ruby_version_selection.png&quot; alt=&quot;Select Input Showing Count Repos by Ruby version&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;querying-organization-repositories-including-ruby-version&quot;&gt;Querying Organization Repositories including Ruby Version&lt;/h1&gt;

&lt;p&gt;This does rely on the fact that our company generally requires a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.ruby-version&lt;/code&gt;, but the same approach could be adjusted to pull the version out of a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dockerfile&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CI file&lt;/code&gt;, or deployment scripts.&lt;/p&gt;

&lt;p&gt;The basic Github GraphQL Query is below, note the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;object(expression: &quot;main:.ruby-version&quot;)&lt;/code&gt; section:&lt;/p&gt;

&lt;div class=&quot;language-javascript 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;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;get_repos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;lang&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;after&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;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;lang_filter_line&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; &lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;lang&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;dl&quot;&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;nx&quot;&gt;lang_filter_line&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;` language:&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;lang&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&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;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;query_line&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;`query: &quot;org:&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;githubOrganization&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;lang_filter_line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; archived:false sort:updated-desc&quot;, type: REPOSITORY, first: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;after&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&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;nx&quot;&gt;query_line&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;` after: &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;after&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&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;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;github4&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`query {
  search(&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;query_line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;) {
    repositoryCount
    pageInfo {
        endCursor
        hasNextPage
    }
    edges {
      node {
        ... on Repository {
          name
          nameWithOwner
          collaborators(first: 20) {
            nodes {
              name
            }
          }
          repositoryTopics(first: 10) {
            nodes {
              topic {
                name
              }
            }
          }
          stargazers {
            totalCount
          }
          object(expression: &quot;main:.ruby-version&quot;) {
            ... on Blob {
              text
            }
          }
        }
      }
    }
  }
}`&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;p&gt;There are several functions to pull things like the organization’s teams, tags, etc. The basic pattern can be extended to find gems, deprecated projects, projects without a team, projects with invalid codeowners files, or more. Once you have data for a file like the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.ruby-versions&lt;/code&gt; file, you can process it to make it friendly for display, this is the snippet I use to process each repository.&lt;/p&gt;

&lt;div class=&quot;language-javascript 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;nx&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ruby_version&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;trim&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()?.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;ruby-&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;unknown&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&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;h1 id=&quot;visualizing--sharing-the-tool&quot;&gt;Visualizing &amp;amp; Sharing the Tool&lt;/h1&gt;

&lt;p&gt;I have been using &lt;a href=&quot;https://observablehq.com/&quot;&gt;Observable&lt;/a&gt; for visualizing and sharing things like that recently. I have a number of internal company notebooks, and shared out &lt;a href=&quot;/ruby_trends&quot;&gt;A Observable notebook displaying Trends&lt;/a&gt; on &lt;a href=&quot;https://ruby.social&quot;&gt;Ruby.Social&lt;/a&gt; a few days ago.&lt;/p&gt;

&lt;p&gt;You can &lt;a href=&quot;https://observablehq.com/d/c046053c399b40b6&quot;&gt;clone the notebook from here yourself&lt;/a&gt;, to make a private version for your organization.&lt;/p&gt;

&lt;iframe width=&quot;120%&quot; height=&quot;1500&quot; frameborder=&quot;0&quot; src=&quot;https://observablehq.com/embed/c046053c399b40b6@726?cell=*&quot;&gt;&lt;/iframe&gt;
</content>
 </entry>
 
 <entry>
   <title>Building Books with AI</title>
   <link href="https://www.mayerdan.com/fun/2022/12/20/ai-book-building"/>
   <updated>2022-12-20T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/fun/2022/12/20/ai-book-building</id>
   <content type="html">
&lt;h3 id=&quot;a-brief-note-on-controversial-ai-images&quot;&gt;A Brief Note on Controversial AI Images&lt;/h3&gt;

&lt;p&gt;I know there is some debate about AI Art and copyright. I am not going to get into that a lot. The current models don’t offer enough protections for Artists. Regardless of where we are with the models we have right now. I believe AI art which is considered fully responsible and reasonable use, will be reached. The full details and depth of that issue are outside the scope of this post.&lt;/p&gt;

&lt;h1 id=&quot;creating-childrens-books-with-ai&quot;&gt;Creating Childrens Books with AI&lt;/h1&gt;

&lt;p&gt;I was playing around with some of the newer AI tools. Originally, using &lt;a href=&quot;https://openai.com/blog/gpt-3-apps/&quot;&gt;GPT3&lt;/a&gt; to write a book with my son. Then I started using &lt;a href=&quot;https://openai.com/dall-e-2/&quot;&gt;DALL·E 2&lt;/a&gt; and Stable Diffusion to generate images to illustrate the book. As the holidays approached, I wrote another for the kids cousins. This year grandparents, cousins, and some aunts and uncles will receive printed copies of books my kids helps write and illustrate with the help of AI. It has been a fun a rewarding project, and is easy to do. It is wild that you can work with a 3 year old to build a book they will feel invested in and proud of.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/sasha_pig_cover.png&quot; alt=&quot;Sasha and the three pigs&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;outline-of-the-steps&quot;&gt;Outline of the Steps&lt;/h1&gt;

&lt;ol&gt;
  &lt;li&gt;Find a topic the kid is interested in. I generally took a favorite classic fairy tale story&lt;/li&gt;
  &lt;li&gt;Generate some of the pages with GPT3, allowing the child to accept or reject pages or add in details of interest
    &lt;ul&gt;
      &lt;li&gt;for example, My son wanted to become a squirrel and feature squirrels in the book&lt;/li&gt;
      &lt;li&gt;My daughter wanted to be dressed as a puppy and have pigs in the book&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Once you have a good story, move on to illustrations. I created one image per page.
    &lt;ul&gt;
      &lt;li&gt;I often tried the exact text of the page to make an image but found it needed adjusting&lt;/li&gt;
      &lt;li&gt;I most often used &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;my prompt, digital art&lt;/code&gt; to get art-like images as opposed to anything looking like photos, 3d, or the like&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Build out the format in a Google Doc or word doc, print it out, and read it to the kids, taking edit notes&lt;/li&gt;
  &lt;li&gt;Finalize the book
    &lt;ul&gt;
      &lt;li&gt;make final edits&lt;/li&gt;
      &lt;li&gt;decide on the final formats, which likely have requirements&lt;/li&gt;
    &lt;/ul&gt;
    &lt;ul&gt;
      &lt;li&gt;I used Amazon Kindle Direct Publishing: Self Publishing&lt;/li&gt;
      &lt;li&gt;This requires specific margins, 24 pages of content, making a cover, etc&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/a_theo2_squ.png&quot; alt=&quot;My Son Theo as a squirrel&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;unique-personalized-gifts&quot;&gt;Unique Personalized Gifts&lt;/h1&gt;

&lt;p&gt;It is fun to make these with the kids or generate a bunch of images myself when I have the time to review them with the kids later. Writing short stories and inserting friends, family, inside jokes, and funny family context is amusing, and the result is unique. After making a few more of these, I am sure I will have the process and flow down well enough to make a genuinely personalized book for friends in a bit of time. Publishing several particular children’s books isn’t something I really could have imagined doing before experimenting with these tools. Now I could see all sorts of interesting mixed media creative experiments I could build with my kids.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/save-the-world-cover.png&quot; alt=&quot;The Cousins save the world&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;whats-next&quot;&gt;What’s Next?&lt;/h1&gt;

&lt;p&gt;I am looking at moving to really put us into the stories and have started to work with &lt;a href=&quot;https://huggingface.co/spaces/fffiloni/stable-diffusion-img2img&quot;&gt;Stable Diffusions Img2Img&lt;/a&gt; to generate images that the kids could act out or help design. This way the kiddos can get even more involved with some of the creative aspects and make the book even more special and unique to themselves.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/img2img-family.jpg&quot; alt=&quot;a img2img photo of our family&quot; /&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Thoughts on Software Complexity</title>
   <link href="https://www.mayerdan.com/software/2022/11/23/thoughts-on-complexity"/>
   <updated>2022-11-23T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/software/2022/11/23/thoughts-on-complexity</id>
   <content type="html">
&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; this was originally a &lt;a href=&quot;https://ruby.social/@danmayer/109385071308169183&quot;&gt;mastodon thread&lt;/a&gt;, but feels like small linked blog posts are a better format&lt;/p&gt;

&lt;p&gt;As there seems to be a tech crunch going on, several companies are responding in different ways, including layoffs which increase the maintenance cost per developer… This naturally leads to companies looking at re-orgs, shutting down services, and current maintenance costs.&lt;/p&gt;

&lt;p&gt;The constant reshuffling of engineers, I find, is universally disliked, and companies seem to be pretty bad at it, although there seem to be some growing trends to try to do it better.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Current popular re-org strategy around &lt;a href=&quot;https://teamtopologies.com/&quot;&gt;team topologies&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;A post where the &lt;a href=&quot;https://medium.com/bbc-product-technology/refactor-organisation-80e4e171d922&quot;&gt;BBC covers how it applied team topologies&lt;/a&gt; and how it went.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pair the above re-org reading with a good rebuttal with &lt;a href=&quot;https://medium.com/nick-tune-tech-strategy-blog/the-structure-and-process-fallacy-40640e844230&quot;&gt;the structure and process fallacy&lt;/a&gt;. Which points out that without fixing underlying problems a re-org isn’t likely to be much help.&lt;/p&gt;

&lt;p&gt;Finally, take a look at the &lt;a href=&quot;https://nolanlawson.com/2022/06/09/the-collapse-of-complex-software/&quot;&gt;collapse of complex software&lt;/a&gt;… Even if you spend time fixing ways of working, org structure, and processes as the above articles suggest if the economic times are forcing a shrinking team to maintain a large and complex system that already has out of control dependencies… The post points to two end states: collapse and rebuilds or build early with sustainable, tried-and-true techniques.&lt;/p&gt;

&lt;p&gt;Some choice quotes from the collapse of complex software:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;more hierarchies, more bureaucracies, deeper intertwinings of social structures. Early on, this makes sense: each new level of complexity brings rewards, in terms of increased economic output, tax revenue, etc. But at a certain point, the law of diminishing returns sets in, and each new level of complexity brings fewer and fewer net benefits, dwindling down to zero and beyond.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I have definitily seen where additional layers start to bring negative value.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;complexity has worked so well for so long, societies are unable to adapt. Even when each new layer of complexity starts to bring zero or even negative returns on investment, people continue trying to do what worked in the past.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I am a architect but often feel powerless to stop the rapidly increasing complexity&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Architects are brought in to “fix” the system. They might wheel out a big whiteboard showing a lot of boxes and arrows pointing at other boxes, and inevitably, their solution is… to add more boxes and arrows. Nobody can subtract from the system; everyone just adds.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This seems to be what Musk is trying to do cutting microservices left and right at twitter (I disagree with how he went about it in a very de-humanizing way).&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;software companies can keep hiring new headcount to manage their existing software (i.e. more engineers to understand more boxes and arrows), but if their labor force is forced to contract, then that same system may become unmaintainable. A rapid and permanent reduction in complexity may be the only long-term solution.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This also seems fairly real from my industry experience.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;I think whether software follows the boom-and-bust model, or a more sustainable model, will depend on the economic pressures of the organization that is producing the software. A software company that values growth at all cost, like the Romans eagerly gobbling up more and more of Gaul, will likely fall into the “add-complexity-and-collapse” cycle.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;how-to-reduce-complexity&quot;&gt;How to reduce complexity?&lt;/h1&gt;

&lt;p&gt;That is likely to big of a topic for this post, but I can through in a bonus link for where to start… Take more careful consideration of your dependencies. If you want to help organization reduce maintenance costs over time, &lt;a href=&quot;https://www.tomrenner.com/blog/2022-06-09/cull-your-dependencies&quot;&gt;cull your dependencies&lt;/a&gt;. Which I generally agree with. Also, make sure you consider your own internal libraries and services as dependencies.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Gardyn 2.0 Review</title>
   <link href="https://www.mayerdan.com/fun/2022/11/10/gardyn-review"/>
   <updated>2022-11-10T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/fun/2022/11/10/gardyn-review</id>
   <content type="html">
&lt;h1 id=&quot;gardyn-review&quot;&gt;Gardyn Review&lt;/h1&gt;

&lt;p&gt;Overall I am happy with the Gardyn indoor hydroponic growing system. I am partially through season two of growing with my Gardyn 2.0 unit and wanted to write up a more detailed review. I feel like the Gardyn has struck a pretty good balance on a DIY vs all-included system. I wish it were a bit more DIY, and I shy away from the yearly subscriptions, which I will explain later. The system grows a good amount of plants, works well year-round, is relatively clean, and, honestly, is very pleasing to look at. I almost wish I had a better space to keep it more in view (My setup is in the back of the unfinished basement). I researched several options and I am happy with where I landed. Suppose you want a more DIY experience and control to expand and modify the system over time. In that case, this isn’t the right setup for you, but if you want a super easy-to-maintain garden supplying you with fresh herbs, lettuces, and small veggies, you won’t be disappointed.&lt;/p&gt;

&lt;p&gt;I am sharing out &lt;a href=&quot;https://mygardyn.com/shop-gardyn-home-kits/?coupon-code=rfdaniel42491&quot;&gt;my gardyn affiliate link&lt;/a&gt; and if anyone wants to buy one, feel free to use it, or feel free to ignore it. It can save you a bit on a purchase, and I can get a few months of subscription for free.&lt;/p&gt;

&lt;h1 id=&quot;pros-and-cons&quot;&gt;Pros and Cons&lt;/h1&gt;

&lt;p&gt;At a glance, these are the points that stand out as I reflect on my experience using the Gardyn 2.0 over the last 18 months.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/gardyn_2_0.jpg&quot; alt=&quot;Gardyn with the kids&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Pro:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;easy to maintain&lt;/li&gt;
  &lt;li&gt;fresh greens all the time&lt;/li&gt;
  &lt;li&gt;holds up great with travel. We have left the gardyn for a week at a time, often and without issue.&lt;/li&gt;
  &lt;li&gt;I love having a new, one-month-old, and 2-month-old variety of my favorite plants&lt;/li&gt;
  &lt;li&gt;The cameras are kind of silly and fun to check in on plants while traveling&lt;/li&gt;
  &lt;li&gt;Access to replacement parts, extra food, plants, etc, all via the store&lt;/li&gt;
  &lt;li&gt;Replacement of failed ycubes is nice and easy&lt;/li&gt;
  &lt;li&gt;fun activity with the kids
    &lt;ul&gt;
      &lt;li&gt;they love to harvest and eat lettuce right as it grows&lt;/li&gt;
      &lt;li&gt;planting and helping with the baby plant nursery is always fun&lt;/li&gt;
      &lt;li&gt;they help with the watering by refilling the tank&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Easy to DIY hack:
    &lt;ul&gt;
      &lt;li&gt;I often make my own ycubes&lt;/li&gt;
      &lt;li&gt;I have extra grow lights near it to help some plants grow and to continually prep nursery&lt;/li&gt;
      &lt;li&gt;I surrounded with Highly Reflective Mylar Films, 82x 47Inch, Metallized Foil Covering Sheet, to increase light efficiency&lt;/li&gt;
      &lt;li&gt;one can add a watering line or extended tank&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Con:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;harder to clean and needs more frequent cleaning than I expected&lt;/li&gt;
  &lt;li&gt;upfront cost is still pretty high&lt;/li&gt;
  &lt;li&gt;The subscription model
    &lt;ul&gt;
      &lt;li&gt;it is always trying to push memberships&lt;/li&gt;
      &lt;li&gt;The membership isn’t beneficial (the AI reminders aren’t worth a monthly fee)&lt;/li&gt;
      &lt;li&gt;you definitely don’t need 10 ycubes a month once you have built up a good collection of plants&lt;/li&gt;
      &lt;li&gt;it is just far too expensive&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;gardyn-tips&quot;&gt;Gardyn Tips&lt;/h1&gt;

&lt;p&gt;Some tips based on how I use my gardyn.&lt;/p&gt;

&lt;h2 id=&quot;how-i-use-the-gardyn&quot;&gt;How I use the Gardyn&lt;/h2&gt;

&lt;p&gt;I use my gardyn for about 9 months a year. I have too much outdoor gardening going on in the summer months and don’t really need or want to deal with the gardyn for those peak summer months. I really enjoy doting on my basement garden in the winter. I enjoy learning new salads and recipes to help cook up and eat through the abundance of whatever I am growing any given month. I also time my monthly subscription having a few months at the start of each new growing season to help me kick off the next growing year with a fun collection of plants before going more fully DIY.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/how_i_gardyn.jpg&quot; alt=&quot;How I Gardyn&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;diy-pods-with-purchased-seeds&quot;&gt;DIY Pods with Purchased Seeds&lt;/h2&gt;

&lt;p&gt;I bought cheap seed packs, and standard grow cubes, and I recycle the plastic part of the ycubes. This way, I can make my own cubes avoiding the $5 per cube cost for non-members. I have had great success getting basically any lettuce and many herbs growing with this DIY method.&lt;/p&gt;

&lt;h2 id=&quot;plant-cycling&quot;&gt;Plant Cycling&lt;/h2&gt;

&lt;p&gt;I try to plant a few basils every few weeks when I first get the garden going… That way, I always have mature basil plants and some younger, more baby plants at any give time. I can grab a few leaves or if one is starting to bolt, I can cut the whole thing down and make pesto. This same technic works well for things that bolt, like cilantro as well.&lt;/p&gt;

&lt;h2 id=&quot;plants-and-seeds&quot;&gt;Plants and Seeds&lt;/h2&gt;

&lt;p&gt;I am happy with the selection of &lt;a href=&quot;https://mygardyn.com/plants/&quot;&gt;plants that Gardyn sells&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Liked
    &lt;ul&gt;
      &lt;li&gt;Celery (it doesn’t go big… but the greens are great in soups, stalks in stir fry, and to add crunch to a salad)&lt;/li&gt;
      &lt;li&gt;All the Basils, I think that I go 80% Genovese 20% Thai basil. This mix is what makes my pesto so good&lt;/li&gt;
      &lt;li&gt;All the lettuces, the Swiss Chard is exceptionally colorful and fun&lt;/li&gt;
      &lt;li&gt;All the Kales&lt;/li&gt;
      &lt;li&gt;Most Herbs, I particularly like Chives, Cilantro, Thyme, Oregano, Parsley, and Rosemary&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;OK
    &lt;ul&gt;
      &lt;li&gt;Cherry Tomatoes, it amuses the kids, and they eat them right off the vine, but they never get big or sweet&lt;/li&gt;
      &lt;li&gt;Peppers, they are small but are still lovely to have around to throw in a salad or recipe to add a few fresh veggies&lt;/li&gt;
      &lt;li&gt;Mint, it grows fine. It even flourishes, but I don’t know what to do with it.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Disliked
    &lt;ul&gt;
      &lt;li&gt;Snap Peas&lt;/li&gt;
      &lt;li&gt;Mini Strawberries also grow very large and basically produced very little… The kids did kind of like them though&lt;/li&gt;
      &lt;li&gt;Green Beans, any of the beans… They grow huge and drop leaves all over the floor… I got maybe 10 beans&lt;/li&gt;
      &lt;li&gt;Eggplants… Fine, I’m not too fond of eggplant, but my wife and kids do… It never produced anything one could eat)&lt;/li&gt;
      &lt;li&gt;Flowers, Can’t eat them, not interested ;)&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;what-does-and-doesnt-work&quot;&gt;What Does and Doesn’t Work?&lt;/h2&gt;

&lt;p&gt;The results for different folks will vary given the natural climate (sunlight, humidity, heat). I have had great luck with all the greens and done OK with a few smaller peppers. I have generally been disappointed or unimpressed with most of the vegetables. The pea pods/beans end up growing and shedding a lot of leaves and becoming hard to grow anything else near, but they barely produce any vegetables. The baby strawberries were cute but so small and infrequent that it was just more of a game for my kids to find and eat them and then complain they were too sour. The cherry tomatoes are similarly too small and sour, so my kids aren’t that into them. Other than all the lettuces that grow great, many of the herbs were a hit as well. I loved having thyme, cilantro, chives, and basil to add to any fresh recipes. Mint grows well, but I never really know what to do with it.&lt;/p&gt;

&lt;h1 id=&quot;a-timeline-or-usage&quot;&gt;A timeline or usage&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;setup with free month and initial first 30 cubes&lt;/li&gt;
  &lt;li&gt;ending membership with many pods, I find I can grow and harvest most far longer than they indicated&lt;/li&gt;
  &lt;li&gt;buying pods at non-member cost&lt;/li&gt;
  &lt;li&gt;cleanings&lt;/li&gt;
  &lt;li&gt;learning how to recycle and reuse pods&lt;/li&gt;
  &lt;li&gt;yearly shutdown during summer&lt;/li&gt;
  &lt;li&gt;yearly deep clean… It was really worth taking the whole thing apart and deep-cleaning EVERYTHING&lt;/li&gt;
  &lt;li&gt;restart monthly membership to get new cubes&lt;/li&gt;
  &lt;li&gt;restarting Gardyn nursery in early fall&lt;/li&gt;
  &lt;li&gt;as I wrap up my final outdoor harvests, I already have a bounty of greens in the basement to lift my spirits.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;final-thoughts&quot;&gt;Final Thoughts&lt;/h1&gt;

&lt;p&gt;I enjoy my odd basement gardening hobby and all the home cooking it inspires. A &lt;a href=&quot;https://photos.app.goo.gl/MLEoP7HsQ8z98H886&quot;&gt;large photo gallery of my gardyn in action&lt;/a&gt; hopefully gives a good idea of what you can expect if you decide you want some more plants in your life.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://photos.app.goo.gl/MLEoP7HsQ8z98H886&quot;&gt;&lt;img src=&quot;/assets/img/gardyn_gallery.jpg&quot; alt=&quot;Gardyn Gallery&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Ownership in an age of microservices</title>
   <link href="https://www.mayerdan.com/management/2022/08/27/ownership-in-an-age-of-microservices"/>
   <updated>2022-08-27T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/management/2022/08/27/ownership-in-an-age-of-microservices</id>
   <content type="html">
&lt;p&gt;This is the second post in a series about &lt;a href=&quot;/management/2022/08/22/hr-org-dev-org&quot;&gt;organization structures&lt;/a&gt;, teams and ownership, and application health.&lt;/p&gt;

&lt;h1 id=&quot;ownership-challenges-abound-in-microservices&quot;&gt;Ownership challenges abound in microservices&lt;/h1&gt;

&lt;p&gt;While there are many great resources out there that discuss microservices, where they may or may not be a &lt;a href=&quot;https://www.martinfowler.com/bliki/MicroservicePremium.html&quot;&gt;good fit&lt;/a&gt;, and how some companies have decided to &lt;a href=&quot;https://segment.com/blog/goodbye-microservices/&quot;&gt;move away from them, and back to a monolith&lt;/a&gt;, in this post I am going to focus on some of the unique challenges that microservices present with regard to &lt;strong&gt;ownership&lt;/strong&gt;. Ownership is frequently called out as a common problem with microservices, and both &lt;a href=&quot;https://www.uber.com/blog/microservice-architecture/&quot;&gt;Uber, working towards macro-services with a Domain-Oriented Microservice Architecture&lt;/a&gt;, and &lt;a href=&quot;https://medium.com/qe-unit/airbnbs-microservices-architecture-journey-to-quality-engineering-d5a490e6ba4f&quot;&gt;AirBnb’s journey towards macro-service Domain aggregation&lt;/a&gt; are aimed at improving the clarity of ownership of domains of data and the teams and services that support them.&lt;/p&gt;

&lt;h1 id=&quot;microservices-and-evolving-models-of-ownership&quot;&gt;Microservices and evolving models of ownership&lt;/h1&gt;

&lt;p&gt;There are many different models of ownership, and one of the reasons microservices took off was to avoid the “tragedy of the commons” problem that often occurs in large shared codebases. We break the systems apart in an attempt to avoid shared codebases that tend to degrade over time. As companies moved to many smaller systems, a number of different ownership models emerged:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Single person owner&lt;/li&gt;
  &lt;li&gt;Orphan codebase, due to attrition or team re-assignment&lt;/li&gt;
  &lt;li&gt;Modular Monolith, shared ownership, whether clear or unclear&lt;/li&gt;
  &lt;li&gt;Team Ownership (my preferred)&lt;/li&gt;
  &lt;li&gt;Workgroup/Guild/Steering Committee Ownership&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With these came some more modern code collaboration patterns, such as&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Internal tickets&lt;/li&gt;
  &lt;li&gt;Internal open-source (my preferred)&lt;/li&gt;
  &lt;li&gt;Trusted outsider&lt;/li&gt;
  &lt;li&gt;Tour of duty&lt;/li&gt;
  &lt;li&gt;Embedded expert&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are all explained very well in a post &lt;a href=&quot;https://blog.thepete.net/blog/2021/06/17/patterns-of-cross-team-collaboration/&quot;&gt;patterns of cross-team collaboration&lt;/a&gt;. I personally like team ownership, acting as stewards of the long term vision and domain of services, and influencing changes via an internal open-source model. The team, in this model, plays a similar role to that of the primary contributors in an open source project.&lt;/p&gt;

&lt;h1 id=&quot;strong-ownership-takes-time&quot;&gt;Strong ownership takes time&lt;/h1&gt;

&lt;p&gt;In a decomposed, microservices ecosystem I personally believe that having a team with a long history of knowledge-building and maintaining an app will lead to better outcomes. The institutional knowledge of operating the service feeds back into the development practices of the team. Over time, the team learns how to mitigate risk, anticipate needs, and more quickly and flexibly change and evolve the system. Strong team ownership also helps with communication, especially during incident response as everyone knows which team owns a given app, which adds confidence that the team also has deep knowledge of the system and will be able to support it. A stable relationship between business domain, services, and team brings clarity and shared knowledge of where responsibilities and vision for the domain belongs. In my last post, I will discuss how reorgs and diverging team names across systems causes &lt;a href=&quot;/management/2022/08/22/hr-org-dev-org&quot;&gt;organizational drift&lt;/a&gt; and confusion.&lt;/p&gt;

&lt;h1 id=&quot;who-is-responsible-for-the-distributed-monolith&quot;&gt;Who is responsible for the Distributed Monolith?&lt;/h1&gt;

&lt;p&gt;One issue that comes up in microservices is the emergence of a distributed monolith. As microservices have been around for a fairly long time at this point (in tech life cycles that is) there are places that have “legacy” microservice systems, perhaps better described as a distributed monolith. As a company first splits up a legacy monolith, they often make many learning mistakes, like setting up poor system boundaries which leads to distributed transactions, or taking shortcuts, like giving multiple applications read/write access to a single shared database (hello shared DB, my old friend). The early extracted set of services often have some “winners” that prove the value of a well-defined and executed extraction, as well as some less than ideal systems that become part of the distributed monolith, the remains of the legacy monolith that folks have failed to finish extracting. At some point, companies need to refactor, delete, and reduce the maintenance of microservices the same way companies end up building strategies to unravel or &lt;a href=&quot;https://shopify.engineering/shopify-monolith&quot;&gt;modernize legacy monoliths, via Modular Monolith&lt;/a&gt; and other techniques.&lt;/p&gt;

&lt;p&gt;The problem with poorly extracted and tightly coupled services is that they don’t have clear domain boundaries, which makes it difficult to build strong ownership and knowledge around. These services end up with the same tragedy of the commons issues of a large, shared code base.&lt;/p&gt;

&lt;p&gt;As part of any thinking about reorgs, considering these systems is key. The new structure should have clear domain boundaries, and take future ownership of  these systems into account. A steering group, architects, or your senior ICs can help think through high-friction boundaries and make recommendations. Often, I see companies try to reorg while only thinking about the new work, as opposed to taking into account these hard to maintain poorly understood systems. This creates a lot of technical drag on the team(s) trying to own them, as well as on any team that owns a feature that is yet to be extracted from these systems. If the reorg doesn’t take this into account, it might make the situation worse by trying to brush these problem areas under a rug.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://medium.com/qe-unit/airbnbs-microservices-architecture-journey-to-quality-engineering-d5a490e6ba4f&quot;&gt;&lt;img src=&quot;https://miro.medium.com/max/700/1*IlxBthy_kQMJh3YgHabJlw.png&quot; alt=&quot;Microservices Architecture Journey at Airbnb&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;Reorgs and technology investments to help address confusing ownership at Airbnb&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;reorgs-impact-clear-ownership&quot;&gt;Reorgs impact clear ownership&lt;/h1&gt;

&lt;p&gt;At the same time, companies are also very keen on reorgs, in part due to the recent popularity of the &lt;a href=&quot;https://www.thoughtworks.com/en-us/radar/techniques/inverse-conway-maneuver&quot;&gt;inverse conway maneuver&lt;/a&gt;. This attempts to use the fact that with Conway’s law, particularly in microservices, your org structure ends up designing your architecture, at least in terms of system boundaries. The &lt;a href=&quot;https://teamtopologies.com/&quot;&gt;Team Topologies&lt;/a&gt; book about this is very compelling, but I think many organizations attempt it without fully understanding the domains and systems that teams currently own. Without a process to understand and purposefully move applications to proper owning teams, the reorg can end up causing more chaos and confusion, increasing maintenance costs, and lowering MTTR for critical user journeys, not to mention reducing trust within the org. When done well, a reorg can lead to stronger and better understood domains owned by teams with a coherent set of domain and system boundaries. This leads to strong ownership and deeper domain knowledge within the team, enabling them to be more productive and responsive to the needs of the business.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;When the company needs to freeze hiring or contract, how are legacy systems assigned ownership and supported. Reorging when a company is growing leads to more positive ownership opportunities than reorging when a company is at a stable size or shrinking. How do we set up ownership so that we can withstand ups and downs. –Erica Tripp&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As leadership sees some of the issues falling out of the current org structures, they often will reach for the reorg, but reorgs themselves don’t change the ways of working, and often can make maintaining legacy systems harder if you move folks away from areas they have built domain expertise.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;changing structure and process without addressing leadership, cultural and ways of working issues, usually exacerbates existing problems rather than improving performance. –&lt;a href=&quot;https://medium.com/nick-tune-tech-strategy-blog/the-structure-and-process-fallacy-40640e844230&quot;&gt;The Structure and Process Fallacy&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In terms of clear ownership in microservices some even propose that, &lt;a href=&quot;https://microservices.io/patterns/decomposition/service-per-team.html&quot;&gt;“Ideally each team has only one service”&lt;/a&gt;. If you are a growing company that has been evolving microservices, I doubt this is the situation. After a number of reorgs, and folks coming and going, a team’s responsibilities more often look like a giant group of only semi-related services and libraries.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/example_team.png&quot; alt=&quot;overloaded but not entirely uncommon diagram showing a teams repo ownership&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;A overloaded but not entirely uncommon diagram showing a teams repo ownership&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you end up having teams with ownership responsibilities as shown above, with more services than team members, it is unlikely there is strong ownership with deep understanding of all the systems involved.&lt;/p&gt;

&lt;h1 id=&quot;what-do-we-need-to-know-about-our-teams&quot;&gt;What do we need to know about our teams?&lt;/h1&gt;

&lt;p&gt;We need to better understand the reality of what our teams are working on and if they are supported to work on the goals we set out for them. If the team’s goals align with the systems they have, if they are set up for success or are in a declining spiral of more maintenance than they can healthily support while also delivering new value. There is a lot we should know about how our teams currently look, so that we can re-balance in ways that ensure all teams are healthier and more effective. While we need to also consider domains, specific systems, and team’s charters to re-define an org structure, there are a lot of things that should be considered about the team health directly.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Is the team overloaded?
    &lt;ul&gt;
      &lt;li&gt;Do they maintain too many repos for the number of folks on the team?&lt;/li&gt;
      &lt;li&gt;Do they have too many weeks on-call per developer?&lt;/li&gt;
      &lt;li&gt;Are they supporting legacy systems with high maintenance burdens, or all newer and actively supported systems?&lt;/li&gt;
      &lt;li&gt;Are the systems they support well bound to a coherent set of domains?&lt;/li&gt;
      &lt;li&gt;Are the apps they currently support healthy? (I will dig in more into what is a healthy app in a future post).&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Is the team well rounded?
    &lt;ul&gt;
      &lt;li&gt;Does the team have some tenured folks that can navigate the org and have historical context?&lt;/li&gt;
      &lt;li&gt;Is the team a good mix of Sr / Jr folks, for the type or work?&lt;/li&gt;
      &lt;li&gt;Is the manager a good fit for the team (A newish manager with a Jr and non-tenured team, leaves everyone trying to learn and navigate at the same time)&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;How is attrition impacting the team?
    &lt;ul&gt;
      &lt;li&gt;Teams with higher attrition than others are likely significantly impaired, consider &lt;a href=&quot;https://twitter.com/benjiweber/status/1480219318273073157&quot;&gt;what really happens when someone leaves a team&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;what-do-we-need-to-know-about-our-repos-and-apps&quot;&gt;What do we need to know about our repos and apps?&lt;/h2&gt;

&lt;p&gt;We can’t just talk about teams without looking at the details. At some point we want to also look at all the repos they own, what they most actively work on, and the health of those systems. Especially after several rounds of reorgs, teams often end up with some things that don’t fit well into their charter, but responsibility for a system has to end up somewhere. It is best to at least make it clear where the responsibility ends up, even if it immediately requires some discussion about how the legacy load might not be healthy, so that it can be addressed. I will follow up on this question in a future post, digging into how to think about the health of repos and the value and risk they create for companies.&lt;/p&gt;

&lt;h1 id=&quot;strong-ownership-drives-quality&quot;&gt;Strong ownership drives quality&lt;/h1&gt;

&lt;p&gt;There are many other posts talking about the &lt;a href=&quot;https://bennorthrop.com/Essays/2022/code-ownership-stewardship-or-free-for-all.php&quot;&gt;impacts on ownership of microservices&lt;/a&gt;. I think there are still a lot of good questions on how best to handle it. I am mostly trying to make the case that thinking through the domain boundaries is not enough, one has to think of what it takes to have deep knowledge and strong ownership of a domain. It takes having a healthy team that isn’t spread too thin. Ideally they have a cohesive set of services and responsibilities that form a common domain. If you move teams into what seems like better domains as part of a reorg but don’t look at or address the other responsibilities a team may already have, strong ownership and the attempts at more clarity of domains will not succeed.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Operations vs Ownership vs HR Organizational Structures</title>
   <link href="https://www.mayerdan.com/management/2022/08/22/hr-org-dev-org"/>
   <updated>2022-08-22T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/management/2022/08/22/hr-org-dev-org</id>
   <content type="html">
&lt;h1 id=&quot;organizational-structure-drift-causes-issues&quot;&gt;Organizational structure drift causes issues&lt;/h1&gt;

&lt;p&gt;When your HR/Management org structure starts to drift from your operational and project development structure. The organization’s communication, shared understanding, system ownership, and incident response times suffer.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Communication: meetings references teams that no longer exist, in terms of ownership of runtime systems and deliverables. Folks that should be in the room aren’t invited.&lt;/li&gt;
  &lt;li&gt;Shared Understanding: If folks in different roles are referring to teams with different names which sometimes reference slightly different groups of people, understanding of who is working on which systems will become blurry.&lt;/li&gt;
  &lt;li&gt;system ownership: running operations for a system and not having input into its long term ownership in direction leads to increased pages and incidents with no vision for a stable and coherent system.&lt;/li&gt;
  &lt;li&gt;incident response: folks see incidents and can’t map the product feature and ownership to the system, team, and pager on-call / at fault to follow up on investigations&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;how-org-structure-drift-happens&quot;&gt;How org structure drift happens&lt;/h2&gt;

&lt;p&gt;If org charts, managers, and teams are reorged a few times eventually a newly formed team can inherit some legacy systems as it fits their newly defined mission.  These inherited systems somehow have moved away from the original authors or recent operators. One day the company will find out that a critical system seems to be owned by a team totally unfamiliar with it, often during an incident. This poor outcome does clarify the issue of re-orgs that don’t reflect operation and code ownership responsibilities.&lt;/p&gt;

&lt;h2 id=&quot;the-tale-of-the-teams-from-before&quot;&gt;The tale of the teams from before…&lt;/h2&gt;

&lt;p&gt;In this tale, an incident occurs with various services having problems, this might be figured out by an outside team that leverages the service that has started to fail. It becomes clear this upstream service needs attention and that no one is clear who is responsible for the system… It hasn’t had updates for months, no deploys for weeks, the code owners file references a team that was reorged out of existence. During the incident the downstream team can even page that service’s owner to find that a responder knows nothing about the service and didn’t know it was assigned to their team… or that the service was inherited and only one person really knows anything about it and they are on vacation.&lt;/p&gt;

&lt;p&gt;At this point during an incident, possibly in the middle of the night, you have folks with very little context about a system making decisions to “fix the service without safety” or “wake up people who likely don’t know how to help”. The failure of the moment must get resolved, and good people will dig in and figure it out,  but during any good post-mortem (incident review). It should come up that there wasn’t clear ownership of that system.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;it wasn’t clear which product team owned the service
    &lt;ul&gt;
      &lt;li&gt;or that team no longer actively worked on it, overlooked “legacy” system that is actually critical&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;a team clearly owned it but knew nothing about it
    &lt;ul&gt;
      &lt;li&gt;this was inherited after a reorg… none of the original engineers originally involved are involved anymore…&lt;/li&gt;
      &lt;li&gt;The new team have spent only minimum maintenance efforts to keep it running.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;a eng team still clearly owned the service, but has been trying to deprecate it for years without enough support to make significant progress&lt;/li&gt;
  &lt;li&gt;it actually isn’t clear who owns that any more… The team that owned it was re-orged away and it wasn’t noticed there is now a pager rotation with no people remaining.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you have been around the block a few times you have likely seen unhealthy system maintenance before. It might happen slowly over time as teams come and go, without paying attention organizational structure drift has started to result in increased risk, increased burnout, lowered productivity.&lt;/p&gt;

&lt;h2 id=&quot;what-does-organizational-drift-look-like&quot;&gt;What does organizational drift look like?&lt;/h2&gt;

&lt;p&gt;Depending on the current situationm needs folks end up having to try to use different tools and shared-language to find the right folks to help solve the problem. Below is some fake/scrubbed data based on Company Org charts mapping to apps, the PagerDuty teams mapped to apps, and Github teams mapped to apps (using &lt;a href=&quot;https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners&quot;&gt;CODEOWNERS files&lt;/a&gt;)&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Company Org Chart&lt;/th&gt;
      &lt;th&gt;PagerDuty Teams&lt;/th&gt;
      &lt;th&gt;Github Teams&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;img src=&quot;/assets/img/company_eng_org_chart.png&quot; alt=&quot;Eng Org Chart&quot; /&gt;&lt;/td&gt;
      &lt;td&gt;&lt;img src=&quot;/assets/img/pager_duty_chart.png&quot; alt=&quot;PagerDuty Chart&quot; /&gt;&lt;/td&gt;
      &lt;td&gt;&lt;img src=&quot;/assets/img/github_teams_chart_two.png&quot; alt=&quot;Github Teams Chart&quot; /&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;blockquote&gt;
  &lt;p&gt;diagrams showing the teams in terms of org charts, operations (PagerDuty), and code owners (Github)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Just from a visual check these diagrams diverge. When one starts to talk about the differences, various organizational issues become extremely obvious. From teams with far too many apps and responsibilities, to teams that simply do not exist anymore in terms of the org chart, but have sets of employees maintaining legacy systems in terms of code ownership or operational support.&lt;/p&gt;

&lt;p&gt;These diagrams don’t even cover that we have Eng Org team charts which don’t 1:1 align to the Product Org charts. If there is an incident occurring for say a 3rd party payment in an international market, that hasn’t triggered pager alerts… Where would you look for support? The International Expansion Engineering team? The Product person in the chart of payment flows? The Engineering Payments team? What if the payment issues are only for a certain product feature? Even if you know the specific application that has the code in it, do you reach out to the operation support team or the code owners of the system?&lt;/p&gt;

&lt;h2 id=&quot;how-many-different-ways-do-folks-try-to-interact-with-a-team&quot;&gt;How many different ways do folks try to interact with a team?&lt;/h2&gt;

&lt;p&gt;The value in having teams with consistent naming across multiple views makes it easier to find the right team whether it is for customer support, an active incident, or possibly just project collaboration or questions. There are many lenses where clear lines to or from teams can be helpful.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;customer relations support: user voice queue&lt;/li&gt;
  &lt;li&gt;pagers / on-call: pager-duty&lt;/li&gt;
  &lt;li&gt;code owners: github&lt;/li&gt;
  &lt;li&gt;metric owners: Datadog, can we map to services or teams?&lt;/li&gt;
  &lt;li&gt;real-time discussions: Slack, which channel for the team? or a project channel?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;how-to-fix-it&quot;&gt;How to fix it…&lt;/h2&gt;

&lt;p&gt;Everyone wants a data driven business and often can’t even get their organizational data to align across systems. Having a source of truth and being able to map it to any and all different use cases as views of the data is a powerful idea. Management should try to keep those views of teams as close as possible and have good reasons for when they drift (migrating from a out-dated to new view of responsibilities as an example). Companies need to make it very easy to find and look up the team organizational information and fix discrepancies.&lt;/p&gt;

&lt;p&gt;The fix is aligning on a source of truth and restructuring the other mappings to follow. You can start to enforce all other systems to map back to the source off truth in order of importance. Do this in code or an automated process, flag mis-matches, do not only rely on humans or you be back at it again as it quickly falls out of sync.&lt;/p&gt;

&lt;p&gt;When a company has shared views of what teams exist, who is on those teams, and what they are working on we can start to use data to look at ensuring product and system goals have appropriate staffing to support the goals, and we can see and respond to changes in the organization such as sudden unexpected resignations putting critical systems at risk. I will talk more about how we can look at team and service health in future postings.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Fun with Rails Query Tracing</title>
   <link href="https://www.mayerdan.com/ruby/2022/06/27/rails-query-tracing"/>
   <updated>2022-06-27T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/ruby/2022/06/27/rails-query-tracing</id>
   <content type="html">
&lt;h1 id=&quot;finding-the-origins-of-database-queries&quot;&gt;Finding the Origins of Database Queries&lt;/h1&gt;

&lt;p&gt;There are a number of reasons and times when trying to find the specific origin of a DB query comes up. Most often in legacy systems which have spiraled into extremely complex systems. A common cause is a shared database and trying to figure out which application is causing some specific query. Even in a single application, trying to tease out DB queries that have embedded themselves deep within a shared library. Whatever the cause, trying to find the source of a DB query comes up as a fairly common code detective task from time to time. Let’s look at a few approaches to tracking down the query.&lt;/p&gt;

&lt;h1 id=&quot;adding-query-comments&quot;&gt;Adding Query Comments&lt;/h1&gt;

&lt;p&gt;A really helpful tool for Rails is &lt;a href=&quot;https://github.com/basecamp/marginalia&quot;&gt;Marginalia&lt;/a&gt;, which can append comments about the source of a query to the query itself as a comment. This lets one leverage traditional DB tools like slow query logs, etc, and cross-reference from the query back to the code source. The output would look something like the below, depending on the tool you used to look at the queries.&lt;/p&gt;

&lt;p&gt;NOTE: A new Ruby query commenter has recently come up as an option. Implementing an open standard that integrates with open tracing and can join with trace-ids to other observability data. It is an implementation of &lt;a href=&quot;https://google.github.io/sqlcommenter/&quot;&gt;Google’s SqlCommenter standard&lt;/a&gt;, which has a &lt;a href=&quot;https://google.github.io/sqlcommenter/ruby/rails/&quot;&gt;Rails Implementation&lt;/a&gt; which extends Marginalia with additional information.&lt;/p&gt;

&lt;div class=&quot;language-sql 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;n&quot;&gt;my&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;service&lt;/span&gt; 	&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;COUNT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;some_table&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;some_table&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;some_reference_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;???&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/*application:my-service,controller:endpoint_a,action:show*/&lt;/span&gt; 	&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;181&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;my&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;service&lt;/span&gt; 	&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;some_table&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;some_table&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;some_table&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;???&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;LIMIT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;???&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/*application:my-service,controller:endpoint_b,action:create*/&lt;/span&gt; 	&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;227&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Some tools strip the comments or need to be configured to keep them. You can also set up your Rails logs to output this as well, but it can be really verbose. I leave it off by default but can toggle it on when debugging something specific.&lt;/p&gt;

&lt;div class=&quot;language-ruby 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;# add in config/initializers/marginalia.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Marginalia&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;application_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;my-service&quot;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;ENABLE_AR_LOGGING&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;logger&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;logger&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;level&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Logger&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;DEBUG&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;dd-database-apm&quot;&gt;DD Database APM&lt;/h1&gt;

&lt;p&gt;If you are already using an observability tool, many offer rich support for databases these days. For example, Datadog now offers &lt;a href=&quot;https://docs.datadoghq.com/database_monitoring/&quot;&gt;database monitoring&lt;/a&gt;. This can be a really powerful tool, and help to understand and drive the deprecation of tables over time. A few quick notes&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;If you have multiple applications connecting to a shared DB, ensure a unique DB Username so that datadog can attribute queries to specific applications&lt;/li&gt;
  &lt;li&gt;DD, unfortunately, strips query comments, it would be really nice if they kept the query comment in the DB Monitoring both for query metrics and samples.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.datadoghq.com/database_monitoring/query_metrics/&quot;&gt;query_metrics&lt;/a&gt; - understand query load, speed, etc especially a historical view&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.datadoghq.com/database_monitoring/query_samples/&quot;&gt;query_samples&lt;/a&gt; - all the details around specific queries&lt;/li&gt;
  &lt;li&gt;The APM application traces can help pinpoint where the code initiates the database span.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;fun-with-rails-logsubscriber&quot;&gt;Fun with Rails LogSubscriber&lt;/h1&gt;

&lt;p&gt;While the others are great general tooling, what if you need something more specific, or you just want to try to find specific queries by running tests and seeing the call stack? Well, this being Ruby of course we can hack together something for development.&lt;/p&gt;

&lt;h2 id=&quot;finding-the-query-needle-in-the-haystack&quot;&gt;Finding the Query Needle In the Haystack&lt;/h2&gt;

&lt;p&gt;Looking for call chains causing specific active record queries? Why not log out the call stack info or drop a debugger to inspect the code whenever a particular query is executed. Combining this with a robust test suite should help track down all the offending callers in no time.&lt;/p&gt;

&lt;p&gt;Rails or more specifically &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActiveRecord/ActiveSupport&lt;/code&gt; have all sorts of cool tools. A really useful one is &lt;a href=&quot;https://api.rubyonrails.org/classes/ActiveSupport/LogSubscriber.html&quot;&gt;LogSubscriber&lt;/a&gt;, it is a part of the &lt;a href=&quot;https://edgeguides.rubyonrails.org/active_support_instrumentation.html&quot;&gt;Active Support Instrumentation / Notifications ecosystem&lt;/a&gt;, which overall is really cool. It can be used to hook into many of the common abstractions in Rails, for example &lt;a href=&quot;https://stackoverflow.com/questions/6377190/modify-log-format-and-content-from-default-actioncontroller-logsubscriber-in-rai&quot;&gt;adding custom action controller logging&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In our case, let’s listen to all active record queries, with a log subscriber, and capture any of interest. This is some throw-away code I add when needed, I track down whatever I am looking for and remove it when my work is complete.&lt;/p&gt;

&lt;div class=&quot;language-ruby 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;# add in spec_helper.rb, or other test setup files&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;ActiveRecord&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;LogSubscriber&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveSupport&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;LogSubscriber&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;sql&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;# NOTE: I add a global $ignore_query == false &amp;amp;&amp;amp; if I need to say ignore all the factories or before/after spec specific queries to help&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;# only find callers in application code.&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/FROM &quot;some_table&quot; WHERE &quot;some_condition&quot;/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;match?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:sql&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
        &lt;span class=&quot;no&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;info&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;SQL FOUND &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;caller_locations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;150&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; 
        &lt;span class=&quot;nb&quot;&gt;binding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;irb&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;QUERY_BINDING&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;LogSubscriber&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;attach_to&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:active_record&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Remarkable2 Review</title>
   <link href="https://www.mayerdan.com/fun/2022/06/25/remarkable2-review"/>
   <updated>2022-06-25T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/fun/2022/06/25/remarkable2-review</id>
   <content type="html">
&lt;p&gt;I bought a &lt;a href=&quot;https://remarkable.com/&quot;&gt;Remarkable2&lt;/a&gt; early after launch (I believe, I was in batch 3). I haven’t really said much about it, in some sense because it doesn’t feel particularly cool, but what is clearly special is that I still use it nearly every day.  I have had the device for over a year now and still enjoy it. It isn’t really something to show off like a cool tech toy, but after settling into some basic usage patterns, it is a nearly daily tool for me and has effectively replaced pen and paper, allowing me to digitize and archive my notes when I want. The device isn’t perfect and future iterations hopefully improve the writing experience, but it has after many attempts gotten good enough that I rarely reach for traditional pen and paper. Although, as the company tries to move from selling a device to making profits off a subscription service, I am more likely to look for alternatives that aren’t subscription based.&lt;/p&gt;

&lt;p&gt;TL;DR: I have used this almost daily for 18 months, and have hundreds of pages of notes on it, it has become a part of my daily workflow.&lt;/p&gt;

&lt;h1 id=&quot;history-of-digital-notes-attempts&quot;&gt;History of Digital Notes Attempts&lt;/h1&gt;

&lt;p&gt;I have been wanting quick digital notes for decades and tried a number of solutions, most of which fell short so quickly, that I never developed any habit of using them on a day-to-day basis… The first attempts might date me and remind folks of a time when many companies were pushing for quick digital notes with palm pilots Graffiti script and making progress on natural digital note conversion for Pocket PCs. In recent years, it feels like there has been less interest in natural writing on digital devices, although Apple, brought some interest back with the &lt;a href=&quot;https://www.apple.com/shop/product/MU8F2AM/A/apple-pencil-2nd-generation&quot;&gt;Apple Pencil&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Pocket PC, with pen&lt;/li&gt;
  &lt;li&gt;Palm Pilot (handspring)&lt;/li&gt;
  &lt;li&gt;Samsung Galaxy, tablet with pen&lt;/li&gt;
  &lt;li&gt;iPad&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://us.livescribe.com/&quot;&gt;live scribe&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;This was an interesting one, but the requirement of special paper, and needing to having a bluetooth phone app running and syncing caused to much friction.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Samsung Galaxy S21 Ultra, with pen&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;why-the-remarkable-works-for-me&quot;&gt;Why the Remarkable Works for Me&lt;/h1&gt;

&lt;p&gt;I basically use the remarkable as an endless stream of notes. I have a primary notebook that I just add pages to the end ever growing. I used to have mutliple notebooks like work, personal, sketching, etc… Even the friction of having to switch from one notebook to another was to much and I would break my habit of writing something down. I then back up and archive that file… If I need something in particular digitized or want to work more on some particular page (say a sketch of a graph), I export it that to a single file. That is kind of it, my primary use case is just replacing the pen and notepad with an endless digital notepad. I love it because it really doesn’t work for much else than pen and paper replacement. It isn’t fancy, and many of the extra features haven’t been to useful to me, for example other than being a neat trick streaming it to my screen over a zoom as a whiteboard isn’t particularly engaging for anyone.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/remarkable_notes.jpg&quot; alt=&quot;Example of My Notes Remarkable&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;An example of what my list of notes often look like on the remarkable&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;alternative-usages&quot;&gt;Alternative Usages&lt;/h1&gt;

&lt;p&gt;OK, yes from time to time I do use the remarkable for other things, but the only thing that is a daily habit is taking quick notes, most often during meetings to serve as a reminder before moving the notes to better long term and more collaborative storage.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;sketching up a graph or chart to think through the design before spending more time on a detailed chart using real data&lt;/li&gt;
  &lt;li&gt;books, it is much easier for me to buy books on kindle as I am hooked into that store and it requires finding non DRM (or hacking around it) content to read on the remarkable, and the lack of backlight limits usable reading locations/time.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;comics-on-the-remarkable&quot;&gt;Comics On The Remarkable&lt;/h2&gt;

&lt;p&gt;Look how great these look, seriously an amazing reading experience.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/remarkable_comics.jpg&quot; alt=&quot;Black &amp;amp; White Comics Look Gorgeous on the Remarkable&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;Black &amp;amp; White Comics Look Gorgeous on the Remarkable&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;These comics, are very cool… but without color, most folks would prefer comics on an iPad. There are a lot of great older comics that were always black and white, which you can buy and put on the remarkable. I bought msot of mine from &lt;a href=&quot;https://support.comixology.com/hc/en-us&quot;&gt;Comixology&lt;/a&gt; before Amazon bought them. I am not sure if they still support non DRM comic formats that work on the remarkable.&lt;/p&gt;

&lt;h2 id=&quot;long-form-articles-are-easy-on-the-eyes&quot;&gt;Long Form Articles are Easy on the Eyes&lt;/h2&gt;

&lt;p&gt;I also read long-form articles on it, during the day with good lighting, I mostly reach tech and blog entries this way to give my eyes a break from the computer screen. I use the highlighter while reading to highlight sections of text to help me remember them or reference them later. I still use my Kindle at night for the backlight and it is simpler to buy content on the kindle, while it takes a bit of effort on the remarkable. For long artciles in the morning with my coffee the Remarkable is a great fit.&lt;/p&gt;

&lt;h2 id=&quot;sketching&quot;&gt;Sketching&lt;/h2&gt;

&lt;p&gt;It is fun to sketch and draw whie watching TV, it isn’t something I do a ton, but it can be a nice way to let the mind wander.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/remarkable_sketch.png&quot; alt=&quot;Doodling on the Remarkable&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;doodling on the Remarkable&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;a-future-full-of-improvements-in-digitial-notes&quot;&gt;A Future Full of Improvements in Digitial Notes&lt;/h1&gt;

&lt;p&gt;Even though this product has found a place in my daily workflow, it doesn’t mean it has solved the problem… In fact, it still means I am chasing the same dream, now I just have something acceptable enough that I don’t entirely give up on it. A short list of things that need to be improved.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;No backlight&lt;/li&gt;
  &lt;li&gt;Slow initial response (getting out of sleep/power off and into notes is annoying at work I have it set up to basically always be awake) but it just doesn’t work for taking a quick note before bed… I just email myself from my phone&lt;/li&gt;
  &lt;li&gt;Digital conversion notes to digitized text are still pretty bad, I convert drawings, charts, etc… but otherwise things are just stored/archived as basically PDF drawings.
    &lt;ul&gt;
      &lt;li&gt;I still dream of being able to hand write in the markdown format for show blog posts&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Article sync isn’t perfect to send remarkable strips of images and graphics… The PDF view is less easy on the eyes but keeps images as part of the file* the pen capture resolution is still notably less sharp and responsive than a pen… My wife’s cursive which is already hard to read with a good pen and paper is particularly difficult as the remarkable is just not as fine or subtle of the line as it could be.&lt;/li&gt;
  &lt;li&gt;Not every device should require a service, as with all software the companies Remarkable is now trying to have a monthly/yearly subscription as part of using the device. I am trying to find and remove all subscriptions I can from my life. I don’t feel like I need to pay for the device and for each month I use it. Most of the cloud features are useless to me, I just need it to back up a file.&lt;/li&gt;
  &lt;li&gt;Color, I really love the e-ink experience but it is still common where I don’t read on the device because articles have pictures, charts, or leverage color… I keep hearing e-ink color is just around the corner&lt;/li&gt;
  &lt;li&gt;A ridiciously good drawing environment. I want layers, colors, smoothing curve line drawing mode… Like the best of photoshop with e-ink&lt;/li&gt;
  &lt;li&gt;Higher resolution writing capture, use a really nice pen and sign your name and then compare to your remarkable signature… There is still a way to go&lt;/li&gt;
  &lt;li&gt;Give me the Web
    &lt;ul&gt;
      &lt;li&gt;Web browser… Yeah, I know e-ink web browsing? Yes, I think it would be great for textually rich sites…&lt;/li&gt;
      &lt;li&gt;Email, I love to do email on the remarkable, if instant text digitization worked well&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the end, it is great and I am happy with it, but it is still kind of an expensive toy, I am sure most folks wouldn’t find it worth it and would be better off with a good phone or tablet. I know I have grown accustom to it, and would miss it if it broke and was no longer a tool I could reach for.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/remarkable_comic_grid.jpg&quot; alt=&quot;Grid view on the Remarkable&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;example of browsing multiple pages on the Remarkable&lt;/p&gt;
&lt;/blockquote&gt;
</content>
 </entry>
 
 <entry>
   <title>NFTs, Art Investing, and Collectibles</title>
   <link href="https://www.mayerdan.com/fun/2022/05/19/nfts-art-investing-and-collectibles"/>
   <updated>2022-05-19T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/fun/2022/05/19/nfts-art-investing-and-collectibles</id>
   <content type="html">
&lt;p&gt;I am still skeptical of Crypto in general, I do own some coins for fun / investment, but consider it an interesting and very risky investment. As far as NFTs built on top of the blockchain, I looked into it and decided that in its current form and iteration it isn’t compelling to me… I think there are some real concerns around the crypto ecosystem for now, and I am happy to watch and wait. Hearing all the swirl around NFTs and art investments did get me thinking a bit more about how I spend money on Art and where I could perhaps do some more purposeful spending to support art.&lt;/p&gt;

&lt;h2 id=&quot;art-as-an-investment&quot;&gt;Art As An Investment&lt;/h2&gt;

&lt;p&gt;Does anyone else having these ads follow them around?&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/art_vs_market.png&quot; alt=&quot;Art vs Market&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;NFTs, brought art investment to the software investment market… and it keeps growing! Or so they say… as NFTs collapse back down.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I know there are a number of startups trying to “open up” the art market to collective investment both for traditional art masterpieces and other digital markets. Positioning it as diversification in investments to get money out of the markets, move away from large cash stock piles, etc… It’s all a bit much for me, but with NFTs and these types of non-traditional investments being marketed all over the internet. The ads did make me think is there any art I would like that might be an investment? Should we always be treating art as something that needs to hold or increase value? I hope not, but if I am not really buying any art that isn’t supporting artists, perhaps I should buy more art and I can pretend it is small investments.&lt;/p&gt;

&lt;p&gt;I have never really had or held art as an investment or followed the art market, but I had as a kid been more into various collectibles… baseball cards, comics, POGs (yes, I have hundreds of them), and magic the gathering cards. I offloaded most of my collections over time but always held on to some toys, comics, and magic cards… Turns out a few of my magic cards in terms of a percentage growth have beat my index funds! I guess, if I can consider collectibles as pop art, I have some experience and it feels a lot more fun to participate in than NFTs.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/dual_lands.jpg&quot; alt=&quot;A Few Dual Lands&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;Oh wow that box of magic cards in my basement has a handful of dual lands and dozens of other older interesting cards.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I don’t really want to be in a world where all art is an investment, so while it might be fun to buy some pop art/collectibles that could increase in value, I also wanted to look at art that just makes me happy and can help support artists.&lt;/p&gt;

&lt;h2 id=&quot;comics&quot;&gt;Comics&lt;/h2&gt;

&lt;p&gt;Comics? Seriously? Yeah, why not with NFTs as art, it feels like folks are really looking to get into collectibles again. Along with the current state of blockbuster movies being mostly comics, I feel like the value of comic collectibles going up over time seems likely… Old nerds watch marvel movies nostalgic for our childhood comic days, soon folks will be nostalgic for early comic movies and the comics of the time that inspired them… A number of comic movies are a generation behind like Spawn, Maxx, and others that might due for a movie revisit. Seems like some of the comics around the movie reboots will jump in value whenever that nostalgia hits.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/comics_framed.jpg&quot; alt=&quot;Comics Framed&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;A few comics after I framed them&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/theo_iron_man.jpg&quot; alt=&quot;Theo with his comics&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;Theo smiling after we put up Iron Man (current favorite) on his wall&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Also, if I am wrong and I probably am, it isn’t a real big investment, and it is cool pop culture art you can hang on your wall, a gift to kids, cousins, and friends. I picked up a number of comics and got them framed for my and some friends’ kids. Instead of paying a ton of money in ETH for a digital NFTs image that at the moment seems mostly like bragging rights, my kiddos have their favorite comic hero up on their wall.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/spiderman_fold.jpg&quot; alt=&quot;Spider Man Spread&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;Spiderman before framing&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/spiderman_grid.jpg&quot; alt=&quot;Spider Man Framed&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;Spiderman comics after framing&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;commissioned-art&quot;&gt;Commissioned Art&lt;/h2&gt;

&lt;p&gt;There are lots of ways to commission custom art, and it isn’t always insanely expensive. After our dog passed, our family had a commissioned piece to remember our dog Spot.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/spot_art.jpg&quot; alt=&quot;Spot Commission Drawing&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;Our dog Spot&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After a small family reunion-type vacation, we had an artist take a group photo and turn it into a drawing that we sent to all the families…&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/fam_art.jpg&quot; alt=&quot;Family Art&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;supporting-small-artists&quot;&gt;Supporting Small Artists&lt;/h3&gt;

&lt;p&gt;On sites like Etsy, you can find much smaller and more unique art that is possibly a better fit for your tastes than things you will find in a more gallery or commercial art market. You can support smaller more niche artists that have a quirky hobby or just haven’t found a large commercial following. Anyways, it is a nice way to be able to support.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.etsy.com/search?q=drawing%20from%20photo&amp;amp;ref=auto-1&amp;amp;as_prefix=drawing%20from%20photo&quot;&gt;Etsy Art&lt;/a&gt; - Many affordable options for commissioned custom art&lt;/li&gt;
  &lt;li&gt;I even know a developer who has &lt;a href=&quot;https://adavis.info/2016/12/conference-speaking-one-year-later-every-single-thing-ive-learned.html&quot;&gt;commissioned custom art for her public speaking slides&lt;/a&gt;!&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;photographers&quot;&gt;Photographers&lt;/h2&gt;

&lt;p&gt;This is probably the most common way many non-arty folks help support artists… Hiring a photographer. Wedding photos, new baby photos, etc… We have enjoyed the experience and the photos so we have increased this practice by hiring photographers for larger family trips when cousins get together. Major life milestones, etc… It is fun and we get some great photos that end up on walls, Christmas cards, etc.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/fam_photo.jpg&quot; alt=&quot;Family Photo&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;A family photo during a vacation&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;local-art-fairs&quot;&gt;Local Art Fairs&lt;/h2&gt;

&lt;p&gt;While I haven’t attended many of these recently, we are planning to hit up &lt;a href=&quot;https://denversartdistrict.org/first-friday&quot;&gt;First Friday Art Walks in Denver’s Art District on Santa Fe&lt;/a&gt; soon. I remember as a kid visiting Art fairs in Springfield, IL often and collecting little small drawings or paintings intended to be sold to kids for $3-5, these pieces proudly hung on my walls and inspired my own creations. I hope my kids enjoy and remember the experience of finding little pieces of art that speak to them. I even found a favorite artist, whom every year, I would seek out their small photo-realistic pencil drawings of animals in the kid’s art tent.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/frog_drawing.jpg&quot; alt=&quot;Frog Drawing&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;Pencil drawings, I bought as a kid… I still have a gorilla, dog, and my favorite frog&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/floating_girl.jpg&quot; alt=&quot;Floating Girl Painting&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;A painting my parents bought in Springfield and was handed down to me&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;original-art&quot;&gt;Original art&lt;/h2&gt;

&lt;p&gt;There are various places to pick up original art. We like to purchase some art while traveling.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/vietnam_painting.jpg&quot; alt=&quot;Vietname Painting&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;A painting we bought when in Vietnam, shipped back, and then framed&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;While traveling met a local painter and loved many of her pieces but the color scheme wouldn’t work for our house, they mentioned commissioning pieces with loose guidelines like color themes and taking it from there. We are actively considering commissioning a piece, and it wouldn’t cost more than any of the paintings they already had for sale, which had standard prices based on canvas size.&lt;/p&gt;

&lt;p&gt;Interested in just finding a custom piece online? If you have any favorite sites let me know.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.singulart.com/en/painting/marine&quot;&gt;SingulArt&lt;/a&gt; - reasonable prices for custom paintings (we haven’t purchased from here yet, but are currently considering a piece).&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>Redis & Sidekiq</title>
   <link href="https://www.mayerdan.com/ruby/2022/04/10/redis-sidekiq"/>
   <updated>2022-04-10T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/ruby/2022/04/10/redis-sidekiq</id>
   <content type="html">
&lt;h1 id=&quot;redis--sidekiq&quot;&gt;Redis &amp;amp; Sidekiq&lt;/h1&gt;

&lt;p&gt;A collection of notes about working with Sidekiq and Redis. A previous post about &lt;a href=&quot;https://www.mayerdan.com/ruby/2022/03/26/ruby-redis&quot;&gt;Ruby &amp;amp; Redis&lt;/a&gt;, briefly touched on some things, but I will get into more specifics in this post.&lt;/p&gt;

&lt;h1 id=&quot;redis-and-background-jobs&quot;&gt;Redis and Background Jobs&lt;/h1&gt;

&lt;p&gt;A common usage of Redis for Rubyists is for background jobs. Two popular libraries for jobs are &lt;a href=&quot;https://github.com/mperham/sidekiq&quot;&gt;Sidekiq&lt;/a&gt; and &lt;a href=&quot;https://github.com/resque/resque&quot;&gt;Resque&lt;/a&gt;. At this point, I highly recommend Sidekiq over Resque as it is more actively maintained and has more community support around it. I am not going to get into too many specifics of Sidekiq and Resque, but talk a bit more about how they use Redis. There are always some gotchas when working with Redis, ask folks about sometimes an incident that occurred because of a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;keys&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;flushall&lt;/code&gt;, or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;flushdb&lt;/code&gt; command. Some of these commands are destructive which is always something to be careful with, but they also all have very slow performance characteristics. It is worth noting how some of the calls in Resque and Sidekiq scale with queue depth, which is critical to understand.&lt;/p&gt;

&lt;blockquote class=&quot;twitter-tweet&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;One of the most desirable properties in queue or stream based systems is increased efficiency as the queue depth grows. Without that property, slight mode shifts can push the system into the state of perpetually growing backlog. Who here hasn&amp;#39;t a seen a graph like this: 1/ &lt;a href=&quot;https://t.co/opKuB7Y9tA&quot;&gt;pic.twitter.com/opKuB7Y9tA&lt;/a&gt;&lt;/p&gt;&amp;mdash; Joe Magerramov (@_joemag_) &lt;a href=&quot;https://twitter.com/_joemag_/status/1511027555813318658?ref_src=twsrc%5Etfw&quot;&gt;April 4, 2022&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async=&quot;&quot; src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;

&lt;h1 id=&quot;a-incident-caused-by-our-sidekiqredis-observability&quot;&gt;A Incident Caused by our Sidekiq/Redis Observability&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;UPDATE:&lt;/strong&gt; We no longer think the line below is the culprite, we observe latency growth and decline with queue size, but we are unsure of the cause and unable to reproduce. As seen, in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NOTE&lt;/code&gt; above the Sidekiq latency call is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;O(1+1)&lt;/code&gt; and therefor fast and predictable.&lt;/p&gt;

&lt;p&gt;We got into trouble when moving from Resque to Sidekiq because our observability instrumentation was frequently making an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;O(S+N)&lt;/code&gt; call (Sidekiq’s queue &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;latency&lt;/code&gt;). It wasn’t much of a problem until we had one of our common traffic spikes that results in a brief deep queue depth. Our previous Resque code didn’t have any issues and had some similar instrumentation being sent to Datadog. While our Sidekiq code had been live for days, this behavior where our processing speed decreased with queue depth hadn’t been observed or noticed. The problem came to light when on a weekend (of course) a small spike caused a background job backlog, as we an expected common case. The latency went way up due to our instrumentation and we started processing jobs slower than we enqueued them. This fairly quickly filled our entire Redis leading to OOM errors.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/redis_sidekiq.png&quot; alt=&quot;Redis Sidekiq Analytics&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;Analytics Monitoring our Recovery&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;These charts are from after the incident. We moved to a new Redis to get things back up and running during the incident, and after things were under control worked on draining the old full Redis, in a isolated way that couldn’t impact production load. In this graph, you can see as we reduce the queue size the latency of our Redis calls also reduces in step. I included CPU to show how hard we were taxing our Redis, this chart isn’t 1:1 as we were adding and removing workers and making some other tweaks, but the queue size -&amp;gt; latency is a direct correlation.&lt;/p&gt;

&lt;h2 id=&quot;code-culprite&quot;&gt;Code Culprite&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;NOTE: Update Mike responded that he doesn’t think the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;latency&lt;/code&gt; call is the &lt;a href=&quot;https://github.com/mperham/sidekiq/issues/5282&quot;&gt;issue so we are further investigating&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UPDATE:&lt;/strong&gt; We no longer think the line below is the culprite, we observe latency growth and decline with queue size, but we are unsure of the cause and unable to reproduce. As seen, in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NOTE&lt;/code&gt; above the Sidekiq latency call is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;O(1+1)&lt;/code&gt; and therefor fast and predictable.&lt;/p&gt;

&lt;p&gt;As mentioned it wasn’t any of our normal code that was really the problem, it was this line that was part of our instrumentation and observability tooling. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Sidekiq::Queue.new(queue_name).latency&lt;/code&gt;. As with any incident, there were a ton of other related things, but it is worth noting that this seemingly simple line could have some hidden gotchas or an outsized impact on your Redis performance. As that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;latency&lt;/code&gt; call scales linearly with queue size, it is calling Redis’s &lt;a href=&quot;https://redis.io/commands/lrange/&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Lrange&lt;/code&gt;&lt;/a&gt; under the hood which is an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;O(S+N)&lt;/code&gt; operation.&lt;/p&gt;

&lt;h1 id=&quot;sidekiq--redis-performance&quot;&gt;Sidekiq / Redis Performance&lt;/h1&gt;

&lt;p&gt;A colleague &lt;a href=&quot;https://twitter.com/samsm&quot;&gt;@samsm&lt;/a&gt;, helped dig into this incident by putting together the queue size -&amp;gt; latency charts above as well as all the helpful tables I am sharing below. These showe how Sidekiq calls translate into their Redis implementation operation details and the operational costs.&lt;/p&gt;

&lt;h2 id=&quot;redis--sidekiq-math&quot;&gt;Redis / Sidekiq Math&lt;/h2&gt;

&lt;p&gt;Doing the math on various Sidekiq operations: how much will they impact Redis?&lt;/p&gt;

&lt;h2 id=&quot;big-o-complexity-notation-101&quot;&gt;Big O Complexity Notation 101&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://www.honeybadger.io/blog/a-rubyist-s-guide-to-big-o-notation/&quot;&gt;Big O Notation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/redis_big_o.png&quot; alt=&quot;Redis Big O&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;sidekiq-operation-complexity&quot;&gt;Sidekiq Operation Complexity&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/redis_sidekiq_map.png&quot; alt=&quot;Redis Sidekiq Mapping&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;additional-sidekiq--redis-reading&quot;&gt;Additional Sidekiq / Redis Reading&lt;/h1&gt;

&lt;p&gt;Some additional reading if you want to dig in further on working with Sidekiq and Redis&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/mperham/sidekiq/wiki/Best-Practices&quot;&gt;Sidekiq Best Practices&lt;/a&gt; has some useful tips and calls out gotchas&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://nateberk.gumroad.com/l/sidekiqinpractice&quot;&gt;Sidekiq in Practice&lt;/a&gt; book, fastest way to get it right the first time if you are just learning Sidekiq&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://sloboda-studio.com/blog/testing-sidekiq-jobs/&quot;&gt;Blog Post on Sidekiq testing&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/mperham/sidekiq/wiki/Testing&quot;&gt;Sidekiq Testing Wiki Entry&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://redis.io/commands/&quot;&gt;Redis Commands Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>CDNs</title>
   <link href="https://www.mayerdan.com/devops/2022/04/04/using-cdns"/>
   <updated>2022-04-04T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/devops/2022/04/04/using-cdns</id>
   <content type="html">
&lt;p&gt;I am starting a series of self-reference materials on staples of web application delivery. A place to collect and document my learnings and understanding of various technologies I often reach for when designing systems. The first in this series was about &lt;a href=&quot;/ruby/2022/03/26/ruby-redis&quot;&gt;Ruby and Redis&lt;/a&gt; While this one will focus in on Content Delivery Networks. These reference pages will be updated over time and evolve as my usage or focus changes.&lt;/p&gt;

&lt;h1 id=&quot;content-delivery-network-cdn&quot;&gt;Content Delivery Network (CDN)&lt;/h1&gt;

&lt;p&gt;A Content Delivery Network can help serve high traffic or high performance websites as well as offer a number of features.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;A content delivery network, or content distribution network (CDN), is a geographically distributed network of proxy servers and their data centers. The goal is to provide high availability and performance by distributing the service spatially relative to end users. CDNs came into existence in the late 1990s as a means for alleviating the performance bottlenecks of the Internet[1][2] as the Internet was starting to become a mission-critical medium for people and enterprises. Since then, CDNs have grown to serve a large portion of the Internet content today…
– wikipedia&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;minimum-of-what-you-want-your-cdn-to-be-doing-for-you&quot;&gt;Minimum of what you want your CDN to be doing for you&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Smart routing: last-mile network distribution&lt;/li&gt;
  &lt;li&gt;Speedy established TLS: speed up your TLS handshakes&lt;/li&gt;
  &lt;li&gt;DDOS Protection: Cached pages are kind of already protected but many CDNs offer DDOS protection (you can also do this at your load balancer layer)&lt;/li&gt;
  &lt;li&gt;Serving assets: handling serving assets to avoid having static file load hit dynamic servers&lt;/li&gt;
  &lt;li&gt;Caching: at least assets, but even better for HTML / API content&lt;/li&gt;
  &lt;li&gt;Compression: gzip and brotli&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;cdn-setups&quot;&gt;CDN Setups&lt;/h1&gt;

&lt;p&gt;Often you might want different configurations and settings for different purposes and uses of CDNs. For example, it is fairly common to have an asset CDN with long-lived caches for assets, while you might want applications to be able to specify find grained caching headers for HTML and API content.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mermaid.live/edit/#pako:eNqFkE1vgzAMhv-K5XM_DrtxmESgt62qxE4j1RQFq6ASgkwihID_vqC0U2_zya_92H7lGbWtCBO8sepr-MplByG-S8F2HIivDw37_Tuk5TiOh8l6_tH2oK2BLD9ft9ai-uZYO9MuIOb0Q6yyi4Pi0e3bCdh613Q3YN_SsEA2n7IC0ssF0vVfOP-DxQrP5dHVuVTDQG54NXZ8ddZDBBb4nIs3SDcBwus7uXAYd2iIjWqq8IV5WyzR1WRIYhLSSvFdouzWwPm-Uo5OVeMsY-LY0w6Vd7aYOv3UkckbFR5qYnH9BTT0dXI&quot;&gt;&lt;img src=&quot;https://mermaid.ink/img/pako:eNqFkE1vgzAMhv-K5XM_DrtxmESgt62qxE4j1RQFq6ASgkwihID_vqC0U2_zya_92H7lGbWtCBO8sepr-MplByG-S8F2HIivDw37_Tuk5TiOh8l6_tH2oK2BLD9ft9ai-uZYO9MuIOb0Q6yyi4Pi0e3bCdh613Q3YN_SsEA2n7IC0ssF0vVfOP-DxQrP5dHVuVTDQG54NXZ8ddZDBBb4nIs3SDcBwus7uXAYd2iIjWqq8IV5WyzR1WRIYhLSSvFdouzWwPm-Uo5OVeMsY-LY0w6Vd7aYOv3UkckbFR5qYnH9BTT0dXI&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1 id=&quot;why-you-should-have-a-cdn&quot;&gt;Why You Should have A CDN&lt;/h1&gt;

&lt;p&gt;It can help even if you aren’t quite ready to fully leverage it. CDNs have a lot of hidden advantages, and with some early setup can be very quickly utilized to handle insane amounts of traffic. Handling loads of largest advertising campaigns for the initial experience even if all those new signups have to be pushed into a queue to handle when you can. As this tweet points out even after folks think of all the obvious reasons to have CDNs, there are often many other clever ways to use CDNs.&lt;/p&gt;

&lt;blockquote class=&quot;twitter-tweet&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;There are a bunch of benefits to putting a CDN on top of your web application, such as reduced TTFB, caching, and DDoS protection. But there&amp;#39;s also a huge benefit that people don&amp;#39;t talk about: No user-side DNS caching. You can switch origins within minutes, not days/weeks/months.&lt;/p&gt;&amp;mdash; Jack Ellis (@JackEllis) &lt;a href=&quot;https://twitter.com/JackEllis/status/1510680148336541698?ref_src=twsrc%5Etfw&quot;&gt;April 3, 2022&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async=&quot;&quot; src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;

&lt;h2 id=&quot;a-start-up-story&quot;&gt;A Start Up Story&lt;/h2&gt;

&lt;p&gt;My first startup got a viral link taking it down and by the time we got page caching on our rails server a few hours had passed and we lost the bump… For this first bit of traction, a CDN could have made all the difference. The content would have been easy to protect with the single viral page behind the CDN and appropriate content headers. That startup never succeeded, but early on much time was wasted trying to improve app layer caching and performance where a CDN could have been a big help.&lt;/p&gt;

&lt;h1 id=&quot;cdn-gotchas&quot;&gt;CDN Gotchas&lt;/h1&gt;

&lt;p&gt;While CDNs are great, nothing is free and every abstraction adds some complexity to your system. Understanding the values it can provide and also understanding some of the gotchas cna help your team decide if it is the right decision for the system.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Accidentally caching private pages/data!&lt;/li&gt;
  &lt;li&gt;caches including things like a set cookie (sessions, return_to, etc) that should be user-specific&lt;/li&gt;
  &lt;li&gt;CSRF, many traditional protections like the built-in Rails CSRF don’t work well with cached pages&lt;/li&gt;
  &lt;li&gt;difficulties with various security implementations like &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/nonce&quot;&gt;content-security-policy &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nonce&lt;/code&gt;&lt;/a&gt; implementations&lt;/li&gt;
  &lt;li&gt;Having different rules for cache keys and what information is sent to the origin&lt;/li&gt;
  &lt;li&gt;you may only cache on specific cookies and headers…but you might want your origin to receive all headers to help with debugging or other info, this is an additional mental load when understanding a request-response cycle.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;understand-the-reliability-risk-and-how-to-mitigate-it&quot;&gt;Understand the reliability risk and how to mitigate it&lt;/h2&gt;

&lt;p&gt;I have written previously about &lt;a href=&quot;/sre/2022/01/18/micro-service-request-depth&quot;&gt;request depth and availabiltiy&lt;/a&gt;. A CDN is an additional layer in the request depth of your application stack… It also only has a &lt;a href=&quot;https://aws.amazon.com/cloudfront/sla/&quot;&gt;99.9% AWS SLA&lt;/a&gt;, as the entry point to your systems which means that is the upper limit on your overall SLA pretending for a moment everything else was 100% reliable. Now, AWS and most CDNs actually have far better real-world uptime and success rates during normal operations, but all of the major CDNs have had notable major outages. Including &lt;a href=&quot;https://www.channelfutures.com/cloud-2/amazon-cloudfront-dns-service-suffers-pre-thanksgiving-outage&quot;&gt;Cloudfront having a major outage the day before thanksgiving&lt;/a&gt;. There are a few protective measures one can take a team really needs to ensure higher availability of their site, neither suggestion is cheap.&lt;/p&gt;

&lt;p&gt; * A team can implement and support &lt;a href=&quot;https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/high_availability_origin_failover.html&quot;&gt;High Availability Origin Failover&lt;/a&gt; as a protective layer within Cloudfront, to protect against origin level failures.
 * A team can implement &lt;a href=&quot;https://constellix.com/news/100-uptime-and-fool-proof-dns-with-failover&quot;&gt;multi-CDN DNS failover&lt;/a&gt; This can be done in an automated or manual fashion depending on complexity and cost concerns.&lt;/p&gt;

&lt;h1 id=&quot;cdns-vs-load-balancers&quot;&gt;CDNs vs Load Balancers&lt;/h1&gt;

&lt;p&gt;These days CDNs can do a lot of what load balancers do, by routing different types of requests to different back-end “origin” servers. Geo target routing to support locale and nearest reach servers… While this overlaps with load balancers, I generally end up with a configuration where I have both a CDN and load balancers in place. My setup looks often looks like so: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;web browser -&amp;gt; CDN -&amp;gt; ALB -&amp;gt; Application Servers&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.cloudflare.com/learning/cdn/cdn-load-balance-reliability/#:~:text=Software%2Dbased%20load%20balancing%20services,using%20physical%20load%2Dbalancing%20hardware.&quot;&gt;What is load balancing? How does a CDN load balance traffic?&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.resonatenetworks.com/2020/06/27/cdns-vs-load-balancers-which-one-should-you-pick/&quot;&gt;CDNs vs. Load Balancers: Which One Should You Pick?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;cdns-enhancing-request-payloads&quot;&gt;CDNs Enhancing Request Payloads&lt;/h1&gt;

&lt;p&gt;As part of the ability to route different requests, CDNs now often handle basic IP Address Geolocation, allowing one to route requests from different countries, language support, city, postal code, or more to different origins. Even if your application doesn’t use different origins, the Geolocation information is often extremely useful. You can avoid additional network calls or 3rd party integrations to leverage the CDN’s built-in geolocation support. For example, &lt;a href=&quot;https://aws.amazon.com/about-aws/whats-new/2020/07/cloudfront-geolocation-headers/&quot;&gt;AWS Cloudfront can provide Geolocation&lt;/a&gt; info on all requests to the CDN to impact caching logic as well as header hints to your application servers. For example, it is helpful to use this data to detect if a user might be accessing your site on the wrong domain given the country they are in. Example additional header data.&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;CloudFront-Viewer-Country-Name: United States
CloudFront-Viewer-Country-Region: MI
CloudFront-Viewer-Country-Region-Name: Michigan
CloudFront-Viewer-City: Ann Arbor
CloudFront-Viewer-Postal-Code: 48105
CloudFront-Viewer-Time-Zone: America/Detroit
CloudFront-Viewer-Latitude: 42.30680
CloudFront-Viewer-Longitude: -83.70590
CloudFront-Viewer-Metro-Code: 505
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; inside a Ruby app, Rails/Rack will format the headers adding in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HTTP&lt;/code&gt; and upcasing, so you could access this data in a typical Rails app like so &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;request.headers[&quot;HTTP_CLOUDFRONT_VIEWER_CITY&quot;]&lt;/code&gt;.&lt;/p&gt;

&lt;h1 id=&quot;cdns-are-evolving&quot;&gt;CDNs Are Evolving&lt;/h1&gt;

&lt;p&gt;This post covers more traditional CDNs and features common consumer-facing sites should consider leveraging. CDNs are now moving into the realm of cloud infrastructure with &lt;a href=&quot;https://aws.amazon.com/lambda/edge/&quot;&gt;Lambda@Edge&lt;/a&gt; and other CDNs allowing code (most commonly Javascript) to be deployed to the CDN network. The features and options supported when looking at having fully supported runtime code at the edge opens up new architecture and application stack options. I am looking at FAAS and edge deployed code, but haven’t leveraged it in any significant production environment yet. It is definitely a space to keep an eye on that promises to simplify global app distribution while maintaining extremely performant consumer experiences.&lt;/p&gt;

&lt;h1 id=&quot;additional-cdn-links&quot;&gt;Additional CDN Links&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://medium.com/@tranduchanh.ms/optimize-rails-app-performance-with-rails-amazon-cloudfront-e3b305f1e86c&quot;&gt;Optimize Rails App Performance With Rails + Amazon CloudFront&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://jvns.ca/blog/2016/04/29/cdns-arent-just-for-caching/&quot;&gt;CDNs aren’t just for caching&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://wizardzines.com/comics/cdn/&quot;&gt;CDN comic&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.thecloudcast.net/2019/10/understanding-https-cdns-and-edge.html&quot;&gt;Understanding HTTP/S, CDNs and Edge Proxies&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/high_availability_origin_failover.html&quot;&gt;Optimizing high availability with CloudFront origin failover&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://aws.amazon.com/cloudfront/sla/&quot;&gt;Cloudfront SLA&lt;/a&gt; - 99.9%&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.channelfutures.com/cloud-2/amazon-cloudfront-dns-service-suffers-pre-thanksgiving-outage&quot;&gt;Cloudfront 2 hour outage&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.theguardian.com/technology/2021/jun/08/edge-cloud-error-tuesday-internet-outage-fastly-speed&quot;&gt;When a CDN goes down&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;The best defense for this is DNS fallback routing around the CDN… If you configure a failover routing policy. For example on AWS, Route53 would check the health of your Cloudfront distribution and if it’s not healthy then the traffic would failover directly to your load balancer.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

</content>
 </entry>
 
 <entry>
   <title>Ruby & Redis</title>
   <link href="https://www.mayerdan.com/ruby/2022/03/26/ruby-redis"/>
   <updated>2022-03-26T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/ruby/2022/03/26/ruby-redis</id>
   <content type="html">
&lt;h1 id=&quot;ruby--redis&quot;&gt;Ruby &amp;amp; Redis&lt;/h1&gt;

&lt;p&gt;A collection of notes and some tips about using Redis.&lt;/p&gt;

&lt;h1 id=&quot;redis-setup&quot;&gt;Redis Setup&lt;/h1&gt;

&lt;p&gt;Redis is super easy to setup, and in dev mode often just works right out of the box, but as you leverage and scale it inproduction, you might want to think more about it’s setup beyond just setting a default &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;REDIS_URL&lt;/code&gt; ENV var. Often a basic Redis for simple product is just setup like so…&lt;/p&gt;

&lt;div class=&quot;language-ruby 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;no&quot;&gt;Redis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;current&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Redis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;url: &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;REDIS_URL&apos;&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;This has some issues:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The usage of &lt;a href=&quot;https://makandracards.com/makandra/510011-version-5-of-the-ruby-redis-gem-removes-redis-current&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Redis.current&lt;/code&gt; is deprecated&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Above is using all default options like timeout&lt;/li&gt;
  &lt;li&gt;A &lt;a href=&quot;https://github.com/redis/redis-rb/issues/826&quot;&gt;shared redis is blocking across threads&lt;/a&gt; and can be much slower for highly threaded server configurations, for example Puma.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A better setup adding in configurable options:&lt;/p&gt;

&lt;div class=&quot;language-ruby 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;no&quot;&gt;Redis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;url: &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;REDIS_URL&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;timeout: &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;REDIS_TIMEOUT&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;reconnect_attempts: &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;REDIS_RECONNECT_ATTEMPTS&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;reconnect_delay: &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;REDIS_RECONNECT_DELAY&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;reconnect_delay_max: &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;REDIS_RECONNECT_DELAY_MAX&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_f&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;If you are wanting to configure a Redis and use it across threads, &lt;a href=&quot;https://tejasbubane.github.io/posts/2020-04-22-redis-connection-pool-in-rails/&quot;&gt;using a Redis connection pool is recommended&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;language-ruby 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;n&quot;&gt;pool_size&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;RAILS_MAX_THREADS&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&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;n&quot;&gt;redis_pool&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ConnectionPool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;size: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pool_size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;Redis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;url: &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;REDIS_URL&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;timeout: &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;REDIS_TIMEOUT&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;reconnect_attempts: &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;REDIS_RECONNECT_ATTEMPTS&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;reconnect_delay: &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;REDIS_RECONNECT_DELAY&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;reconnect_delay_max: &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;REDIS_RECONNECT_DELAY_MAX&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_f&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Although this means when using it you need to grab a pool connection first&lt;/p&gt;

&lt;div class=&quot;language-ruby 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;# original style, which is deprecated and would block across threads&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Redis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;some_key&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# utilizing a pool&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;redis_pool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;some_key&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;thx &lt;a href=&quot;https://twitter.com/ericactripp&quot;&gt;@ericactripp&lt;/a&gt;, for sharing the link about connection pools&lt;/p&gt;

&lt;h1 id=&quot;redis-in-common-libraries&quot;&gt;Redis in Common Libraries&lt;/h1&gt;

&lt;p&gt;All the above helps when you are working with Redis directly, but often we are configuring common libraries with Redis, how many of them are able to leverage the same kinds of benifits like a connection pool?&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;sidekiq:&lt;/strong&gt; If you use sidekiq it already has a &lt;a href=&quot;https://github.com/mperham/sidekiq/blob/cf7b067c89ae3b1303e35d29408099cf40991f6d/lib/sidekiq/redis_connection.rb#L34&quot;&gt;connection pool under the hood&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;resque:&lt;/strong&gt; Currently, resque doesn’t support a connection pool… It does have away to avoid re-connecting for each worker by using &lt;a href=&quot;https://github.com/redis/redis-rb#expert-mode-options&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;inherit_socket: true&lt;/code&gt;&lt;/a&gt; as described as &lt;a href=&quot;https://github.com/resque/resque/issues/1254&quot;&gt;one option to avoid needing a connection pool&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;flipper:&lt;/strong&gt; The current &lt;a href=&quot;https://github.com/jnunemaker/flipper/blob/master/lib/flipper/adapters/redis.rb#L184&quot;&gt;flipper redis&lt;/a&gt;, is a single Redis connection, but creating a redis adapter that used a pool, looks like it would be pretty easy.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Rails.cache:&lt;/strong&gt; Rails redis cache you are likely using a single Redis instance, a shared redis connection, &lt;a href=&quot;https://api.rubyonrails.org/classes/ActiveSupport/Cache/ConnectionPoolLike.html&quot;&gt;support for connection pools was added&lt;/a&gt;, but folks &lt;a href=&quot;https://github.com/rails/rails/issues/39479&quot;&gt;need to opt into it&lt;/a&gt; with some additional options, see the example configuration below.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-ruby 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;# common config that won&apos;t leverage a redis connection pool&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;cache_store&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:redis_cache_store&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;ss&quot;&gt;url: &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;REDIS_URL&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;c1&quot;&gt;# by setting the pool side and timeout, you can leverage a connection pool with your Redis&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;cache_store&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:redis_cache_store&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;ss&quot;&gt;url: &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;REDIS_URL&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;pool_size: &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;pool_timeout: &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;6&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;h1 id=&quot;investigate-combining-redis-calls&quot;&gt;Investigate Combining Redis Calls&lt;/h1&gt;

&lt;p&gt;If you have an app that is making many sequential Redis calls, there is a good chance you could make a significant improvement by leveraging &lt;a href=&quot;https://redis.io/docs/manual/pipelining/&quot;&gt;Redis pipelining&lt;/a&gt; or Mget. I think that that the Flipper codebase is a great way to learn and see various Ruby techniques. It is high quality and has a wide adoption so you can trust it has been put through the paces. If you want to dig into combinging calls, &lt;a href=&quot;https://medium.com/@jychen7/redis-get-pipeline-vs-mget-6e41aeaecef&quot;&gt;read about the differences between pipeline and mget in terms of code and latency.&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/jnunemaker/flipper/commit/033bb20e288436d357f81002de7b2aa1d5bd7c18&quot;&gt;Flipper PR adding in pipelining&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/jnunemaker/flipper/blob/master/lib/flipper/adapters/redis_cache.rb#L147&quot;&gt;Flipper usage of Mget&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://api.rubyonrails.org/classes/ActiveSupport/Cache/RedisCacheStore.html#method-i-read_multi&quot;&gt;Leverage read_multi &amp;amp; write_multi though the Rails Cache Redis Adapter&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;pipeline-syntax-change&quot;&gt;Pipeline Syntax Change&lt;/h3&gt;

&lt;p&gt;As long as we are updating some of our calls, worth being aware of another depracation. “Pipelining commands on a Redis instance is deprecated and will be removed in Redis 5.0.0.”&lt;/p&gt;

&lt;div class=&quot;language-ruby 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;n&quot;&gt;redis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;pipelined&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;redis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;key&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# should be replaced by&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;redis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;pipelined&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pipeline&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;pipeline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;key&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;redis-usage-beyond-the-basics&quot;&gt;Redis Usage Beyond The Basics&lt;/h1&gt;

&lt;p&gt;If you are looking to do a bit more than the default &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rails.cache&lt;/code&gt; capabilities with Redis, you will find it supports a lot of powerful feature, and can along with pipelining be extremely performant. If you are looking to push for as performant as you can, setup &lt;a href=&quot;https://github.com/redis/hiredis-rb&quot;&gt;hiredis-rb&lt;/a&gt; as your connection for redis-rb. It uses C extensions to be as performant as possible. This post goes into some details where &lt;a href=&quot;https://www.keypup.io/blog/use-redis-sets-to-track-and-expire-cache-keys-in-rails&quot;&gt;direct caching wiht Redis&lt;/a&gt; can provide more powerful capabilities than using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rails.cache&lt;/code&gt;&lt;/p&gt;

&lt;h1 id=&quot;redis-cli&quot;&gt;Redis CLI&lt;/h1&gt;

&lt;p&gt;A few useful tips around using Redis and understanding how your application is using Redis.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brew install redis&lt;/code&gt;: install redis via homebrew&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brew uninstall redis&lt;/code&gt;: uninstall redis via homebrew&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brew info redis&lt;/code&gt;: Get info on currently installed redis&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;redis-cli ping&lt;/code&gt;: Check if redis service is running&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;redis-cli monitor&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;redis-cli slowlog get 100&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;exciting-things-are-happening-with-ruby-redis&quot;&gt;Exciting Things Are Happening with Ruby Redis&lt;/h1&gt;

&lt;p&gt;A good deal of things will be changing in Redis-rb 5.0, we mentioned &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Redis.current&lt;/code&gt; and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;redis.pipelined&lt;/code&gt; changes. These changes and others help support a move to a simpler and faster redis-client under the hood.&lt;/p&gt;

&lt;p&gt;A move to simplify the &lt;a href=&quot;https://github.com/redis/redis-rb/issues/1070#issuecomment-1074094773&quot;&gt;redis-rb codebase and drop a mutex looks like it will roll out redis-client&lt;/a&gt;, which can significantly speed up some use cases. It looks like sidekiq for example with move to this in the near future.&lt;/p&gt;

&lt;blockquote class=&quot;twitter-tweet&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;New prototype Redis driver cuts Sidekiq job processing time from 61 sec to 39 sec. Incredible improvement! &lt;a href=&quot;https://t.co/ZzIw7koMiq&quot;&gt;https://t.co/ZzIw7koMiq&lt;/a&gt;&lt;/p&gt;&amp;mdash; Mike Perham 🇺🇦 (@getajobmike) &lt;a href=&quot;https://twitter.com/getajobmike/status/1506709040952356865?ref_src=twsrc%5Etfw&quot;&gt;March 23, 2022&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async=&quot;&quot; src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;

&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; Looks like that perf win was a bit to good to be true.&lt;/p&gt;

&lt;blockquote class=&quot;twitter-tweet&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Sadly I have to walk this back. Looks like there was a bug in the new driver and the performance is not significantly different. &lt;a href=&quot;https://t.co/2o7cgAUX0K&quot;&gt;https://t.co/2o7cgAUX0K&lt;/a&gt;&lt;/p&gt;&amp;mdash; Mike Perham 🇺🇦 (@getajobmike) &lt;a href=&quot;https://twitter.com/getajobmike/status/1508487013950918658?ref_src=twsrc%5Etfw&quot;&gt;March 28, 2022&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async=&quot;&quot; src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;

</content>
 </entry>
 
 <entry>
   <title>Micro-Service Request Depth Availability</title>
   <link href="https://www.mayerdan.com/sre/2022/01/18/micro-service-request-depth"/>
   <updated>2022-01-18T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/sre/2022/01/18/micro-service-request-depth</id>
   <content type="html">
&lt;h1 id=&quot;micro-service-request-depth-availability&quot;&gt;Micro-Service Request Depth Availability&lt;/h1&gt;

&lt;p&gt;In systems that use micro-services, often the growth and interaction of the services grow organically over time. While it is enabling teams to move quickly and integrate whatever they need it leads to some known bad patterns in terms of micro-service interactions that have serious impacts on availability. This post explains two of &lt;a href=&quot;https://akfpartners.com/growth-blog/microservices-for-breadth-libraries-for-depth&quot;&gt;micro-service integration anti-patterns&lt;/a&gt; calling them “The Mesh” (I prefer Distributed Monolith) and “Services in Depth”. In both, the issue is a single request into your system can fan out to many individual services both in breadth and depth. Most micro-service systems I have seen have a mix of both fan-outs in-breadth and deep lines of service depths in some cases.&lt;/p&gt;

&lt;h1 id=&quot;understand-the-implecations-of-deep-service-call-depth&quot;&gt;Understand the Implecations of Deep Service Call Depth&lt;/h1&gt;

&lt;p&gt;Let’s consider service depth, as the simpler version of the problem to reason about. When a team is investing going micro-services some breath and depth calls are to be expected but understanding what it means and how to consider the impacts in the designing of the system. Below we will consider that each application has an &lt;a href=&quot;https://sre.google/sre-book/embracing-risk/&quot;&gt;aggregate request availability&lt;/a&gt; of 99.9%.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What does the request success rate look like for a request with 6 micro-service call depth?&lt;/strong&gt;&lt;/p&gt;

&lt;pre&gt;
&amp;gt; (0.999) * 100
=&amp;gt; 99.9
&amp;gt; (0.999 * 0.999) * 100
=&amp;gt; 99.8001
&amp;gt; (0.999 * 0.999 * 0.999) * 100
=&amp;gt; 99.7002999
&amp;gt; (0.999 * 0.999 * 0.999 * 0.999) * 100
=&amp;gt; 99.6005996001
&amp;gt; (0.999 * 0.999 * 0.999 * 0.999 * 0.999) * 100
=&amp;gt; 99.5009990004999
&amp;gt; (0.999 * 0.999 * 0.999 * 0.999 * 0.999 * 0.999) * 100
=&amp;gt; 99.4014980014994
&lt;/pre&gt;

&lt;blockquote&gt;
  &lt;p&gt;Assuming each service has an aggregate availability of 99.9%, A service call depth of 6 has a request availability of %99.4&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is likely a lot lower than teams expected. Also, often depending on your infrastructure it is a lot easier to stack up to six network calls than you may think. Also, while I am not covering the impact to latency in this post, understand it will have a large and negative impact on latency for a deep request call stack.&lt;/p&gt;

&lt;h1 id=&quot;visualizing-the-request-failure-rate&quot;&gt;Visualizing The Request Failure Rate&lt;/h1&gt;

&lt;p&gt;A nice way to think about the combined success rate is by thinking of each network hop as having a small opportunity for failure. These failure threads peel off as requests navigate the micro-service call stack. As the complexity of the network communications increases and the call depth deepens, the likelihood of failure increases as well. This would include things like your load balancer, DBs, App servers, and application caches.&lt;/p&gt;

&lt;div style=&quot;margin-left: -300px&quot;&gt;
&lt;style&gt;
  .node rect {
    fill-opacity: .9;
    shape-rendering: crispEdges;
  }
  
  .node text {
    pointer-events: none;
    text-shadow: 0 1px 0 #fff;
  }
  
  .link {
    fill: none;
    stroke: #000;
    stroke-opacity: .2;
  }
  
  .link:hover {
    stroke-opacity: .5;
  }
&lt;/style&gt;
&lt;script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.12.0/lodash.min.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;

&lt;div id=&quot;micro_service_fail-customContent&quot; style=&quot;visibility: hidden; position: fixed; top: 30%; width: 1100px; height: 60%; min-height: 480px; background-color:white; border: 1px solid; border-radius: 5px; padding: 10px;&quot;&gt;
  &lt;div id=&quot;micro_service_fail-displayContent&quot; style=&quot;padding: 10px;&quot;&gt;
  &lt;/div&gt;
  &lt;button id=&quot;micro_service_fail-close-btn&quot; class=&quot;px-4 py-2 bg-red-500 text-white text-base font-medium rounded-md w-full shadow-sm hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-green-300&quot;&gt;
    close
  &lt;/button&gt;
&lt;/div&gt;
&lt;p id=&quot;micro_service_fail&quot;&gt;&lt;/p&gt;  
&lt;script type=&quot;text/javascript&quot; src=&quot;/visualization/micro_service_request_failures.js?cache=16&quot;&gt;&lt;/script&gt;

&lt;/div&gt;
&lt;blockquote&gt;
  &lt;p&gt;Each network hop is an opportunity for failure, in the above showing 7 failure opportunities&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;request-depth-failure-trends&quot;&gt;Request Depth Failure Trends&lt;/h1&gt;

&lt;p&gt;Another way to visualize this is just a simple bar chart showing a decline of expected availability as service depth grows.&lt;/p&gt;

&lt;div style=&quot;margin-left: -200px&quot;&gt;
&lt;!-- Load d3.js --&gt;
&lt;script src=&quot;https://d3js.org/d3.v4.js&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.12.0/lodash.min.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;
&lt;div id=&quot;my_dataviz&quot;&gt;&lt;/div&gt;
&lt;script&gt;
  // set the dimensions and margins of the graph
  var margin = {top: 30, right: 30, bottom: 70, left: 60},
      width = 1000 - margin.left - margin.right,
      height = 400 - margin.top - margin.bottom;
  
  // append the svg object to the body of the page
  var svg = d3.select(&quot;#my_dataviz&quot;)
    .append(&quot;svg&quot;)
      .attr(&quot;width&quot;, width + margin.left + margin.right)
      .attr(&quot;height&quot;, height + margin.top + margin.bottom)
    .append(&quot;g&quot;)
      .attr(&quot;transform&quot;,
            &quot;translate(&quot; + margin.left + &quot;,&quot; + margin.top + &quot;)&quot;);
  
  // number of services
  var serviceStart = 1;
  var serviceEnd = 15;

  var data = [];

  while(serviceStart &lt; serviceEnd+1){
    data.push({ Services: serviceStart, Value: Math.pow(0.999,serviceStart) * 100.0});
    serviceStart++;
  }

  // sort data
  data.sort(function(b, a) {
    return a.Value - b.Value;
  });

  // X axis
  var x = d3.scaleBand()
    .range([ 0, width ])
    .domain(data.map(function(d) { return d.Services; }))
    .padding(0.2);
  svg.append(&quot;g&quot;)
    .attr(&quot;transform&quot;, &quot;translate(0,&quot; + height + &quot;)&quot;)
    .call(d3.axisBottom(x))
    .selectAll(&quot;text&quot;)
      .attr(&quot;transform&quot;, &quot;translate(-10,0)rotate(-45)&quot;)
      .style(&quot;text-anchor&quot;, &quot;end&quot;);

  // Add Y axis
  var y = d3.scaleLinear()
    .domain([95, 100])
    .range([ height, 0]);
  svg.append(&quot;g&quot;)
    .call(d3.axisLeft(y));

  // ----------------
  // Create a tooltip
  // https://www.d3-graph-gallery.com/graph/barplot_stacked_hover.html
  // ----------------
  var tooltip = d3.select(&quot;#my_dataviz&quot;)
    .append(&quot;div&quot;)
    .style(&quot;opacity&quot;, 0)
    .attr(&quot;class&quot;, &quot;tooltip&quot;)
    .style(&quot;background-color&quot;, &quot;white&quot;)
    .style(&quot;border&quot;, &quot;solid&quot;)
    .style(&quot;border-width&quot;, &quot;1px&quot;)
    .style(&quot;border-radius&quot;, &quot;5px&quot;)
    .style(&quot;padding&quot;, &quot;10px&quot;)

  // Three function that change the tooltip when user hover / move / leave a cell
  var mouseover = function(d) {
    var subgroupName = d.Services;
    console.log(d);
    var subgroupValue = d.Value;
    tooltip
        .html(&quot;Number Of Services: &quot; + subgroupName + &quot;&lt;br&gt;&quot; + &quot;Max Theoretical Availability: &quot; + subgroupValue)
        .style(&quot;opacity&quot;, 1)
  }
  var mousemove = function(d) {
    tooltip
      .style(&quot;left&quot;, (d3.mouse(this)[0]+90) + &quot;px&quot;) // It is important to put the +90: other wise the tooltip is exactly where the point is an it creates a weird effect
      .style(&quot;top&quot;, (d3.mouse(this)[1]) + &quot;px&quot;)
  }
  var mouseleave = function(d) {
    tooltip
      .style(&quot;opacity&quot;, 0)
  }  

  // Bars
  svg.selectAll(&quot;mybar&quot;)
    .data(data)
    .enter()
    .append(&quot;rect&quot;)
      .attr(&quot;x&quot;, function(d) { return x(d.Services); })
      .attr(&quot;y&quot;, function(d) { return y(d.Value); })
      .attr(&quot;width&quot;, x.bandwidth())
      .attr(&quot;height&quot;, function(d) { return height - y(d.Value); })
      .attr(&quot;fill&quot;, &quot;#69b3a2&quot;)
      .on(&quot;mouseover&quot;, mouseover)
      .on(&quot;mousemove&quot;, mousemove)
      .on(&quot;mouseleave&quot;, mouseleave)
  
  &lt;/script&gt;
&lt;/div&gt;

&lt;h1 id=&quot;micro-service-request-availability-calculator&quot;&gt;Micro-Service Request Availability Calculator&lt;/h1&gt;

&lt;p&gt;The below calculator will let you quickly estimate your theoretical availability based on the estimated SLA across multiple service calls. Consider each part of your infrastructure (CDNs, load balancers, DBs, Caches) as well as the total services involved in a successful response to a request.&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;div&gt;
&lt;style&gt;
&lt;/style&gt;

&lt;div id=&quot;micro_service_calculator&quot; class=&quot;w-full max-w-xs&quot;&gt;
  &lt;form class=&quot;bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4&quot;&gt;
    &lt;div class=&quot;mb-4&quot;&gt;
      &lt;label class=&quot;block text-gray-700 text-sm font-bold mb-2&quot; for=&quot;Service Count&quot;&gt;
        Service Count
      &lt;/label&gt;
      &lt;input class=&quot;shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline&quot; id=&quot;service_count&quot; type=&quot;text&quot; placeholder=&quot;10&quot; /&gt;
    &lt;/div&gt;
    &lt;div class=&quot;mb-4&quot;&gt;
      &lt;label class=&quot;block text-gray-700 text-sm font-bold mb-2&quot; for=&quot;Service Count&quot;&gt;
        Service Aggregate Availability
      &lt;/label&gt;
      &lt;input class=&quot;shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline&quot; id=&quot;service_avail&quot; type=&quot;text&quot; placeholder=&quot;99.9&quot; /&gt;
    &lt;/div&gt;
    &lt;div class=&quot;flex items-center justify-between&quot;&gt;
      &lt;button id=&quot;service_calculator&quot; class=&quot;bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline&quot; type=&quot;button&quot;&gt;
        Caclulate
      &lt;/button&gt;
    &lt;/div&gt;
    &lt;div class=&quot;mb-4&quot;&gt;
      &lt;label class=&quot;block text-gray-700 text-sm font-bold mb-2&quot; for=&quot;Avail Result&quot;&gt;
        Combined Multi-Service Aggregate Availability
      &lt;/label&gt;
      &lt;input class=&quot;shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline&quot; id=&quot;avail_result&quot; type=&quot;text&quot; placeholder=&quot;N/A&quot; /&gt;
    &lt;/div&gt;
  &lt;/form&gt;
  &lt;p class=&quot;text-center text-gray-500 text-xs&quot;&gt;
    Micro-Service Call Depth Availibility Calculator
  &lt;/p&gt;
&lt;/div&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;/visualization/micro_service_request_calculator.js?cache=2&quot;&gt;&lt;/script&gt;

&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h2 id=&quot;aws-slas&quot;&gt;AWS SLAs&lt;/h2&gt;

&lt;p&gt;So far we have been talking about micro-services and their availability, we should consider the services often used to host our application code into the cloud. A very popular cloud for hosting service is AWS, which publishes &lt;a href=&quot;https://aws.amazon.com/legal/service-level-agreements/?aws-sla-cards.sort-by=item.additionalFields.serviceNameLower&amp;amp;aws-sla-cards.sort-order=asc&amp;amp;awsf.tech-category-filter=*all&quot;&gt;all AWS SLAs&lt;/a&gt;. Let’s look at this from the perspective of a typical AWS application, assuming a single app stack (no micro-services), a pretty standard setup, and assuming the app code has a runtime SLA of 99.9%, the combined math leaves a total theoretical max request success expectation of %99.6. If you are building something that needs extremely high reliability, &lt;a href=&quot;https://cloudonaut.io/aws-sla-are-you-able-to-keep-your-availability-promise/&quot;&gt;are you able to keep availability promises?&lt;/a&gt;]&lt;/p&gt;

&lt;p&gt;You can see as you stack AWS services, regardless of your application stability, request success percentage decreases…&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Service&lt;/th&gt;
      &lt;th&gt;SLA&lt;/th&gt;
      &lt;th&gt;% Success Math&lt;/th&gt;
      &lt;th&gt;Request % Success&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;cloudfront&lt;/td&gt;
      &lt;td&gt;&lt;a href=&quot;https://aws.amazon.com/cloudfront/sla/&quot;&gt;99.9&lt;/a&gt;&lt;/td&gt;
      &lt;td&gt;0.999&lt;/td&gt;
      &lt;td&gt;99.9%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;ALB&lt;/td&gt;
      &lt;td&gt;&lt;a href=&quot;https://aws.amazon.com/elasticloadbalancing/sla/&quot;&gt;99.99&lt;/a&gt;&lt;/td&gt;
      &lt;td&gt;0.999 * 0.9999&lt;/td&gt;
      &lt;td&gt;99.89%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;ECS&lt;/td&gt;
      &lt;td&gt;&lt;a href=&quot;https://aws.amazon.com/compute/sla/&quot;&gt;99.99&lt;/a&gt;&lt;/td&gt;
      &lt;td&gt;0.999 * 0.9999 * 0.9999&lt;/td&gt;
      &lt;td&gt;99.88%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Custom App&lt;/td&gt;
      &lt;td&gt;99.9&lt;/td&gt;
      &lt;td&gt;0.999 * 0.9999 * 0.9999 * 0.999&lt;/td&gt;
      &lt;td&gt;99.78%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Elasticache&lt;/td&gt;
      &lt;td&gt;&lt;a href=&quot;https://aws.amazon.com/elasticache/sla/&quot;&gt;99.9&lt;/a&gt;&lt;/td&gt;
      &lt;td&gt;0.999 * 0.9999 * 0.9999 * 0.999 * 0.999&lt;/td&gt;
      &lt;td&gt;99.68%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;RDS (Postgres)&lt;/td&gt;
      &lt;td&gt;&lt;a href=&quot;https://aws.amazon.com/rds/sla/&quot;&gt;99.95&lt;/a&gt;&lt;/td&gt;
      &lt;td&gt;0.999 * 0.9999 * 0.9999 * 0.999 * 0.999 * 0.9995&lt;/td&gt;
      &lt;td&gt;99.63%&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h1 id=&quot;mitigations--considerations&quot;&gt;Mitigations / Considerations&lt;/h1&gt;

&lt;p&gt;When you realize as the system scales and grows and the number of total microservice dependencies a typical request into your system may have, it is worth thinking about and considering some mitigation strategies. Opposed to pushing towards &lt;a href=&quot;https://www.continuitycentral.com/feature0267.htm&quot;&gt;Five Nines: Chasing The Dream?&lt;/a&gt;, embrace failure and resilience, find an acceptable and achievable level of availability for your service. Then invest in mitigation techniques and strategies to deliver a reliable client experience on unreliable internet. A few examples of mitigations are listed below.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Client Side Retries: A good reason to have client-side retries and avoid implementing retries at all levels of the infrastructure (some other special cases may make sense to avoid full round trips). See Google’s SRE book, sections &lt;a href=&quot;https://sre.google/sre-book/handling-overload/&quot;&gt;Client-Side Throttling&lt;/a&gt; and &lt;a href=&quot;https://sre.google/sre-book/handling-overload/&quot;&gt;Deciding to Retry&lt;/a&gt; from the Handling Overload chapter.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Be Wary of Circular Graphs: Detect circular graphs, even if this can technically be supported in your infrastructure, it may be best to avoid as a way to force folks to think through more robust and scalable solutions.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;Avoid Duplicate Service Calls: This happens when you might have a very common piece of data that a service calls, before you know it all your micro-services call this in high demand service. You might have an initial request fan out to 3 micro-services that all call this common data service under the hood. This often happens for something like user data.
    &lt;ul&gt;
      &lt;li&gt;Consider common data and look at data forwarding, which can early in the request processing add metadata that is sent to all upstream requests. Avoiding all upstream requests from making individual network requests for the data.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Agree on Constraints: Consider alerting on requests that exceed an agreed maximum service call depth or circular call graphs.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h1&gt;

&lt;p&gt;There are a lot of benefits of microservices, but I feel like the expectations around reliability and latency are often overlooked when folks move from a larger shared codebase and adopt microservices. The companies are looking for faster deployments and teams that move independently and do not fully grasp that they slowly have turned every method call or DB join into a remote network request with all the failures and performance characteristics that come with it.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Revisiting Front-Ends</title>
   <link href="https://www.mayerdan.com/javascript/2022/01/08/revisiting-front-ends"/>
   <updated>2022-01-08T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/javascript/2022/01/08/revisiting-front-ends</id>
   <content type="html">
&lt;h1 id=&quot;revisiting-front-ends&quot;&gt;Revisiting Front-Ends&lt;/h1&gt;

&lt;p&gt;I have been a ‘full-stack’ developer for a long time. These days depending on where you work and how your org works being full-stack isn’t really viable anymore. Given the growing complexity of both the front-end and back-end end systems, it is more and more required to specialize. That being said, I feel like there are good reasons to understand and think across the front-end boundaries. For example, if you care about user performance how you design backend APIs and deploy front ends can have a massive impact. From fully supporting and leveraging CDNs, pre-fetching, cached API-queries, and more. Anyways, as my front-end skills fell further behind and some exciting changes have been occurring in the front-end world it was a good time to spend a little time refreshing my knowledge and sharpening my tools.&lt;/p&gt;

&lt;h1 id=&quot;where-i-started&quot;&gt;Where I started&lt;/h1&gt;

&lt;p&gt;I decided to look at a couple of different projects and ways to explore the space. This hasn’t brought me fully up to speed with the amazing front-end folks I work with, but I have learned a lot and enjoyed working in a bit more visual space.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;I worked on a side project modernizing its look and design updating &lt;a href=&quot;https://semantic-ui.com/&quot;&gt;Semantic-UI&lt;/a&gt;/&lt;a href=&quot;https://fomantic-ui.com/&quot;&gt;Fomantic-UI&lt;/a&gt; designs.&lt;/li&gt;
  &lt;li&gt;I picked up, &lt;a href=&quot;https://pragprog.com/titles/nrclient/modern-front-end-development-for-rails/&quot;&gt;Modern Front-End Development for Rails&lt;/a&gt; and worked through some exercises.&lt;/li&gt;
  &lt;li&gt;I dug into CDNs, caching, and compression options (&lt;a href=&quot;https://github.com/google/brotli&quot;&gt;Brotli&lt;/a&gt;)… modernizing our setup at work&lt;/li&gt;
  &lt;li&gt;I helped automate and setup &lt;a href=&quot;https://github.com/GoogleChrome/lighthouse-ci&quot;&gt;lighthouse&lt;/a&gt; tracking on projects at work, and fixed some low hanging fruit&lt;/li&gt;
  &lt;li&gt;I played around with &lt;a href=&quot;https://hotwired.dev/&quot;&gt;hotwire&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;I picked up &lt;a href=&quot;https://tailwindcss.com/&quot;&gt;Tailwind CSS&lt;/a&gt; for a few toy projects&lt;/li&gt;
  &lt;li&gt;I converted my blog to Tailwind CSS from an old customized Twitter bootstrap theme&lt;/li&gt;
  &lt;li&gt;I started working on some visualizations in &lt;a href=&quot;https://d3js.org/&quot;&gt;D3.js&lt;/a&gt;, diagramming network traffic, failure rates, and org/system structures.&lt;/li&gt;
  &lt;li&gt;I built a presentation as a Tailwind / D3 microsite vs a slide show&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some of the next things coming up?&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;I will likely convert over the Semantic-UI/Fomantic-UI site to Tailwind&lt;/li&gt;
  &lt;li&gt;I am helping support our frontend team on efforts to decouple our Front End deployment from Rails&lt;/li&gt;
  &lt;li&gt;I will be digging into our custom react design system a bit more at work and porting over a few pages to it* Adding some more visualizations directly to this blog&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;tailwind&quot;&gt;Tailwind&lt;/h1&gt;

&lt;p&gt;After trying out a few frameworks including a bit of a deep dive on &lt;a href=&quot;https://semantic-ui.com/&quot;&gt;Semantic-UI&lt;/a&gt;/&lt;a href=&quot;https://fomantic-ui.com/&quot;&gt;Fomantic-UI&lt;/a&gt;. I wasn’t satisfied, the growing buzz around Tailwind pulled me in. I still have a lot to learn and a ways to go, but it is better matching my needs/desires for front-end support than anything has in a long time. As I played with Tailwind, I needed a few projects to drive a bit more real-world usage.&lt;/p&gt;

&lt;h2 id=&quot;converting-the-blog-to-tailwind&quot;&gt;Converting the Blog to Tailwind&lt;/h2&gt;

&lt;p&gt;This blog you are reading moved from Twitter Bootstrap now supports Purge CSS for the various pages, has Tailwind layouts, templates, and dev support. Nothing too complicated, but I feel it looks much better than it did. Simpler header, more readable font / white space. Dropped most of the sidebar, etc… I had to reformat some of my Markdown and post tags, I &lt;a href=&quot;https://github.com/danmayer/danmayer.github.com/blob/main/Rakefile#L47&quot;&gt;wrote a conversion script&lt;/a&gt; to reformat my post history.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/tailwind_blog.png&quot; alt=&quot;Bootstrap vs Tailwind&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;tailwind-learning-sources&quot;&gt;Tailwind Learning Sources&lt;/h2&gt;

&lt;p&gt;There are a lot of great resources out there and I wanted to share a few. I have also been using Tailwind on some test Rails projects, so some of the links are more Rails -&amp;gt; Tailwind specific.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Learning &amp;amp; Exploring
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;https://play.tailwindcss.com/&quot;&gt;Tailwind Play&lt;/a&gt;, this real time configurable exploritory playground for Tailwind is a great way to quickly fool around with ideas and see results. I used this a lot before working on templates and files. You can also share ideas.&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://daily-dev-tips.com/posts/center-elements-with-tailwind-css/&quot;&gt;Center elements with Tailwind CSS&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://play.tailwindcss.com/MjQpzw58WW&quot;&gt;Tailwind breakout grid&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://tailwindcss.com/docs/preflight&quot;&gt;Tailwind preflight&lt;/a&gt;, I recommend understanding what preflight does and what it ‘resets’&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Rails &amp;amp; Tailwind
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;https://github.com/rails/tailwindcss-rails&quot;&gt;tailwindcss-rails&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://evilmartians.com/chronicles/set-up-tailwind-css-jit-in-a-rails-project-to-compile-styles-20x-faster&quot;&gt;Tailwind CSS JIT in a Rails project to compile styles 20x faster&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://v2.tailwindcss.com/docs/upgrading-to-v2#install-tailwind-css-v2-0-and-post-css-8&quot;&gt;Install Tailwind CSS v2.0 and PostCSS 8&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Tailwind Blogs / Jekyll (My templates are heavily based on some of the below, but customized a bit)
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;https://blog.frankdejonge.nl/setting-up-docs-with-tailwind-css-and-github-pages/&quot;&gt;Setting up docs with Tailwind CSS &amp;amp; GitHub Pages&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://github.com/frankdejonge/tailwind-docs-example&quot;&gt;Tailwind Docs&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://github.com/chrisrhymes/purpletual-blog-theme&quot;&gt;purpletual-blog-theme (Tailwind &amp;amp; Jekyll starter kit)&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://github.com/taylorbryant/jekyll-starter-tailwind&quot;&gt;Jekyll Starter Tailwind&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://www.zerostatic.io/theme/jekyll-atlantic/&quot;&gt;jekyll-atlantic theme&lt;/a&gt; - This is the base of my theme&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;other-learning-projects&quot;&gt;Other Learning Projects&lt;/h1&gt;

&lt;p&gt;Other than the blog, a few other examples from my recent front-end exploration exploration.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/coverband_Semantic-UI.png&quot; alt=&quot;Coverband Semantic&quot; /&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Coverband Web built in Semantic-UI&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div style=&quot;margin-left: -300px&quot;&gt;
&lt;style&gt;
  .node rect {
    fill-opacity: .9;
    shape-rendering: crispEdges;
  }
  
  .node text {
    pointer-events: none;
    text-shadow: 0 1px 0 #fff;
  }
  
  .link {
    fill: none;
    stroke: #000;
    stroke-opacity: .2;
  }
  
  .link:hover {
    stroke-opacity: .5;
  }
&lt;/style&gt;
&lt;script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.12.0/lodash.min.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;

&lt;div id=&quot;micro_service_fail-customContent&quot; style=&quot;visibility: hidden; position: fixed; top: 30%; width: 1100px; height: 60%; min-height: 480px; background-color:white; border: 1px solid; border-radius: 5px; padding: 10px;&quot;&gt;
  &lt;div id=&quot;micro_service_fail-displayContent&quot; style=&quot;padding: 10px;&quot;&gt;
  &lt;/div&gt;
  &lt;button id=&quot;micro_service_fail-close-btn&quot; class=&quot;px-4 py-2 bg-red-500 text-white text-base font-medium rounded-md w-full shadow-sm hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-green-300&quot;&gt;
    close
  &lt;/button&gt;
&lt;/div&gt;
&lt;p id=&quot;micro_service_fail&quot;&gt;&lt;/p&gt;  
&lt;script type=&quot;text/javascript&quot; src=&quot;/visualization/micro_service_request_failures.js?cache=16&quot;&gt;&lt;/script&gt;

&lt;/div&gt;

&lt;blockquote&gt;
  &lt;p&gt;D3 Network request flow chart&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/org_preview.png&quot; alt=&quot;Org / System Relationships&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;Presentation breaking down org charts, team / system relationships, and network request flows (D3 and Tailwind)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/max_sla.png&quot; alt=&quot;D3 SLA Chart&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;D3 Request Max SLA Calculator (interactive visualization)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;final-thoughts&quot;&gt;Final Thoughts&lt;/h1&gt;

&lt;p&gt;It is good to revisit and resharpen skills in an area even if you aren’t planning to be an expert. While I don’t really do full-stack work in my daily workflow anymore, I am often heavily influencing our system design and architecture as it relates to microservices, mobile, and the future of our front-ends. I want to ensure I am still looking closely enough to know what questions to ask and understand when folks are sharing ideas and concerns… I want and need to know the landscape, so to speak, including some of the toolchains, pain points, and benefits over older styles of development. A quick bit of focused exploratory work can help one stay fresh while also not slowing down or getting in the way of the experts doing the real front-end work where I work. I am able to be a better and more capable partner in discussions and designs.&lt;/p&gt;

&lt;p&gt;As I continue learning more about Tailwind and Visualization tools, please share any good links with me.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Principal / Staff Engineer Resources</title>
   <link href="https://www.mayerdan.com/career/2022/01/04/principal-engineer-resources"/>
   <updated>2022-01-04T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/career/2022/01/04/principal-engineer-resources</id>
   <content type="html">
&lt;h1 id=&quot;principal--staff-engineer-resources&quot;&gt;Principal / Staff Engineer Resources&lt;/h1&gt;

&lt;p&gt;A friend who recently was promoted to a Staff Engineer, and wanted to learn a bit more about the role asked me if I had any good resources. I put together a small set of resources that could be helpful when aspiring or transitioning into Principal or Staff level engineering roles. I figured it would be good to share out with others who might be interested. I broke up the resources into a few different categories, so folks can dig in where relevant. It is worth noting that higher-level technical roles have a lot of variance between companies. Folks will find that they can often shape the role or find a company that defines the role in a way that will be interesting to them. Don’t feel like you need to fit into a tightly defined box, long as you are providing high-level value, there is a place for you to grow and apply your technical experience. You will hear of staff engineers who never code, while others with the same title still consider software development a core part of their job responsibilities. Whatever way you want to grow and increase the value of your work there is always more to learn as you expand your technical software career and look to have a broader impact on software development.&lt;/p&gt;

&lt;h1 id=&quot;staff-and-leadership-resources&quot;&gt;Staff and Leadership Resources&lt;/h1&gt;

&lt;p&gt;A few things actually focus on this narrow niche of technology roles. Overall, these are all excellent resources that I highly recommended.
This list includes sources that continually create and add new content.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://staffeng.com/&quot;&gt;Staff Eng&lt;/a&gt; - easily the best community and set of materials out there for learning more at the top of the technical track.
    &lt;ul&gt;
      &lt;li&gt;a &lt;a href=&quot;https://staffeng.com/&quot;&gt;newsletter&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;a book, &lt;a href=&quot;https://staffeng.com/book&quot;&gt;Staff Engineer: Leadership beyond the management track&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;a podcast, &lt;a href=&quot;https://podcast.staffeng.com/&quot;&gt;Staff Eng Podcast&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;a list of &lt;a href=&quot;https://staffeng.com/guides/learning-materials&quot;&gt;staff engineer learning resources&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://softwareleadweekly.com/&quot;&gt;Software Lead Weekly&lt;/a&gt; - email newsletter with great links of posts being discussed in the community.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://leaddev.com/staffpluslive&quot;&gt;LeadDev StaffPlus&lt;/a&gt; - LeadDev runs some of the best advanced eng confrences. Staff Plus, is the best conf that touches on all the cross functional aspects that you will be involved with as you advance in a technical career.
    &lt;ul&gt;
      &lt;li&gt;A lot of &lt;a href=&quot;https://www.youtube.com/watch?v=7-ELnjgbS-o&quot;&gt;past material&lt;/a&gt; is &lt;a href=&quot;https://leaddev.com/events/influencing-without-management-authority-senior-individual-contributor&quot;&gt;turned into articles, videos, and other online content&lt;/a&gt;.&lt;/li&gt;
      &lt;li&gt;See my &lt;a href=&quot;https://www.mayerdan.com/programming/2018/07/18/lead-dev-conf-london&quot;&gt;Lead Dev London Summary&lt;/a&gt; to get a better idea for the feel.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.ctoconnection.com/&quot;&gt;CTO Connection&lt;/a&gt; - Community, confs, newsletter for leaders. Frequent interviews and videos shared via email updates. This is more management focused then the others. It also has been a bit more hit and miss, and seems to have been less relavent for me lately.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;staff-and-leadership-articles&quot;&gt;Staff and Leadership Articles&lt;/h2&gt;

&lt;p&gt;There are many good articles as well, but obviously these are more of one offs.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://keavy.com/work/thriving-on-the-technical-leadership-path/&quot;&gt;Thriving on the Technical Leadership Path&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.coinbase.com/on-becoming-a-senior-technical-leader-14106f1383b8&quot;&gt;Becoming a Sr Technical Leader&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://skamille.medium.com/an-incomplete-list-of-skills-senior-engineers-need-beyond-coding-8ed4a521b29f&quot;&gt;An incomplete list of skills senior engineers need, beyond coding&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.jessfraz.com/post/defining-a-distinguished-engineer/&quot;&gt;Defining a Distinguished Engineer&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://twitter.com/norootcause/status/1475518692041510915?t=5Ee9lvL9tC269L8LyIuigw&amp;amp;s=19&quot;&gt;A Thread on Sr. Software Skills&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;keeping-up-with-tech-in-general&quot;&gt;Keeping Up with Tech in General&lt;/h1&gt;

&lt;p&gt;As you are shaping technology decisions that can have impacts for years, plan to try to keep up on some of what is happening with the industry in general. Find sources that relate to your field, attend conferences, read newsletters, or listen to podcasts. Figure out the best way for you to low effort keep your ear to the ground. A few resources, I have enjoyed.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://changelog.com/podcast&quot;&gt;The Changelog podcast, excellent discussions with engineers&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Leading From Incidents, &lt;a href=&quot;https://neverworkintheory.org/&quot;&gt;It Will Never Work in Theory&lt;/a&gt; - Short summaries of recent results in empirical software engineering research&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.greaterthancode.com/&quot;&gt;Greater Than Code Podcast&lt;/a&gt; - A podcast that is more about the who, why, and community… vs the technical how.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;distributed-team-resources&quot;&gt;Distributed Team Resources&lt;/h1&gt;

&lt;p&gt;My friend and I both work on remote-first teams. A few resources around remote work, and how to lead tech outside of centralized teams.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;First, if you are working remote long term, it is worth getting a good &lt;a href=&quot;https://olivierlacan.com/posts/high-fidelity-remote-communication/&quot;&gt;remote audio/video setup&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;I don’t think top down leadership scales particularly well for software, I think it is even less effective when distributed. &lt;a href=&quot;https://martinfowler.com/articles/scaling-architecture-conversationally.html&quot;&gt;Scaling Architecture Conversationally&lt;/a&gt; sets out a number of approaches to encourage a more team sourced achitecture with guidance.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://works.hashicorp.com/&quot;&gt;Hashicorp: Distributed, Async, and Document Driven&lt;/a&gt; - learn from how Hashicorp builds distributed software&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.amazon.com/Remote-Office-Required-Jason-Fried-ebook&quot;&gt;Remote&lt;/a&gt;, while this company seemed to go off the Rails (pun intended) and loose a lot of the good will they had in the remote community, much of the advice can still be useful.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;books&quot;&gt;Books&lt;/h1&gt;

&lt;p&gt;Although there are lots of great posts, talks, and threads about technology leadership, sometimes nothing can give the big picture and the depth of a book. The shortlist of books below, stand out in my mind as worth the time.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.amazon.com/Accelerate-Software-Performing-Technology-Organizations-ebook/dp/B07B9F83WM/ref=tmm_kin_swatch_0?_encoding=UTF8&amp;amp;qid=&amp;amp;sr=&quot;&gt;Accelerate&lt;/a&gt; - If you want to shape how a software org functions, ensure you are basing it on research backed successes. This book summaries what is actually working best based on research.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.amazon.com/First-Days-Updated-Expanded-Strategies-ebook/dp/B00B6U63ZE/&quot;&gt;The First 90 days&lt;/a&gt; - While I highly disagree with a few things in this book, there is enough that is actionable and will help you have an impact as well as understand what is motivating other new leaders, that it is worth reading.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.amazon.com/Managers-Path-Leaders-Navigating-Growth-ebook/dp/B06XP3GJ7F/&quot;&gt;The Manager’s Path&lt;/a&gt; - Even if you are looking to stay on the Staff / technical track many parts of this book will help you grow with parts of your job.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.amazon.com/Working-Backwards-Insights-Stories-Secrets-ebook/dp/B08BYCQBZN&quot;&gt;Working Backwards&lt;/a&gt; - Another book I don’t agree or like all the recommendations, but has enough practical and actionable advice on how Amazon scaled and managed to stay agile with massive growth, that it is worth reading.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.amazon.com/Designing-Data-Intensive-Applications-Reliable-Maintainable-ebook/dp/B06XPJML5D/&quot;&gt;Designing Data-Intensive Applications: The Big Ideas Behind Reliable, Scalable, and Maintainable Systems&lt;/a&gt; - Even if you aren’t handling big data (yet), this book will let you understand when and why to reach for distributed systems and tools.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.amazon.com/Domain-Driven-Design-Tackling-Complexity-Software-ebook-dp-B00794TAUG/dp/B00794TAUG/ref=mt_other?_encoding=UTF8&amp;amp;me=&amp;amp;qid=1640219028&quot;&gt;Domain Driven Design&lt;/a&gt; - One of the most important things while scaling and hardest to fix later is bad domain modeling, deeply learn about building good data models.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;ruby-specific&quot;&gt;Ruby Specific&lt;/h1&gt;

&lt;p&gt;I am mostly operating in the Ruby world, and my friend is also at a Ruby-based company, so some Ruby-specific resources. As you grow as a tech leader in some sense you will care less about a specific technology, but there are reasons to keep an ear to the ground for the technologies your business has invested so much in. As I have grown my career, I have found it valuable to stay up to date with things going on in the community. If your company is invested in Ruby it means your hiring, training, operations, and infrastructure costs are all related to Ruby, ensure you are using it at its best.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://rubyweekly.com/&quot;&gt;Ruby Weekly&lt;/a&gt; - easiest way to see the most discussed articles in and around the Ruby community&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://rubyflow.com/&quot;&gt;RubyFlow&lt;/a&gt; - a more techy stream of Ruby links and project updates&lt;/li&gt;
  &lt;li&gt;Speedshop - a few great ways to soak in Ruby operational and performance tips &lt;a href=&quot;https://www.speedshop.co/blog/&quot;&gt;Rails performance Blog&lt;/a&gt;, &lt;a href=&quot;https://speedshop.us11.list-manage.com/subscribe?u=1aa0f43522f6d9ef96d1c5d6f&amp;amp;id=840412962b&quot;&gt;Ruby Performance Newsletter&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.youtube.com/channel/UCpFXaEz3vKJ45XjW5m5pbow&quot;&gt;Ruby Central Youtube channel&lt;/a&gt; - various confrence talks&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.youtube.com/results?search_query=confreaks+ruby&quot;&gt;Ruby Confreaks Recordings&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://groups.google.com/g/rubyonrails-security&quot;&gt;Rails Security Email List&lt;/a&gt; - ensure you see critical security issue announcements&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://sustainable-rails.com/&quot;&gt;Sustainable Web Development with Ruby on Rails&lt;/a&gt; - If you are currently struggling with scaling Rails as the dev team grows, this book should help guide ways to move forward that are less likely to come back to haunt you.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;enjoy-the-role&quot;&gt;Enjoy the Role&lt;/h1&gt;

&lt;p&gt;Good luck with the new role and growth. As always there are lots to learn, but there is a growing community out there to find friends and mentors and talk about what you want out of your career. The highly defined ladder is changing and roles are more malleable as we move to more hybrid and distributed ways of working. Feel free to explore and help shape the ways folks can lead in technology, it doesn’t have to be a path to being a manager, director, or VP in all cases anymore. It is an exciting time to be a technical learner and leader.&lt;/p&gt;

&lt;p&gt;If you have any good articles, sites, or books please share them with me, as I am always looking to learn more about what others in this area are doing.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Availability, Outages, Reliability, and SLAs</title>
   <link href="https://www.mayerdan.com/sre/2021/11/23/availability"/>
   <updated>2021-11-23T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/sre/2021/11/23/availability</id>
   <content type="html">
&lt;h2 id=&quot;todo&quot;&gt;TODO:&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Grammarly&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;availability-outages-reliability-and-slas&quot;&gt;Availability, Outages, Reliability, and SLAs&lt;/h1&gt;

&lt;p&gt;As companies mature, eventually downtime moves from speculative loss of revenue or customer growth, to directly impacting revenue and decreasing active customer trust. The tradeoffs of move fast and find market fit changes when a business shifts to driving revenue from a working business plan. This shifts the dynamics and companies working with processes and models that worked during early startup growth often struggle with the shift, understanding where new products and features can still learn and change fast, while more established critical systems require different approaches. In this post, I want to take a look at some of the different and related concerns around building reliable services.&lt;/p&gt;

&lt;h1 id=&quot;outages-and-uptime&quot;&gt;Outages and Uptime&lt;/h1&gt;

&lt;p&gt;When talking about reliability folks can mean different things… Measuring uptime in terms of total site availability. When talking about outages and uptime, folks generally mean the entire experience is available and aren’t concenred with invididual request level success. If the site is generally a bit slow and functions within normal bonds it is considered to be available and not suffering from an outage, even if the p99 is not looking good.&lt;/p&gt;

&lt;h1 id=&quot;planned-vs-unplanned-downtime&quot;&gt;Planned Vs Unplanned Downtime&lt;/h1&gt;

&lt;p&gt;In all the discussion below we are focused on unplanned downtime and the risk assement to a business and service. Planned downtime should be part of the plan and help with risk mitagation. It is built in and schedulable for nearly all cloud services, because one way to mitagate risk is controlling for planned downtime during the lowest impact to users and the business. Further discussion of planned downtime is out of scope of this post.&lt;/p&gt;

&lt;h1 id=&quot;why-not-just-build-for-high-availability-and-five-nines-from-the-start&quot;&gt;Why not just build for High Availability and Five Nines from the Start?&lt;/h1&gt;

&lt;p&gt;A quick note since some folks try to push for massive scale or availability far before it is needed. I recommend not not &lt;a href=&quot;https://www.continuitycentral.com/feature0267.htm&quot;&gt;Five nines: chasing the dream?&lt;/a&gt;, from the start. It doesn’t really work and it isn’t worth it, and most companies have learned that it isn’t worth the opportunity cost.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://sre.google/sre-book/embracing-risk/&quot;&gt;Google doesn’t do it&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://aws.amazon.com/legal/service-level-agreements/?aws-sla-cards.sort-by=item.additionalFields.serviceNameLower&amp;amp;aws-sla-cards.sort-order=asc&amp;amp;awsf.tech-category-filter=*all&quot;&gt;Amazon Doesn’t do it&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;If you build off cloud services, and if you build off their clouds you likely really aren’t either (we will cover this more later)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I have worked at a number of companies at various sizes and stages of growth, and seen companies grow to global scale while still never reaching high availability at a multi-region level. This isn’t to say you may not want to design for that level of resilience depending on your business, but you might be surpised at the number of companies that have gone public before needed to tackle that problem.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;users typically don’t notice the difference between high reliability and extreme reliability in a service, because the user experience is dominated by less reliable components like the cellular network or the device they are working with. Put simply, a user on a 99% reliable smartphone cannot tell the difference between 99.99% and 99.999% service reliability! –&lt;a href=&quot;https://sre.google/sre-book/embracing-risk/&quot;&gt;Google SRE Book&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;difference-between-outages-and-normal-operations&quot;&gt;Difference between Outages and Normal Operations&lt;/h1&gt;

&lt;p&gt;One thing to note is most of the time core services don’t have total outages anymore, but they do happen and there are ways to prepare for them… A more common day to day impact on users of your systems is operational efficiency in terms of both success rate and performance.&lt;/p&gt;

&lt;h3 id=&quot;total-infrastructure-outages&quot;&gt;Total Infrastructure Outages&lt;/h3&gt;

&lt;p&gt;Much of this is outside your control and you are dependent on your provides, but there are some patterns with providers or across multiple providers to mitgate incrustructure issues. The ability to change these more often lies with platform and infrastructure teams, but often comes with high cost, increased complexity, and little value outside of the risk mitigation. Having high availability around redundant CDN providers with DNS failovers as an example is an expensive solution to a problem that seems to occur every few years for CDN providers.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;AWS Cloudfront&lt;/li&gt;
  &lt;li&gt;AWS Region&lt;/li&gt;
  &lt;li&gt;AWS S3&lt;/li&gt;
  &lt;li&gt;Fastly outage&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This category, is where you might want to be thinking more in terms of &lt;strong&gt;Time-based availability&lt;/strong&gt; opposed to &lt;strong&gt;Aggregate availability&lt;/strong&gt; These are often rare occurances where massive systems fail and the only way to prepare for them is building multiple redundant systems ahead of time.&lt;/p&gt;

&lt;h3 id=&quot;total-application-services-outages&quot;&gt;Total Application Services Outages&lt;/h3&gt;

&lt;p&gt;These are system level issues that cause outages that are under your application, platform, and infrastructure engineers control. A number of stragies can be applied both at platform and application level to reduce risk&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;configuration change mistake&lt;/li&gt;
  &lt;li&gt;application code logic mistake&lt;/li&gt;
  &lt;li&gt;data store failure (DB migration mistake)&lt;/li&gt;
  &lt;li&gt;system overload&lt;/li&gt;
  &lt;li&gt;bad host issues&lt;/li&gt;
  &lt;li&gt;thundering herd&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This category, is where you might want to be thinking more in terms of &lt;strong&gt;Time-based availability&lt;/strong&gt;, as these types of outages aren’t just expected built in failure rates that all services should be built to handle as part of normal opperations, these are trigged by faulty changes, unexpected load, out of expectation infrastructure issues.&lt;/p&gt;

&lt;h3 id=&quot;total-3rd-party-integration-outages&quot;&gt;Total 3rd Party Integration Outages&lt;/h3&gt;

&lt;p&gt;You might have some 3rd parties that are in portions of your critical path… Similar to infrastructure outages you are slightly dependant on your partners, but unlike infrastructure most mitigations can be handled by Eng or Platform teams opposed to infrastructure teams. For example, if you offer multiple logins you should ensure a single auth provider can’t take down your login page or consume so many web workers while timing out that it is impossible to process other login sources successfully.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;braintree&lt;/li&gt;
  &lt;li&gt;paypay&lt;/li&gt;
  &lt;li&gt;stripe&lt;/li&gt;
  &lt;li&gt;FB Auth&lt;/li&gt;
  &lt;li&gt;Google Auth&lt;/li&gt;
  &lt;li&gt;Apple ID&lt;/li&gt;
  &lt;li&gt;authzero&lt;/li&gt;
  &lt;li&gt;onelogin&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;daily-service-quality&quot;&gt;Daily Service Quality&lt;/h3&gt;

&lt;p&gt;This is the impact of day to day expected success rates, latency, and failures that should be expected during normal operations. The effect of these issues is somehting your application teams have the ability to impact and control the outcomes of. Note: that all your third parties also have spikes in latency, error rates, and can for partial outages be mitigated by eng and platform teams.&lt;/p&gt;

&lt;h2 id=&quot;micro-service-slas&quot;&gt;Micro Service SLAs&lt;/h2&gt;

&lt;p&gt;The issue with SLAs is that folks often want to aim for extremly high SLAs like “five nines” 99.999% uptime… Companies also want high fully independent teams running and managing microservices…
Let’s take a look at how this plays out. Every time you add another service dependency you reduce the theoritical maximum SLA you could provide.&lt;/p&gt;

&lt;h2 id=&quot;basic-single-application-stack-request-flow&quot;&gt;Basic Single Application Stack Request Flow&lt;/h2&gt;

&lt;p&gt;In this example I am using Sankey diagrams to attempt to visualize the flow of a request from clients through an example infrastructure stack.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;A single request (like a request for an html page or single API request)&lt;/li&gt;
&lt;/ul&gt;

&lt;div&gt;
  &lt;script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.12.0/lodash.min.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;
&lt;style&gt;
  .node rect {
    cursor: move;
    fill-opacity: .9;
    shape-rendering: crispEdges;
  }
  
  .node text {
    pointer-events: none;
    text-shadow: 0 1px 0 #fff;
  }
  
  .link {
    fill: none;
    stroke: #000;
    stroke-opacity: .2;
  }
  
  .link:hover {
    stroke-opacity: .5;
  }
&lt;/style&gt; 
&lt;div id=&quot;div_customContent&quot; style=&quot;visibility: hidden; position: fixed; top: 30%; width: 1100px; height: 60%; background-color:white; border: 1px solid; border-radius: 5px; padding: 10px;&quot;&gt;
  &lt;div id=&quot;displayContent&quot; style=&quot;padding: 10px;&quot;&gt;
  &lt;/div&gt;
  &lt;button id=&quot;close-btn&quot; style=&quot;position: relative;&quot; class=&quot;px-4 py-2 bg-red-500 text-white text-base font-medium rounded-md w-full shadow-sm hover:bg-green-600 focus:outline-none focus:ring-2 focus:ring-green-300&quot;&gt;
    close
  &lt;/button&gt;
&lt;/div&gt;
&lt;p id=&quot;web_requests&quot;&gt;&lt;/p&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;/visualization/requests.js?cache=11&quot;&gt;&lt;/script&gt;
&lt;/div&gt;

&lt;h2 id=&quot;micro-service-requests&quot;&gt;Micro Service Requests&lt;/h2&gt;

&lt;p&gt;An Example of a Microservice Architecture.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Includes: a web front-end, API service, and common supporting services (DB and Redis Cache)&lt;/li&gt;
&lt;/ul&gt;

&lt;div&gt;
  &lt;style&gt;
  .node rect {
    fill-opacity: .9;
    shape-rendering: crispEdges;
  }
  
  .node text {
    pointer-events: none;
    text-shadow: 0 1px 0 #fff;
  }
  
  .link {
    fill: none;
    stroke: #000;
    stroke-opacity: .2;
  }
  
  .link:hover {
    stroke-opacity: .5;
  }
&lt;/style&gt;
&lt;script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.12.0/lodash.min.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;

&lt;div id=&quot;micro_service_fail-customContent&quot; style=&quot;visibility: hidden; position: fixed; top: 30%; width: 1100px; height: 60%; min-height: 480px; background-color:white; border: 1px solid; border-radius: 5px; padding: 10px;&quot;&gt;
  &lt;div id=&quot;micro_service_fail-displayContent&quot; style=&quot;padding: 10px;&quot;&gt;
  &lt;/div&gt;
  &lt;button id=&quot;micro_service_fail-close-btn&quot; class=&quot;px-4 py-2 bg-red-500 text-white text-base font-medium rounded-md w-full shadow-sm hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-green-300&quot;&gt;
    close
  &lt;/button&gt;
&lt;/div&gt;
&lt;p id=&quot;micro_service_fail&quot;&gt;&lt;/p&gt;  
&lt;script type=&quot;text/javascript&quot; src=&quot;/visualization/micro_service_request_failures.js?cache=16&quot;&gt;&lt;/script&gt;

&lt;/div&gt;

&lt;h2 id=&quot;microservice-slas&quot;&gt;Microservice SLAs&lt;/h2&gt;

&lt;p&gt;micro&lt;/p&gt;

&lt;pre&gt;
&amp;gt; (0.999) * 100
=&amp;gt; 99.9
&amp;gt; (0.999 * 0.999) * 100
=&amp;gt; 99.8001
&amp;gt; (0.999 * 0.999 * 0.999) * 100
=&amp;gt; 99.7002999
&amp;gt; (0.999 * 0.999 * 0.999 * 0.999) * 100
=&amp;gt; 99.6005996001
&amp;gt; (0.999 * 0.999 * 0.999 * 0.999 * 0.999) * 100
=&amp;gt; 99.5009990004999
&amp;gt; (0.999 * 0.999 * 0.999 * 0.999 * 0.999 * 0.999) * 100
=&amp;gt; 99.4014980014994
&lt;/pre&gt;

&lt;h2 id=&quot;aws-slas&quot;&gt;AWS SLAs&lt;/h2&gt;

&lt;p&gt;Let’s look at this from the perspective of a typical AWS applications, assuming a single app stack (no micro services), a pretty standard setup with app code of 99.9% still leaves a total SLA max of %99.6&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://aws.amazon.com/legal/service-level-agreements/?aws-sla-cards.sort-by=item.additionalFields.serviceNameLower&amp;amp;aws-sla-cards.sort-order=asc&amp;amp;awsf.tech-category-filter=*all&quot;&gt;AWS Service SLAs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://cloudonaut.io/aws-sla-are-you-able-to-keep-your-availability-promise/&quot;&gt;Are you able to keep availability promises?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;pre&gt;
cloudfront   ALB       ECS      YOUR APP    Elasticache   RDS (Postgres)
(0.999   *  0.9999  * 0.9999  *  0.999   *    0.999        *  0.9995)   * 100
=&amp;gt; 99.63052065660449
&lt;/pre&gt;

&lt;p&gt;You can see as you stack AWS services, regardless of your application stability, request success percentage decreases…&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Service&lt;/th&gt;
      &lt;th&gt;SLA&lt;/th&gt;
      &lt;th&gt;% Success Math&lt;/th&gt;
      &lt;th&gt;Request % Success&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;cloudfront&lt;/td&gt;
      &lt;td&gt;&lt;a href=&quot;https://aws.amazon.com/cloudfront/sla/&quot;&gt;99.9&lt;/a&gt;&lt;/td&gt;
      &lt;td&gt;0.999&lt;/td&gt;
      &lt;td&gt;99.9%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;ALB&lt;/td&gt;
      &lt;td&gt;&lt;a href=&quot;https://aws.amazon.com/elasticloadbalancing/sla/&quot;&gt;99.99&lt;/a&gt;&lt;/td&gt;
      &lt;td&gt;0.999 * 0.9999&lt;/td&gt;
      &lt;td&gt;99.89%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;ECS&lt;/td&gt;
      &lt;td&gt;&lt;a href=&quot;https://aws.amazon.com/compute/sla/&quot;&gt;99.99&lt;/a&gt;&lt;/td&gt;
      &lt;td&gt;0.999 * 0.9999 * 0.9999&lt;/td&gt;
      &lt;td&gt;99.88%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Custom App&lt;/td&gt;
      &lt;td&gt;99.9&lt;/td&gt;
      &lt;td&gt;0.999 * 0.9999 * 0.9999 * 0.999&lt;/td&gt;
      &lt;td&gt;99.78%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Elasticache&lt;/td&gt;
      &lt;td&gt;&lt;a href=&quot;https://aws.amazon.com/elasticache/sla/&quot;&gt;99.9&lt;/a&gt;&lt;/td&gt;
      &lt;td&gt;0.999 * 0.9999 * 0.9999 * 0.999 * 0.999&lt;/td&gt;
      &lt;td&gt;99.68%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;RDS (Postgres)&lt;/td&gt;
      &lt;td&gt;&lt;a href=&quot;https://aws.amazon.com/rds/sla/&quot;&gt;99.95&lt;/a&gt;&lt;/td&gt;
      &lt;td&gt;0.999 * 0.9999 * 0.9999 * 0.999 * 0.999 * 0.9995&lt;/td&gt;
      &lt;td&gt;99.63%&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2&gt;Impacts of the internet&lt;/h2&gt;
&lt;p&gt;Now let’s consider that even if one has achived all of this, are your customers actually receiving “five nines” of service?
If we consider their provider ISP (assuming home internet connections via ISPs), local wifi setup, or even worse a mobile connection… You can already drop expectations to at least %99.9, really you are trying to make your service appear as stable as their internet, with as many network failures as the customer has to endure with any other serivce.
&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  If failures are being measured from the end-user perspective and it is possible to drive the error rate for the service below the background error rate, those errors will fall within the noise for a given user’s Internet connection. While there are significant differences between ISPs and protocols (e.g., TCP versus UDP, IPv4 versus IPv6), we’ve measured the typical background error rate for ISPs as falling between 0.01% and 1%.
  -- &lt;a href=&quot;https://sre.google/sre-book/embracing-risk/&quot;&gt;Embracing Risk, Site Reliability Engineering&lt;/a&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;br /&gt;
WHAT ABOUT COST ^^ Google covers as well
&lt;br /&gt;&lt;/p&gt;
&lt;div style=&quot;margin-left: -400px&quot;&gt;
&lt;!-- Load d3.js --&gt;
&lt;script src=&quot;https://d3js.org/d3.v4.js&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.12.0/lodash.min.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;
&lt;div id=&quot;my_dataviz&quot;&gt;&lt;/div&gt;
&lt;script&gt;
  // set the dimensions and margins of the graph
  var margin = {top: 30, right: 30, bottom: 70, left: 60},
      width = 1000 - margin.left - margin.right,
      height = 400 - margin.top - margin.bottom;
  
  // append the svg object to the body of the page
  var svg = d3.select(&quot;#my_dataviz&quot;)
    .append(&quot;svg&quot;)
      .attr(&quot;width&quot;, width + margin.left + margin.right)
      .attr(&quot;height&quot;, height + margin.top + margin.bottom)
    .append(&quot;g&quot;)
      .attr(&quot;transform&quot;,
            &quot;translate(&quot; + margin.left + &quot;,&quot; + margin.top + &quot;)&quot;);
  
  // number of services
  var serviceStart = 1;
  var serviceEnd = 15;

  var data = [];

  while(serviceStart &lt; serviceEnd+1){
    data.push({ Services: serviceStart, Value: Math.pow(0.999,serviceStart) * 100.0});
    serviceStart++;
  }

  // sort data
  data.sort(function(b, a) {
    return a.Value - b.Value;
  });

  // X axis
  var x = d3.scaleBand()
    .range([ 0, width ])
    .domain(data.map(function(d) { return d.Services; }))
    .padding(0.2);
  svg.append(&quot;g&quot;)
    .attr(&quot;transform&quot;, &quot;translate(0,&quot; + height + &quot;)&quot;)
    .call(d3.axisBottom(x))
    .selectAll(&quot;text&quot;)
      .attr(&quot;transform&quot;, &quot;translate(-10,0)rotate(-45)&quot;)
      .style(&quot;text-anchor&quot;, &quot;end&quot;);

  // Add Y axis
  var y = d3.scaleLinear()
    .domain([95, 100])
    .range([ height, 0]);
  svg.append(&quot;g&quot;)
    .call(d3.axisLeft(y));

  // ----------------
  // Create a tooltip
  // https://www.d3-graph-gallery.com/graph/barplot_stacked_hover.html
  // ----------------
  var tooltip = d3.select(&quot;#my_dataviz&quot;)
    .append(&quot;div&quot;)
    .style(&quot;opacity&quot;, 0)
    .attr(&quot;class&quot;, &quot;tooltip&quot;)
    .style(&quot;background-color&quot;, &quot;white&quot;)
    .style(&quot;border&quot;, &quot;solid&quot;)
    .style(&quot;border-width&quot;, &quot;1px&quot;)
    .style(&quot;border-radius&quot;, &quot;5px&quot;)
    .style(&quot;padding&quot;, &quot;10px&quot;)

  // Three function that change the tooltip when user hover / move / leave a cell
  var mouseover = function(d) {
    var subgroupName = d.Services;
    console.log(d);
    var subgroupValue = d.Value;
    tooltip
        .html(&quot;Number Of Services: &quot; + subgroupName + &quot;&lt;br&gt;&quot; + &quot;Max Theoretical Availability: &quot; + subgroupValue)
        .style(&quot;opacity&quot;, 1)
  }
  var mousemove = function(d) {
    tooltip
      .style(&quot;left&quot;, (d3.mouse(this)[0]+90) + &quot;px&quot;) // It is important to put the +90: other wise the tooltip is exactly where the point is an it creates a weird effect
      .style(&quot;top&quot;, (d3.mouse(this)[1]) + &quot;px&quot;)
  }
  var mouseleave = function(d) {
    tooltip
      .style(&quot;opacity&quot;, 0)
  }  

  // Bars
  svg.selectAll(&quot;mybar&quot;)
    .data(data)
    .enter()
    .append(&quot;rect&quot;)
      .attr(&quot;x&quot;, function(d) { return x(d.Services); })
      .attr(&quot;y&quot;, function(d) { return y(d.Value); })
      .attr(&quot;width&quot;, x.bandwidth())
      .attr(&quot;height&quot;, function(d) { return height - y(d.Value); })
      .attr(&quot;fill&quot;, &quot;#69b3a2&quot;)
      .on(&quot;mouseover&quot;, mouseover)
      .on(&quot;mousemove&quot;, mousemove)
      .on(&quot;mouseleave&quot;, mouseleave)
  
  &lt;/script&gt;
&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Learning with Game Days</title>
   <link href="https://www.mayerdan.com/management/2021/01/08/learning-with-game-days"/>
   <updated>2021-01-08T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/management/2021/01/08/learning-with-game-days</id>
   <content type="html">
&lt;blockquote&gt;
  &lt;p&gt;photo credit &lt;a href=&quot;https://pixabay.com/photos/football-colored-sports-gear-1166205/&quot;&gt;Neramitevent@pixabay&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;learning-with-game-days&quot;&gt;Learning with Game Days&lt;/h1&gt;

&lt;p&gt;Many different companies and posts talk about why and how to run Game Days. I won’t rehash all of that in this post, instead I will give a basic intro and link to some sources then dive into some more specific Game Days I have recently been involved with.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;A game day simulates a failure or event to test systems, processes, and team responses. The purpose is to actually perform the actions the team would perform as if an exceptional event happened&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://wa.aws.amazon.com/wat.concept.gameday.en.html#:~:text=A%20game%20day%20simulates%20a,if%20an%20exceptional%20event%20happened.&quot;&gt;AWS Concepts Game Day&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.newrelic.com/engineering/how-to-run-a-game-day/&quot;&gt;New Relic How to Run a Game Day&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://medium.com/@rebeccaholzschuh/break-your-software-or-how-to-run-a-gameday-b68150188bb8&quot;&gt;How to Run a Game Day&lt;/a&gt; by &lt;a href=&quot;https://medium.com/@rebeccaholzschuh&quot;&gt;Rebecca Holzschuh&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Below are a few recent Game Day examples, with some details of what we did, what we learned, how we broke something in a “safe” manner, and a runbook to run your own. In all cases, one important factor of running the Game Day is having a way to stop if the simulated incident starts to cause a real incident. When planning a simulated emergency make sure you have a planned way to escape out of the test if something unexpected is occurring.&lt;/p&gt;

&lt;h2 id=&quot;safe-and-confident-deployments&quot;&gt;Safe and Confident Deployments&lt;/h2&gt;

&lt;p&gt;For various reasons some of our systems were relatively slow to deploy. This means &lt;!--more--&gt; if something bad happened, it could take awhile to revert, pass CI, deploy etc… We finally got access to a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rollback&lt;/code&gt; tool which was much faster than the normal deploy process to jump back to a recently deployed version of the code. While the tool was available for awhile many folks had their first experience using it during a real incident! Trying to find docs and understand the commands while stressed about some service being broken. Not the ideal way to learn… We got a group together with a preplanned and scheduled time, we then broke a ‘hidden’ endpoint on production, used rollback, fixed the code and showed how to “roll forward” back into the standard development flow. Since our tool did a bunch of cool things like freezing the deploy when you roll back, alerting various folks that it was going on, etc we got to see and feel the full experience. We had folks running the commands who had never had to do a rollback in production before, which made the exercise really valuable.&lt;/p&gt;

&lt;h3 id=&quot;runbook&quot;&gt;Runbook&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;schedule with a group of interested folks&lt;/li&gt;
  &lt;li&gt;announce to everyone in relevant channels so they don’t believe a real incident is occurring&lt;/li&gt;
  &lt;li&gt;deploy a non-important change&lt;/li&gt;
  &lt;li&gt;roll back and verify all the alerts, notifications, and commands&lt;/li&gt;
  &lt;li&gt;watch dashboards confirming how quickly the rollback worked and shows how to monitor deploy progress&lt;/li&gt;
  &lt;li&gt;fix the non important change&lt;/li&gt;
  &lt;li&gt;unfreeze deploys (if your system supports something like this)&lt;/li&gt;
  &lt;li&gt;deploy the main branch and restore the standard flow.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;upstream-partner-timeouts&quot;&gt;Upstream Partner Timeouts&lt;/h2&gt;

&lt;p&gt;We have a feature that I will cover more at some point in a future post, called chaos traces, similar to chaos monkey it lets you inject some chaos into your system to see how it behaves. In this Game Day, after the release of a new partner integration, we used our chaos trace tool to validate how the integration handle timeouts and errors. The code author and the PR reviewer paired up following the deploy and used chaos traces to verify that when the integration had an API Error or if the integration was running slow that the app handled it how everyone expected. By injecting say 3 seconds of latency into the respond time, we verify that the app would handle the timeout and give the customer a reasonable UX experience.&lt;/p&gt;

&lt;h3 id=&quot;runbook-1&quot;&gt;Runbook&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;schedule with a group of interested folks&lt;/li&gt;
  &lt;li&gt;announce to everyone in relevant channels so they don’t believe a real incident is occurring&lt;/li&gt;
  &lt;li&gt;cause partner error scenarios (if you  don’t have chaos functionality a feature flipper, or ENV toggle may work)&lt;/li&gt;
  &lt;li&gt;watch dashboards confirming expected logs / metrics&lt;/li&gt;
  &lt;li&gt;screen shot / share the UX experience of the error states with the team&lt;/li&gt;
  &lt;li&gt;have confidence that edge case integrations are not leaving users stuck in a confusing state&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;simulating-traffic-spikes&quot;&gt;Simulating Traffic Spikes&lt;/h2&gt;

&lt;p&gt;I didn’t participate directly in this Game Day, but I helped write some of the code and reviewed PRs. As a new service was being integrated into a high traffic flow, before the new service went live the team wanted to load test it. In the past we have often used simulated load, but it is hard to realistically generate traffic that looks like production… We tried something new and did a Game Day around production load… The team had areas of site that would soon make the new experience visible fire and forget all get requests to the new services without displaying and results… They even had a scale factor so we took traffic on a normal day and generated 3X production traffic to the new backing service… Since all the requests were fire and forget it didn’t slow down the user experience and let us work on performance tunning the new service under real heavy load.&lt;/p&gt;

&lt;h3 id=&quot;runbook-2&quot;&gt;Runbook&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;schedule with a group of interested folks&lt;/li&gt;
  &lt;li&gt;announce to everyone in relevant channels so they don’t believe a real incident is occurring&lt;/li&gt;
  &lt;li&gt;have a feature toggle to enable or disable the fire and forget load&lt;/li&gt;
  &lt;li&gt;enable and start to scale up the load factor&lt;/li&gt;
  &lt;li&gt;watch dashboards
    &lt;ul&gt;
      &lt;li&gt;ensuring the Game Day isn’t impacting existing services or users&lt;/li&gt;
      &lt;li&gt;learn how the new service response to heavy and increasing load and fix any performance issues&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;toggle back off the simulated load, and move forward with confidence the new service can handle real traffic&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;cache-failures&quot;&gt;Cache Failures&lt;/h2&gt;

&lt;p&gt;Caching is hard, eventually you might have to break a bad cache… If this cache has data for a few million users, sometimes very bad things happen to performance while the system tries to rebuild a cache. This is a future Game Day, that we have not yet done, but are currently planning after running into a cache issue. We were able to quickly bust the cache and fix a service that was in a invalid state, but it got us thinking… As the service and data set keeps growing, how long can we rely on that? At what point will we break cache and start to see a cascading of timeouts throughout all of our client services as we buckle under the load… Seems like a perfect thing to Game Day, we plan to put a feature toggle in place to let us ramp up &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cache_skips&lt;/code&gt; for a percentage of requests. Turn up the dial and ensure we can hit 100% without to large of an impact to our performance and stability.&lt;/p&gt;

&lt;h3 id=&quot;runbook-3&quot;&gt;Runbook&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;schedule with a group of interested folks&lt;/li&gt;
  &lt;li&gt;announce to everyone in relevant channels so they don’t believe a real incident is occurring&lt;/li&gt;
  &lt;li&gt;have a feature toggle turn up cache skips&lt;/li&gt;
  &lt;li&gt;start to turn up while watching dashboards and dial back down if there is any unexpected performance issues&lt;/li&gt;
  &lt;li&gt;identify any scaling issues and address and repeat until comfortable&lt;/li&gt;
  &lt;li&gt;enjoy the confidence that next time someone needs to bust the entire cache it shouldn’t be a problem&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;event-bus-failures&quot;&gt;Event Bus Failures&lt;/h2&gt;

&lt;p&gt;After some recent issues with our Event Bus, we put a number of protections in place that we thought would ensure our service could handle a significant unplanned outage without causing any interruption to clients. We made several changes to how we were handling events&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;moving from synchronous to asynchronous sending of events where possible&lt;/li&gt;
  &lt;li&gt;on any synchronous message queue retries in background jobs for later&lt;/li&gt;
  &lt;li&gt;increase our background job storage to be able to handle millions of stalled and failed events waiting for recovery&lt;/li&gt;
  &lt;li&gt;autoscaling background workers to be able to quickly process a big backlog of events without going to fast to overload the system&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;OK, great next time there is an incident our service will keep humming along without any issue… Well, would it really hold up? How could we be sure? Of course by now, y’all realize that instead of waiting for the next time our events queue has an incident, we can run a Game Day! That is what we did, we made a toggle to disable our access to the event queue server… Then flipped it off, we got to see if our protections worked verifying each doing what was expected… If we were wrong we could very quickly restore the system… During the incident the week prior, our internal background job storage overflowed in about 15m as we helplessly watched in horror… As we watched for 15 minutes during this Game Day our new monitor and connected alert showed the estimated time remaining we could buffer all events before running into problems. Showing how we could now handle hours of downtime, giving the teams plenty of time to get paged, and work on a resolution if needed.&lt;/p&gt;

&lt;h3 id=&quot;runbook-4&quot;&gt;Runbook&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;schedule with a group of interested folks&lt;/li&gt;
  &lt;li&gt;announce to everyone in relevant channels so they don’t believe a real incident is occurring&lt;/li&gt;
  &lt;li&gt;toggle off access to the event queue service&lt;/li&gt;
  &lt;li&gt;watch dashboards and improve any lacking observability to be confident in being able to understand your system&lt;/li&gt;
  &lt;li&gt;scale up any resources that are more impacted than expected&lt;/li&gt;
  &lt;li&gt;use the background job stalled jobs load to calculate and set alerts and storage needed for appropriate response times&lt;/li&gt;
  &lt;li&gt;enjoy the confidence that next time the event service has unexpected maintenance your service won’t drop any data and the services clients won’t see any errors.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h1&gt;

&lt;p&gt;Game Days can be used to work through what could be a difficult or exceptional situation in a controlled manner at a planned time and the support of a team. When done well, they can teach folks how to be better prepared for incidents, better understand their systems, and have more empathy for how other folks process and understand system in the stress of a real incident. They are a great way to ensure everyone on the team is ready when something unexpected next happens.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Performance of JSON Parsers at Scale</title>
   <link href="https://www.mayerdan.com/ruby/2020/12/02/performance-at-scale-JSON-parsers"/>
   <updated>2020-12-02T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/ruby/2020/12/02/performance-at-scale-JSON-parsers</id>
   <content type="html">
&lt;blockquote&gt;
  &lt;p&gt;photo credit &lt;a href=&quot;https://pixabay.com/photos/scale-question-importance-balance-2635397/&quot;&gt;qimono@pixabay&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;performance-of-json-parsers-at-scale&quot;&gt;Performance of JSON Parsers at Scale&lt;/h1&gt;

&lt;p&gt;In a recent post, &lt;a href=&quot;https://www.mayerdan.com/ruby/2020/11/15/benchmarking-JSON-parser&quot;&gt;benchmarking JSON Parsers (OJ, SimdJson, FastJsonParser)&lt;/a&gt;. This compared the parsers based on local microbenchmarks. In the end, I recommended for almost all general use cases go with OJ. Saying that FastJsonParser might be worth it for specific use cases. I want to do a quick follow up on sharing what happens when microbenchmarks meet real-world &lt;!--more--&gt; data, scale, and systems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL; DR;&lt;/strong&gt; you probably just want to use &lt;a href=&quot;https://github.com/ohler55/oj&quot;&gt;OJ&lt;/a&gt; as originally recommended, even on data where FastJsonParser wins in a microbenchmark, the real-world data was undetectable. While moving from StdLib to OJ was a 40% latency improvement holding up across multiple services.&lt;/p&gt;

&lt;h2 id=&quot;microbenchmarks&quot;&gt;Microbenchmarks&lt;/h2&gt;

&lt;p&gt;As often the case microbenchmarks come with a lot of issues. In this case, my microbenchmarks showed with a single example of real-world data that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FastJsonParser&lt;/code&gt; was that faster and had the lowest memory usage… &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OJ&lt;/code&gt; was about &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1.55x&lt;/code&gt; slower in both &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;with_keys&lt;/code&gt; and normal string key benchmarks. I benchmarked against two JSON pieces, a very small fake JSON payload, and a real-world large payload pulled from one of our production systems. For the specific examples, I used and with no other concerns yes &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FastJsonParser&lt;/code&gt; is faster, but that doesn’t mean it will translate into a real-world performance win.&lt;/p&gt;

&lt;p&gt;Given that we had previously seen 40% latency improvements when moving to OJ, it seemed like another &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;50%&lt;/code&gt; speed lift would be worth it, so I set out to test &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FastJsonParser&lt;/code&gt; on some of our production systems.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/40_perc_latency.png&quot; alt=&quot;40 percent improvement graph&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;Last week / Yesterday -&amp;gt; OJ released with 40% latency improvement&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;what-does-real-world--at-scale-mean&quot;&gt;What Does Real World &amp;amp; At Scale Mean?&lt;/h2&gt;

&lt;p&gt;In my case, I started with a single app having all API calls use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FastJsonParser&lt;/code&gt; to parse responses as well as when pulling JSON out of caches. The single app had a smaller JSON payload than I benchmarked with but had a very high throughput. After deployment, there was no detectable change in latency… Why not?&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;At that point, the way the app was performing was already fairly well optimized&lt;/li&gt;
  &lt;li&gt;According to DataDog trace spans JSON parsing was taking up less than 1ms of response time&lt;/li&gt;
  &lt;li&gt;Um… what is 50% faster on 1ms of a response, where JSON parse wasn’t even in the top 10 time-consuming spans of building the response? Nothing really&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;OK, I figured I picked a bad test case… I had originally benchmarked with a large JSON collection blob that passed through multiple systems. I decided to target 5 applications that worked together that used and served the original data I used to benchmark. This broke down like so:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;1 front end app&lt;/li&gt;
  &lt;li&gt;4 microservices sending different JSON payloads&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In total that large JSON collection data was passed through 3 of the 5 apps, with other JSON data coming from the other services. I figured this would have a bunch of small wins that would add up to show reduced latency for the front end application. Since all the small gains would eventually roll up to its final response time.&lt;/p&gt;

&lt;p&gt;After sending out 5 PRs, getting approvals, deploying, I played a waiting game watching graphs and collecting data… &lt;strong&gt;NOTHING&lt;/strong&gt;, I could see nothing. No errors, no problems, no performance impact.&lt;/p&gt;

&lt;h2 id=&quot;why-wasnt-it-faster&quot;&gt;Why Wasn’t It Faster?&lt;/h2&gt;

&lt;p&gt;I think similar to the single app example, even in this case OJ had really already captured the majority of the wins. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;JSON.parse&lt;/code&gt; was no longer in the top 10 spans of any of the 5 apps I updated. It previously was a part of the critical performance path… It no longer was… I am guessing there might have been tiny improvements, but nothing I could see with the naked eye… For most of these services, a 1ms improvement in each service, wouldn’t have been visible with all the random network noise.&lt;/p&gt;

&lt;p&gt;I think just network latency outweighed any further improvement on JSON parsing… None of the payloads were large or complex enough to drive a significant cost. This goes back to the original point, you need to really have a good reason to spend the extra time with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FastJsonParser&lt;/code&gt; to drive further improvements over OJ’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Json.parse&lt;/code&gt; drop-in replacement which also ensures all the Rails toolchain and middleware is using the improvements. Since &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FasterJsonParser&lt;/code&gt; requires the developer to explicitly call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FastJsonparser.parse&lt;/code&gt; I only did that where we handled API calls, it took more work and it wasn’t an improvement. If you have spans where &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;JSON.parse&lt;/code&gt; is showing significant time in your application traces, it could be different for you.&lt;/p&gt;

&lt;iframe src=&quot;https://giphy.com/embed/SwmYRaqcowXczDPAxP&quot; width=&quot;480&quot; height=&quot;400&quot; frameborder=&quot;0&quot; class=&quot;giphy-embed&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
&lt;p&gt;&lt;a href=&quot;https://giphy.com/gifs/Friends-SwmYRaqcowXczDPAxP&quot;&gt;via GIPHY&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;Microbenchmark -&amp;gt; Welcome to the real world&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h1&gt;

&lt;p&gt;Unless you are maintaining a gem and are avoiding dependencies, I highly recommend using &lt;a href=&quot;https://github.com/ohler55/oj&quot;&gt;OJ&lt;/a&gt; for your applications. It requires very low effort and holds up in microbenchmarks and across many different services and real-world data.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>benchmarking JSON Parsers (OJ, SimdJson, FastJsonParser)</title>
   <link href="https://www.mayerdan.com/ruby/2020/11/15/benchmarking-JSON-parser"/>
   <updated>2020-11-15T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/ruby/2020/11/15/benchmarking-JSON-parser</id>
   <content type="html">
&lt;blockquote&gt;
  &lt;p&gt;photo credit &lt;a href=&quot;https://pixabay.com/illustrations/compare-comparison-scale-balance-643305/&quot;&gt;Tumisu lt: @pixabay&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;update-added-fastjsonparser&quot;&gt;UPDATE: Added FastJsonParser&lt;/h2&gt;

&lt;p&gt;After some feedback on &lt;a href=&quot;https://www.reddit.com/r/ruby/comments/jvv6fa/benchmarking_json_parsers_oj_vs_simdjson_2x/gco1khd/?utm_source=reddit&amp;amp;utm_medium=web2x&amp;amp;context=3&quot;&gt;reddit (thx @f9ae8221b)&lt;/a&gt;, pointing out a JSON gem I wasn’t aware of, I updated the benchmarks to also support &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FastJSONparser&lt;/code&gt; and cover symbolize_keys, which is important for my companies use cases (which a co-worker pointed out) and can cause significant performance issues if you have to do that independently of JSON parsing.&lt;/p&gt;

&lt;h1 id=&quot;performance-benchmarks-between-oj-simdjson-fastjsonparser-and-stdlib&quot;&gt;Performance Benchmarks between OJ, SimdJSON, FastJsonparser, and StdLib&lt;/h1&gt;

&lt;p&gt;I was recently looking at the performance of some endpoints that process large amounts of JSON, and I wondered if we could do even better than we do in terms of performance for that processing. Across our company we have recently switch most of our apps from the Ruby StdLib JSON to &lt;a href=&quot;https://github.com/ohler55/oj&quot;&gt;OJ&lt;/a&gt;, but I had read about &lt;a href=&quot;https://github.com/saka1/simdjson_ruby&quot;&gt;SimdJSON&lt;/a&gt; and was curious if we should look further into it as well. In this article I will tell you a bit about each of the Ruby JSON options and why you might want to consider them.&lt;/p&gt;

&lt;h2 id=&quot;oj&quot;&gt;OJ&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/ohler55/oj&quot;&gt;OJ&lt;/a&gt; is a Ruby library for both parsing and generating JSON with a ton of options. I would basically say if you don’t want to think too much but care about JSON performance just set up the OJ gem, and it should be &lt;!--more--&gt; the best option for most folks. It is well known, tested, and trusted in the community with a ton of support.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;A drop in replacement, set it forget it, it is faster and better&lt;/li&gt;
  &lt;li&gt;Has built in Rails support, for some Rails JSON quirks&lt;/li&gt;
  &lt;li&gt;Supports both generation and parsing&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;simdjson&quot;&gt;SimdJSON&lt;/h2&gt;

&lt;p&gt;The &lt;a href=&quot;https://github.com/saka1/simdjson_ruby&quot;&gt;SimdJSON&lt;/a&gt; Ruby library doesn’t have a lot of talks, documentation, or attention… but it is binding to &lt;a href=&quot;https://github.com/simdjson/simdjson&quot;&gt;the fastest JSON parser out there&lt;/a&gt;… It offers parsing speeds that nothing else can touch, and if you are trying to parse extremely large and dynamic JSON, it might just be the best option for you.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Only supports parsing&lt;/li&gt;
  &lt;li&gt;Doesn’t support &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;symbolize_keys&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;The fastest option out there (uses simdjson C libs)&lt;/li&gt;
  &lt;li&gt;Not a lot of community activity&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;fastjsonparser&quot;&gt;FastJsonparser&lt;/h2&gt;

&lt;p&gt;While &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;simdJSON&lt;/code&gt; is a fast gem, it doesn’t have much support and the way it handles rescuing errors could leak memory. While I didn’t see such issues in my limited production rollout that is worth noting. The user &lt;a href=&quot;https://www.reddit.com/user/f9ae8221b/&quot;&gt;@f9ae8221b
&lt;/a&gt; pointed out the memory issue and that the gem &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FastJsonparser&lt;/code&gt; also wraps &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;simdjson&lt;/code&gt; and has wider community support. I had never heard of the gem, and was already trying to &lt;a href=&quot;https://github.com/saka1/simdjson_ruby/issues/22&quot;&gt;patch SimdJSON&lt;/a&gt; to support &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;symbolize_keys&lt;/code&gt;. Luckily &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FastJsonparser&lt;/code&gt; already supports that option. It still is faster than OJ, and requires a bit more work to intgrate, but looks like a better option than &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SimdJSON&lt;/code&gt; when you are looking for improved parsing speed. The user still mentions it could have some production issues, so I will have to report back as I roll it out to various systems.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Only supports parsing&lt;/li&gt;
  &lt;li&gt;Does support &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;symbolize_keys&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;The fastest option out there (uses simdjson C libs)&lt;/li&gt;
  &lt;li&gt;Larger community support&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;stdlib-ruby-json&quot;&gt;StdLib Ruby JSON&lt;/h2&gt;

&lt;p&gt;It is built in, seriously if you do much with JSON in a production system, just use OJ, unless you want to dig in deeper or find some specific reason it won’t work for you. The Ruby library is fine and will work for any quick check, but if you have any reason to care about performance, OJ is a easy to use drop in replacement… A note of when you shouldn’t use it? If you are authoring a gem, reduce your hard dependencies as much as possible, if you call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;JSON.parse&lt;/code&gt; and a hosting app is using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OJ&lt;/code&gt;, your gem will use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OJ&lt;/code&gt; and be faster… You shouldn’t force users of your gem to require &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OJ&lt;/code&gt;.&lt;/p&gt;

&lt;h1 id=&quot;benchmarking-the-methods&quot;&gt;Benchmarking the methods&lt;/h1&gt;

&lt;p&gt;Let’s see the difference with favorite Ruby benchmarking gem &lt;a href=&quot;https://github.com/evanphx/benchmark-ips&quot;&gt;benchmark-ips&lt;/a&gt;, which gives a bit more readable reports than the standard &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;benchmark&lt;/code&gt; lib. These are just quick micro-benchmarks, with all the issues that come with them, but the performance impact has been further validated by deploying to production systems with measurable impacts on the response time. The product use case included far larger JSON payloads and with much higher variability to the data, making me think the results would apply to most web service like systems.&lt;/p&gt;

&lt;h2 id=&quot;benchmarking-json-parsing-without-symbolize_keys-added-fastjsonparser&quot;&gt;Benchmarking JSON Parsing (without symbolize_keys, added FastJsonparser)&lt;/h2&gt;

&lt;p&gt;We will load up the various libraries, and some weird fake HASH/JSON data. Then benchmark parsing it for a number of seconds…&lt;/p&gt;

&lt;div class=&quot;language-ruby 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;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;benchmark/ips&apos;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;json&apos;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;oj&apos;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;simdjson&apos;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;fast_jsonparser&apos;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;memory_profiler&apos;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;rails&apos;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;one&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;two&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;three&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;3&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;nested&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;s2&quot;&gt;&quot;I&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;go&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;deep&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;when&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;i&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;need&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;a: &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;array&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:[&lt;/span&gt;
    &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;mixed&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;types&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;mi&quot;&gt;6&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;nf&quot;&gt;as_json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;freeze&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;ensure these match&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt;  &lt;span class=&quot;no&quot;&gt;Oj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;symbol_keys: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Simdjson&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
        &lt;span class=&quot;no&quot;&gt;Simdjson&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;symbolize_names: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
        &lt;span class=&quot;no&quot;&gt;FastJsonparser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;symbolize_keys: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Simdjson&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;Benchmark&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ips&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:time&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:warmup&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;report&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;oj parse&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;no&quot;&gt;Oj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;symbol_keys: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;false&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;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;report&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;simdjson parse&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;no&quot;&gt;Simdjson&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&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;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;report&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;FastJsonparser parse&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;no&quot;&gt;FastJsonparser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;symbolize_keys: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;false&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;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;report&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;stdlib JSON parse&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;no&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;symbolize_names: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;false&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;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;compare!&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Let&apos;s check memory as well...&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;report&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;MemoryProfiler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;report&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;times&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Simdjson&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dup&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;end&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;simpdjson memory&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;report&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;pretty_print&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;report&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;MemoryProfiler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;report&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;times&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Oj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dup&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;end&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;OJ memory&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;report&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;pretty_print&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;benchmark-results-without-symbolize_keys&quot;&gt;Benchmark Results (without symbolize_keys)&lt;/h2&gt;

&lt;p&gt;This shows as claimed that SimdJSON and FastJsonparser outperform OJ even on pretty small and contrived JSON examples. The Performance gap holds up or sometimes looks more significant when looking at more realistic production payloads seen in some of the product systems I work with. Note if you need &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;symbolize_keys&lt;/code&gt; or want a bit more community support I would go with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FastJsonparser&lt;/code&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;ensure these match
true
Warming up --------------------------------------
            oj parse    12.697k i/100ms
      simdjson parse    17.276k i/100ms
FastJsonparser parse    17.834k i/100ms
   stdlib JSON parse     8.662k i/100ms
Calculating -------------------------------------
            oj parse    121.709k (± 3.5%) i/s -      1.828M in  15.040973s
      simdjson parse    171.253k (± 4.3%) i/s -      2.574M in  15.060276s
FastJsonparser parse    190.436k (± 3.2%) i/s -      2.853M in  15.000218s
   stdlib JSON parse     93.032k (± 3.4%) i/s -      1.403M in  15.102830s

Comparison:
FastJsonparser parse:   190436.3 i/s
      simdjson parse:   171252.9 i/s - 1.11x  (± 0.00) slower
            oj parse:   121709.5 i/s - 1.56x  (± 0.00) slower
   stdlib JSON parse:    93032.1 i/s - 2.05x  (± 0.00) slower
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;benchmarking-json-parsing-with-symbolize_keys&quot;&gt;Benchmarking JSON Parsing (with symbolize_keys)&lt;/h2&gt;

&lt;div class=&quot;language-ruby 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;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;benchmark/ips&apos;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;json&apos;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;oj&apos;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;simdjson&apos;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;fast_jsonparser&apos;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;memory_profiler&apos;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;rails&apos;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;one&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;two&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;three&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;3&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;nested&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;s2&quot;&gt;&quot;I&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;go&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;deep&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;when&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;i&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;need&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;a: &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;array&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:[&lt;/span&gt;
    &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;mixed&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;types&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;mi&quot;&gt;6&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;nf&quot;&gt;as_json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;freeze&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;ensure these match&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt;  &lt;span class=&quot;no&quot;&gt;Oj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;symbol_keys: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Simdjson&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;deep_symbolize_keys!&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
        &lt;span class=&quot;no&quot;&gt;Simdjson&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;deep_symbolize_keys!&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;symbolize_names: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
        &lt;span class=&quot;no&quot;&gt;FastJsonparser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Simdjson&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;deep_symbolize_keys!&lt;/span&gt;


&lt;span class=&quot;no&quot;&gt;Benchmark&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ips&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:time&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:warmup&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;report&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;oj parse&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;no&quot;&gt;Oj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;symbol_keys: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&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;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;report&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;simdjson parse&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;no&quot;&gt;Simdjson&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;deep_symbolize_keys!&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;report&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;FastJsonparser parse&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;no&quot;&gt;FastJsonparser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&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;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;report&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;stdlib JSON parse&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;no&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;symbolize_names: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&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;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;compare!&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;benchmark-results-with-symbolize_keys&quot;&gt;Benchmark Results (with symbolize_keys)&lt;/h2&gt;

&lt;p&gt;This is the other main reason to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FastJsonparser&lt;/code&gt; depending on the integrations in your apps you might rely on symbolized_keys… We had added that at a very low level in our shared ApiClient, and the performance implications of having to symbolize_keys as a second pass make a big difference. This shows how the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;simdjson&lt;/code&gt; performance win doesn’t hold up when you need &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;symbolized_keys&lt;/code&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;ensure these match
true
Warming up --------------------------------------
            oj parse    13.455k i/100ms
      simdjson parse     7.752k i/100ms
FastJsonparser parse    19.458k i/100ms
   stdlib JSON parse     8.546k i/100ms
Calculating -------------------------------------
            oj parse    134.285k (± 4.5%) i/s -      2.018M in  15.060313s
      simdjson parse     75.825k (± 7.2%) i/s -      1.132M in  15.022033s
FastJsonparser parse    208.199k (± 3.1%) i/s -      3.133M in  15.061737s
   stdlib JSON parse     86.504k (± 3.5%) i/s -      1.299M in  15.035736s

Comparison:
FastJsonparser parse:   208199.1 i/s
            oj parse:   134285.4 i/s - 1.55x  (± 0.00) slower
   stdlib JSON parse:    86503.7 i/s - 2.41x  (± 0.00) slower
      simdjson parse:    75825.4 i/s - 2.75x  (± 0.00) slower
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;using-large-json-data&quot;&gt;Using Large JSON Data&lt;/h2&gt;

&lt;p&gt;The results are very similar for a much larger production 120K JSON payload, pulled for a live system. (NOTE: these benchmarks were run on a different machine)… In this case we are showing nearly a 2X performance boost.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;without symbolize_keys:&lt;/strong&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;Warming up --------------------------------------
            oj parse    62.000  i/100ms
      simdjson parse    79.000  i/100ms
   stdlib JSON parse    42.000  i/100ms
Calculating -------------------------------------
            oj parse    622.377  (± 3.9%) i/s -      9.362k in  15.066907s
      simdjson parse    815.699  (± 4.5%) i/s -     12.245k in  15.045902s
   stdlib JSON parse    426.656  (± 3.5%) i/s -      6.426k in  15.083428s

Comparison:
      simdjson parse:      815.7 i/s
            oj parse:      622.4 i/s - 1.31x  (± 0.00) slower
   stdlib JSON parse:      426.7 i/s - 1.91x  (± 0.00) slower
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;with symbol_keys:&lt;/strong&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;ensure these match
true
Warming up --------------------------------------
            oj parse    71.000  i/100ms
      simdjson parse    29.000  i/100ms
FastJsonparser parse    82.000  i/100ms
   stdlib JSON parse    41.000  i/100ms
Calculating -------------------------------------
            oj parse    726.191  (± 1.5%) i/s -     10.934k in  15.059977s
      simdjson parse    294.947  (± 2.4%) i/s -      4.437k in  15.052250s
FastJsonparser parse    909.828  (±10.2%) i/s -     13.530k in  15.026051s
   stdlib JSON parse    497.749  (± 3.6%) i/s -      7.462k in  15.011659s

Comparison:
FastJsonparser parse:      909.8 i/s
            oj parse:      726.2 i/s - 1.25x  (± 0.00) slower
   stdlib JSON parse:      497.7 i/s - 1.83x  (± 0.00) slower
      simdjson parse:      294.9 i/s - 3.08x  (± 0.00) slower
&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;MemoryProfiler&lt;/code&gt; (nor production deployments, server metrics) on either small or large JSON objects didn’t really show any substantial difference, so I wouldn’t be too concerned with memory when picking these libraries.&lt;/p&gt;

&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h1&gt;

&lt;p&gt;If you have a Ruby service that is parsing large quantities of JSON, it might be worth taking a look at the newer and less known &lt;a href=&quot;https://github.com/anilmaurya/fast_jsonparser&quot;&gt;FastJsonparser&lt;/a&gt;. While the gem is less documented and takes a bit more work to integrate into your app than &lt;a href=&quot;https://github.com/ohler55/oj&quot;&gt;OJ&lt;/a&gt;. If you are looking for a drop in replacement OJ is still the way to go, but for some use cases &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SimpdJSON&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FastJsonparser&lt;/code&gt; will be worth the extra effort. If you are using Rails with a production deployment I can’t really see any reason to not use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OJ&lt;/code&gt; for the significant performance benefits that come with it. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OJ&lt;/code&gt; library made it as easy as possible to use as a drop in replacement and if you rely on nearly any particular JSON quick of the past they have options to help you stay fully compatible. I know as we look towards Ruby 3 we are also hoping to move away from some of the native extension C libraries, but when it comes to very low level repetitive application tasks vs application logic, sometimes it is hard to beat and worth the integration and dependency cost.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Ruby: Understanding create_or_find_by vs find_or_create_by</title>
   <link href="https://www.mayerdan.com/ruby/2020/10/22/ar-find_or_create"/>
   <updated>2020-10-22T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/ruby/2020/10/22/ar-find_or_create</id>
   <content type="html">
&lt;blockquote&gt;
  &lt;p&gt;photo credit &lt;a href=&quot;https://pixabay.com/photos/alzheimer-s-dementia-words-3068938/&quot;&gt;geralt: @pixabay&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;performance-benchmarks--considerations-between-create_or_find_by--find_or_create_by&quot;&gt;Performance Benchmarks &amp;amp; Considerations between &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;create_or_find_by&lt;/code&gt; &amp;amp; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;find_or_create_by&lt;/code&gt;&lt;/h1&gt;

&lt;p&gt;I was recently optimizing an endpoint and got to think through some interesting differences between two Active Record methods that help you either find an existing record or create a new one. At first glance, it seems either is fine with some notable differences around their race conditions.&lt;/p&gt;

&lt;h2 id=&quot;find_or_create_by&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;find_or_create_by&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;The &lt;a href=&quot;https://apidock.com/rails/v4.0.2/ActiveRecord/Relation/find_or_create_by&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;find_or_create_by&lt;/code&gt;&lt;/a&gt; method has been around longer and is more familiar to many Rubyists. The race condition is called out in the linked docs, excerpt below.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Please note &lt;em&gt;this method is not atomic&lt;/em&gt;, it runs first a SELECT, and if there are no results an INSERT is attempted. If there are other threads or processes there is a race condition between both calls and it could be the case that you end up with two similar records.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This lead to Rails 6 adding the newer methods…&lt;/p&gt;

&lt;h2 id=&quot;create_or_find_by&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;create_or_find_by&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;The new &lt;a href=&quot;https://apidock.com/rails/v6.0.0/ActiveRecord/Relation/create_or_find_by&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;create_or_find_by&lt;/code&gt;&lt;/a&gt; methods have a more rare race condition (on deleted ids), but can prevent a more common insert race condition on duplicates… It is well described in this post, &lt;a href=&quot;https://blog.bigbinary.com/2019/03/25/rails-6-adds-create_or_find_by.html&quot;&gt;Rails 6 adds &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;create_or_find_by&lt;/code&gt;&lt;/a&gt;, along with some downsides. For example without a unique DB constraint it will create duplicates (ex: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;add_index :posts, :title, unique: true&lt;/code&gt;). These issues are also called out in the docs &lt;!--more--&gt; linked above, excerpt below.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;The underlying table must have the relevant columns defined with unique constraints.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;While we avoid the race condition between SELECT -&amp;gt; INSERT from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#find_or_create_by&lt;/code&gt;, we actually have another race condition between INSERT -&amp;gt; SELECT, which can be triggered if a DELETE between those two statements is run by another client. But for most applications, that’s a significantly less likely condition to hit.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;It relies on exception handling to handle control flow, which may be marginally slower.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;The primary key may auto-increment on each create, even if it fails. This can accelerate the problem of running out of integers, if the underlying table is still stuck on a primary key of type int (note: All Rails apps since 5.1+ have defaulted to bigint, which is not liable to this problem).&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;benchmarking-the-methods&quot;&gt;Benchmarking the methods&lt;/h1&gt;

&lt;p&gt;While the docs are good at calling out the race conditions, they are not as clear about the performance implications… In fact, they could lead one to believe that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;create_or_find_by&lt;/code&gt; is always slower from this line, “may be marginally slower”… The reality is you need to know the usage characteristics of where you will be calling these methods to pick the one with the best performance characteristics.&lt;/p&gt;

&lt;p&gt;Both of the methods will either find or create a record, and they try those in different orders… If you expect to most often find the record vs most often create a record that has a big impact on the performance. Let’s see how much by breaking out my favorite Ruby benchmarking gem &lt;a href=&quot;https://github.com/evanphx/benchmark-ips&quot;&gt;benchmark-ips&lt;/a&gt;, which gives a bit more readable reports than the standard &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;benchmark&lt;/code&gt; lib. These are just quick micro-benchmarks, with all the issues that come with them, but the performance has also been validated by deploying to production systems at scale.&lt;/p&gt;

&lt;h2 id=&quot;benchmarking-all-finds&quot;&gt;Benchmarking All Finds&lt;/h2&gt;

&lt;p&gt;In this case, we are going to benchmark a case that simulates a code path that is all finds, and no creates… If you have an endpoint that creates once in a user’s lifecycle and then forever is hitting the find, you likely will have a much higher find vs create ratio close to this benchmark.&lt;/p&gt;

&lt;div class=&quot;language-ruby 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;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;benchmark/ips&apos;&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;logger&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;destroy_all&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;Benchmark&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ips&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:time&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&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;ss&quot;&gt;:warmup&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;report&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;create_or_find_by&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create_or_find_by!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;title: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;create_or_find_by&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;report&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;find_or_create_by&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;find_or_create_by!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;title: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;find_or_create_by&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;compare!&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;results:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As expected, when you would find an existing record all the time &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;find_or_create_by&lt;/code&gt; is much faster, approximately 4X faster!&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;Warming up --------------------------------------
  create_or_find_by     49.000  i/100ms
  find_or_create_by    204.000  i/100ms
Calculating -------------------------------------
   create_or_find_by     450.791  (± 7.8%) i/s -      4.508k in  10.063664s
   find_or_create_by     2.078k (± 6.9%) i/s -     20.808k in  10.061016s

Comparison:
  find_or_create_by:     2078.1 i/s
  create_or_find_by:     450.8 i/s - 4.61x  (± 0.00) slower
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;benchmarking-all-creates&quot;&gt;Benchmarking All Creates&lt;/h2&gt;

&lt;p&gt;In this case, we will benchmark where nearly all the calls are creating new records… This would simulate an endpoint that is generally creating brand new records and very rarely should find an existing record.&lt;/p&gt;

&lt;div class=&quot;language-ruby 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;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;benchmark/ips&apos;&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;logger&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;destroy_all&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;Benchmark&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ips&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:time&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&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;ss&quot;&gt;:warmup&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;report&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;create_or_find_by&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create_or_find_by!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;title: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;create_or_find_by &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;rand&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;report&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;find_or_create_by&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;find_or_create_by!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;title: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;find_or_create_by &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;rand&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;compare!&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;results:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In a case where you are always creating it is faster to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;create_or_find_by&lt;/code&gt; but the overall difference is less dramatic.&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;Warming up --------------------------------------
  create_or_find_by     73.000  i/100ms
  find_or_create_by     44.000  i/100ms
Calculating -------------------------------------
  create_or_find_by     722.939  (± 8.3%) i/s -      7.227k in  10.069582s
  find_or_create_by     522.615  (± 9.6%) i/s -      5.192k in  10.028946s

Comparison:
  create_or_find_by:      722.9 i/s
  find_or_create_by:      522.6 i/s - 1.38x  (± 0.00) slower
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h1&gt;

&lt;p&gt;When you are working with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;create_or_find_by&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;find_or_create_by&lt;/code&gt; ensure you are considering how and which race conditions might affect your code. If it is easier to just have your app handle DB constraint errors and retry directly, most of the time using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;find_or_create_by&lt;/code&gt; is going to be simpler and more performant… If you reach for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;create_or_find_by&lt;/code&gt; ensure you understand the additional complexity and performance impacts depending on the expected hit and miss ratio for your use case.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Ruby: Patching StdLib in Gems</title>
   <link href="https://www.mayerdan.com/ruby/2020/07/15/ruby-gems-patching-std-lib"/>
   <updated>2020-07-15T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/ruby/2020/07/15/ruby-gems-patching-std-lib</id>
   <content type="html">
&lt;blockquote&gt;
  &lt;p&gt;photo credit &lt;a href=&quot;https://pixabay.com/photos/hand-labor-sew-patchwork-187951/&quot;&gt;patches: AnnaER @pixabay&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;why-patch-ruby-stdlib-code-in-gems&quot;&gt;Why Patch Ruby StdLib Code in Gems&lt;/h1&gt;

&lt;p&gt;Well, the Ruby community does this a lot, it can unlock powerful enhancements, features, observability, and more…&lt;/p&gt;

&lt;p&gt;Here are some examples of patching Ruby’s StdLib (standard library). Let’s just look at a few that patch a single piece of Ruby, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Net::HTTP&lt;/code&gt;. Many libraries want to tap into what is happening around the network.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;DataDog’s &lt;a href=&quot;https://github.com/DataDog/dd-trace-rb&quot;&gt;dd-trace-rb&lt;/a&gt; gem, patches many &lt;a href=&quot;https://github.com/DataDog/dd-trace-rb/blob/master/lib/ddtrace/contrib/http/instrumentation.rb#L16&quot;&gt;Ruby StdLib features like Net::HTTP&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/MiniProfiler/rack-mini-profiler&quot;&gt;MiniProfiler&lt;/a&gt; also patches &lt;a href=&quot;https://github.com/MiniProfiler/rack-mini-profiler#nethttp-stack-level-too-deep-errors&quot;&gt;Net::HTTP&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/bblimke/webmock&quot;&gt;WebMock&lt;/a&gt; in tests patches &lt;a href=&quot;https://github.com/bblimke/webmock/blob/master/lib/webmock/http_lib_adapters/net_http.rb#L16&quot;&gt;Net::HTTP&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sometimes opposed to patching upstream Ruby code, one can just have adapters/wrappers around them, while related it is a much different approach and you can see how &lt;a href=&quot;https://github.com/lostisland/faraday/blob/master/lib/faraday/adapter/net_http.rb&quot;&gt;Faraday handles adapting Net::HTTP&lt;/a&gt; as an example of that approach. Which is safer, but requires upstream apps to change their code to use the libraries’ APIs as opposed to modifying existing behavior.&lt;/p&gt;

&lt;h1 id=&quot;gems-patch-ruby-stdlib-so-what&quot;&gt;Gems Patch Ruby StdLib, So What?&lt;/h1&gt;

&lt;p&gt;The problem comes up with multiple gems trying to patch the same method. From the examples above, there are multiple ways to attempt to modify the original code, which doesn’t always play nicely together.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;alias&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;alias_method&lt;/code&gt;, and the like&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;prepend&lt;/code&gt;, class/module extension ways of extending a method and using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;super&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;replacing constants&lt;/code&gt;, I don’t know the common term for what &lt;a href=&quot;https://github.com/bblimke/webmock/blob/master/lib/webmock/http_lib_adapters/net_http.rb#L16&quot;&gt;WebMock does to patch Net::Http&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you have multiple gems patching the same upstream Ruby StdLib (or Rails) class or function, you can run into issues. This is a known &lt;a href=&quot;https://bugs.ruby-lang.org/issues/11120&quot;&gt;Ruby ‘Bug’&lt;/a&gt; along with a known solution to detect and patch in the same way.&lt;/p&gt;

&lt;h1 id=&quot;example-errors-stack-level-too-deep&quot;&gt;Example: Errors: stack level too deep&lt;/h1&gt;

&lt;p&gt;The reason I am writing this up is that I had a &lt;a href=&quot;https://github.com/danmayer/coverband/issues/367&quot;&gt;bug in Coverband for months&lt;/a&gt;, thx bug reporters(&lt;a href=&quot;https://github.com/kejordan&quot;&gt;@&lt;/a&gt;) I appreciate it, that made no sense to me… I couldn’t reproduce it, I didn’t have any great stack traces, I had no idea what area of code the issue was even in… I couldn’t even investigate the issue. At the time all I really knew about the bug? Exception: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Stack level too deep error&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;After months, of once in awhile taking a look but not understanding the problem… I got a new bug report from &lt;a href=&quot;https://github.com/hanslauwers&quot;&gt;@ hanslauwers&lt;/a&gt;… Which, added some details, specifically that the gem &lt;a href=&quot;https://github.com/airbrake&quot;&gt;AirBrake&lt;/a&gt; and &lt;a href=&quot;https://github.com/danmayer/coverband&quot;&gt;Coverband&lt;/a&gt;, both were patching Resque… but in different ways…&lt;/p&gt;

&lt;p&gt;A few days prior to the above report, I saw while working on another project this excellent description of a problem that had been solved in the &lt;a href=&quot;https://github.com/MiniProfiler/rack-mini-profiler&quot;&gt;MiniProfiler&lt;/a&gt; project, the readme documents how to resolve &lt;a href=&quot;https://github.com/MiniProfiler/rack-mini-profiler#nethttp-stack-level-too-deep-errors&quot;&gt;Net::HTTP stack level too deep errors&lt;/a&gt;… So the new bug report made my spidey sense tingle, and I was finally able to fix it.&lt;/p&gt;

&lt;h1 id=&quot;how-to-handle-applications-differences&quot;&gt;How to handle applications differences&lt;/h1&gt;

&lt;p&gt;I ended up following the same pattern as &lt;a href=&quot;https://github.com/MiniProfiler/rack-mini-profiler&quot;&gt;MiniProfiler&lt;/a&gt;, which described the problem and the fix excellently in &lt;a href=&quot;https://github.com/MiniProfiler/rack-mini-profiler#nethttp-stack-level-too-deep-errors&quot;&gt;it’s readme&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;If you start seeing SystemStackError: stack level too deep errors from Net::HTTP after installing Mini Profiler, this means there is another patch for Net::HTTP#request that conflicts with Mini Profiler’s patch in your application. To fix this, change rack-mini-profiler gem line in your Gemfile to the following:&lt;/p&gt;

  &lt;p&gt;… examples …&lt;/p&gt;

  &lt;p&gt;This conflict happens when a ruby method is patched twice, once using module prepend, and once using method aliasing. See this ruby issue for details. The fix is to apply all patches the same way. Mini Profiler by default will apply its patch using method aliasing, but you can change that to module prepend by adding require: [‘prepend_net_http_patch’] to the gem line as shown above.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The readme, explains the issue, has code examples for how app’s integrating the gem can resolve the issue, and links to the original &lt;a href=&quot;https://bugs.ruby-lang.org/issues/11120&quot;&gt;Ruby “Bug”&lt;/a&gt;, which explains the issue in detail and discusses approaches to solve the problem&lt;/p&gt;

&lt;h1 id=&quot;coverbands-patching-solution&quot;&gt;Coverband’s Patching Solution&lt;/h1&gt;

&lt;p&gt;This is the &lt;a href=&quot;https://github.com/danmayer/coverband/pull/383&quot;&gt;PR that was merged&lt;/a&gt; after understanding the problem and approach I took to resolve the problem. Again, heavily patterned off the MiniProfiler solution.&lt;/p&gt;

&lt;p&gt;In the end, it is a pretty simple fix, but it took time and various folks participating in the bug report to understand. If you see an open github issue that still seems relevant, add some comments and details. You never know if you will be the trigger that helps folks understand and resolve the issue.&lt;/p&gt;

&lt;p&gt;I know patching always gets a bad wrap in Ruby, and it can be hard to fully understand and debug, but it is also extremely powerful. It is good to understand the gotcha’s that can occur, and how to work around those issues, especially if you are shipping shared code that can patch other shared code like Ruby’s StdLib.
 &lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Rails Flaky Spec Solutions</title>
   <link href="https://www.mayerdan.com/ruby/2020/01/14/rails-flaky-specs-solutions"/>
   <updated>2020-01-14T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/ruby/2020/01/14/rails-flaky-specs-solutions</id>
   <content type="html">
&lt;blockquote&gt;
  &lt;p&gt;photo credit &lt;a href=&quot;https://pixabay.com/illustrations/banner-header-plaster-cracks-old-902587/&quot;&gt;flaky wall: pixabay&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;introducing-rails-flaky-spec-examples&quot;&gt;Introducing Rails Flaky Spec Examples&lt;/h1&gt;

&lt;p&gt;I have written about how our teams are &lt;a href=&quot;https://www.mayerdan.com/ruby/2019/09/07/flaky-ruby-tests&quot;&gt;dealing with flaky ruby tests&lt;/a&gt; in legacy projects. In this post, I want to show, how we can teach about common testing pitfalls and how to avoid them.&lt;/p&gt;

&lt;p&gt;In this post, I want to introduce a new project &lt;a href=&quot;https://github.com/danmayer/rails_flaky_spec_examples&quot;&gt;Rails Flaky Spec Examples&lt;/a&gt;. I created the example flaky Rails spec suite for an internal code clinic at Stitch Fix. We ran it as a 1-hour workshop to teach about common flaky test issues and how to resolve them. I am hoping that over time, I can continue to grow the example base and talk about some of the more complex flaky tests and how we could perhaps more systematically avoid them. As I work with this project over time, I hope to make it into a good learning tool for the community.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/flaky_specs.gif&quot; alt=&quot;Flaky vs Stable Suite&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;Running the flaky and thens stable suite&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;why-a-rails-suite-one-problem-with-flaky-test-posts&quot;&gt;Why A Rails Suite? One Problem with Flaky Test Posts&lt;/h1&gt;

&lt;p&gt;While &lt;!--more--&gt; there are a number of great blog posts on flaky specs:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://engineering.gusto.com/eliminating-flaky-ruby-tests/&quot;&gt;eliminating flaky ruby tests&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://medium.com/better-programming/why-rspec-tests-fail-and-how-to-fix-them-402f1c7dce16&quot;&gt;why rspec tests fail and how to fix them&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/evilmartians/terraforming-rails/blob/master/guides/flaky.md&quot;&gt;list of flaky test practices to avoid&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The majority of the posts don’t have runnable examples. While they might have some code snippets showing examples in the post they don’t have a runnable project. You can try to paste some of the examples into a file, but they reference dependencies, like an active record model without the related migration or dependencies. Often the snippets get too simplified to show how the errors look in a real large legacy project.&lt;/p&gt;

&lt;p&gt;Since the examples aren’t runnable it makes it a bit harder to use them as a teaching tool, or show more complex tests or CI interactions. This project aims to be in the sweet spot, where it is still small enough to easily understand the issues, but it is part of a real runnable app that can be extended to highlight more complex Rails and JS testing issues. Adding things like common libraries (Faker, Capybara, etc) and different levels of tests including browser-based javascript tests and the related race conditions.&lt;/p&gt;

&lt;p&gt;While this project isn’t a real-world example which are the best sources of flaky specs, sometimes real-world examples are hard to easily understand. Many of the examples in this project were extracted from real world examples. If you really want to dive into a fully developed complex code base that has Flaky specs, the best source for that with tagged flaky specs comes from &lt;a href=&quot;https://twitter.com/samsaffron&quot;&gt;@ samsaffron&lt;/a&gt;/&lt;a href=&quot;https://discourse.org/&quot;&gt;discourse.org&lt;/a&gt; in their tagged collection of &lt;a href=&quot;https://review.discourse.org/tags/heisentest&quot;&gt;flaky heisentest&lt;/a&gt; which is described more in the excellent post, &lt;a href=&quot;https://samsaffron.com/archive/2019/05/15/tests-that-sometimes-fail&quot;&gt;tests that sometimes fail&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This project allows devs, to run spec examples, see the failures, and try to fix the flaky specs either themselves or with a small group. If they get stuck example solutions are readily available. It should also be relatively easy to extend the project to add examples extracted from real-world projects. I would love to get some flaky test submissions for difficult flaky spec issues.&lt;/p&gt;

&lt;h1 id=&quot;project-structure&quot;&gt;Project Structure&lt;/h1&gt;

&lt;p&gt;The project is designed to have two versions of every spec the flaky version, and the stable version.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/flaky_project_structure.png&quot; alt=&quot;Project Structure&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;You can see this in the folder structure each spec folder has a sub-directory &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;solved&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This lets me use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rspec&lt;/code&gt; tags to run either the solved or flaky specs.&lt;/p&gt;

&lt;div class=&quot;language-ruby 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;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;define_derived_metadata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;file_path: &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Regexp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;/solved/&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:solved&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;SOLVED_SPECS&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;true&apos;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With these dynamic tags and a default &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.rspec&lt;/code&gt; with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--tag ~solved:true&lt;/code&gt; we can now run either the flaky or stable suite.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;flaky: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bundle exec rspec&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;stable: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bundle exec rspec --tag solved&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;example-solving-a-flaky-spec&quot;&gt;Example: Solving A Flaky Spec&lt;/h1&gt;

&lt;p&gt;Let me show the expected workflow when learning with the project…&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Run the suite and pick a failure that looks interesting…&lt;/li&gt;
  &lt;li&gt;Read the Related Code&lt;/li&gt;
  &lt;li&gt;Modify the Spec, try to fix it&lt;/li&gt;
  &lt;li&gt;Compare your answer to the provided solution (remember the is more than one way to solve many of these issues)&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;1-pick-a-failure&quot;&gt;1. Pick a Failure&lt;/h2&gt;

&lt;p&gt;Let’s run the suite and pick a failure. In this case &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;spec/models/post_example_e_spec.rb&lt;/code&gt;, Post ordered expect many order posts to be in alphabetical order, looks interesting.&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;bundle exec rspec
Run options: exclude {:solved=&amp;gt;true}

Randomized with seed 5788
...Capybara starting Puma...
* Version 4.3.1 , codename: Mysterious Traveller
* Min threads: 0, max threads: 4
* Listening on tcp://127.0.0.1:64214
...FF.FF.FFF.F.FFF

Failures:
...

3) Post post ordered expect many order posts to be in alphabetical order
   Failure/Error: alphabet.each { |el| Post.create!(title: Faker::String.random(2), body: el) }

   ActiveRecord::RecordInvalid:
     Validation failed: Title has already been taken
   # ./spec/models/post_example_e_spec.rb:12:in `block (4 levels) in &amp;lt;top (required)&amp;gt;&apos;
   # ./spec/models/post_example_e_spec.rb:12:in `each&apos;
   # ./spec/models/post_example_e_spec.rb:12:in `block (3 levels) in &amp;lt;top (required)&amp;gt;&apos;
...

Finished in 10.2 seconds (files took 3.33 seconds to load)
21 examples, 11 failures

Failed examples: ...
rspec ./spec/models/post_example_e_spec.rb:10 # Post post ordered expect many order posts to be in alphabetical order
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;2-read-the-related-code&quot;&gt;2. Read the Related Code&lt;/h2&gt;

&lt;p&gt;Let’s take a closer look at the code involved. In this case, from the comments, we see this spec is flaky on its own and doesn’t require the full suite to be flaky.&lt;/p&gt;

&lt;div class=&quot;language-ruby 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;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;rails_helper&apos;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Post&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;validates&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;uniqueness: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;scope&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:ordered&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;body: :asc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;id: :asc&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;end&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Classification: Randomness&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Success Rate: 80%&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Suite Required: false&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;RSpec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;describe&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;type: :model&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;describe&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;post ordered&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;it&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;expect many order posts to be in alphabetical order&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;alphabet&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;a&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;z&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_a&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;alphabet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;el&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;title: &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Faker&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;body: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;el&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;n&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ordered&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;eq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;alphabet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;3-modify-the-spec&quot;&gt;3. Modify The Spec&lt;/h2&gt;

&lt;p&gt;For the above example, since we require the title to be unique, but we are using a small random value… We can see collisions occur. There are a number of ways to solve this, perhaps we don’t need randomness at all in this case!&lt;/p&gt;

&lt;div class=&quot;language-ruby 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;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;rails_helper&apos;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Post&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;validates&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;uniqueness: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;scope&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:ordered&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;body: :asc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;id: :asc&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;end&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Classification: Randomness&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Success Rate: 80%&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Suite Required: false&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;RSpec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;describe&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;type: :model&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;describe&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;post ordered&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;it&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;expect many order posts to be in alphabetical order&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;alphabet&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;a&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;z&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_a&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;alphabet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;el&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;title: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;el&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;body: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;el&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;n&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ordered&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;eq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;alphabet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Run the spec a few times to ensure it now always passes.&lt;/p&gt;

&lt;h2 id=&quot;4-compare-answers&quot;&gt;4. Compare Answers&lt;/h2&gt;

&lt;p&gt;Now you can look at the file in the solutions folder. It will contain a solved spec with additional details explaining why there was an issue, and how it was solved. Occasionally offering more than one solution. Now you can repeat the steps until you have no more errors in the spec suite.&lt;/p&gt;

&lt;h1 id=&quot;using-the-project-for-a-workshop&quot;&gt;Using the Project for A Workshop&lt;/h1&gt;

&lt;p&gt;This is how we turned the project into a code clinic or workshop. We ran it entirely remotely, scheduling multiple 1-hour sessions folks could sign up for. There was a brief set of slides, explaining the project and getting folks bootstrapped and installed. Then we used &lt;a href=&quot;https://support.zoom.us/hc/en-us/articles/206476093-Getting-Started-with-Breakout-Rooms&quot;&gt;Zoom to breakout&lt;/a&gt; into small groups of 4 devs, to solve the specs as a little team. Regrouping at the end to discuss and share our solutions. The workgroup format was broken down like below:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;introduce the project and the workflow (10m)&lt;/li&gt;
  &lt;li&gt;solve a single flaky test with the whole group mob programming style (10m)&lt;/li&gt;
  &lt;li&gt;break up into groups and have the groups solve flaky tests (30m)&lt;/li&gt;
  &lt;li&gt;regroup, share solutions, and discussion (10m)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you try running a workshop, let me know I would be curious how it went.&lt;/p&gt;

&lt;h1 id=&quot;related-links&quot;&gt;Related Links&lt;/h1&gt;

&lt;p&gt;Here are some other helpful links related to flaky Rails tests.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://buildpulse.io/&quot;&gt;Flaky Test Detection&lt;/a&gt;, help detect and find tests causing flakiness&lt;/li&gt;
  &lt;li&gt;make it easier to debug flaky tests, &lt;a href=&quot;https://building.buildkite.com/5-ways-weve-improved-flakey-test-debugging-4b3cfb9f27c8&quot;&gt;5-ways to improve flaky test debugging&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;test-smells, &lt;a href=&quot;https://github.com/testdouble/test-smells&quot;&gt;examples of bad JS and Ruby test patterns&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://sonja.codes/fixing-flaky-tests-like-a-detective&quot;&gt;fixing flaky tests like a detective&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://johnpwood.net/2015/04/23/tips-and-tricks-for-dubugging-and-fixing-slowflaky-capybara-specs/&quot;&gt;Tips and Tricks for Dubugging and Fixing Slow/Flaky Capybara Specs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://marcgg.com/blog/2015/01/05/css-animations-failing-capybara-specs/&quot;&gt;How CSS Animations Can Break Your Tests&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://hackernoon.com/flaky-tests-a-war-that-never-ends-9aa32fdef359&quot;&gt;Flaky Tests - A War that Never Ends&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>What I learned about Software Development from building a Climbing Wall</title>
   <link href="https://www.mayerdan.com/software/2019/11/20/software-climbing-wall"/>
   <updated>2019-11-20T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/software/2019/11/20/software-climbing-wall</id>
   <content type="html">
&lt;blockquote&gt;
  &lt;p&gt;Theo liked to imitate as he was learning to walk.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;tldr-this-post-is-just-for-fun&quot;&gt;TL;DR: This post is just for fun!&lt;/h1&gt;

&lt;p&gt;I didn’t really learn about programming, this is just a catchy title, and I wanted to share a big project I have continued to work on that has nothing to do with programming. I thought I could make a kind of funny post by stringing together a bunch of programming ‘wisdom’ that could really be associated with nearly anything.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/wall_final_pano.jpg&quot; alt=&quot;The Wall&apos;s Current State&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;climbing wall’s current state&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;prototype-iterate-test-and-iterate-more&quot;&gt;Prototype, Iterate, Test, and Iterate More&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Climbing Wall:&lt;/strong&gt; If you aren’t sure how much you will use the wall, or how much effort you want to put into it you can start small. Build and expand over time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Software:&lt;/strong&gt; In software, most of the time these days folks take an agile / lean approach and try to deliver working MVPs along the life of the project to deliver continuous value and learning.&lt;/p&gt;

&lt;h2 id=&quot;prototype&quot;&gt;Prototype&lt;/h2&gt;

&lt;p&gt;I started really small, initially with a &lt;a href=&quot;https://www.amazon.com/Metolius-Project-Training-Board-Swirl/dp/B002N56X8I/ref=sr_1_3?dchild=1&amp;amp;keywords=climbing+board&amp;amp;qid=1574036711&amp;amp;sr=8-3&quot;&gt;training board&lt;/a&gt;. I installed this above the stairs entering the basement.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/climbing_board.jpg&quot; alt=&quot;climbing board&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;A training board lets you strengthen your fingers, and do weird pull-ups&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;iterate&quot;&gt;Iterate&lt;/h2&gt;

&lt;p&gt;I then added a few holds directly into the wall &lt;!--more--&gt; near the original board so I could string together a few moves.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/iterate_collage.jpg&quot; alt=&quot;iterate on wall&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;A first box of bulk climbing holds, a test mounting on a board&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;iterate-again--again&quot;&gt;Iterate Again &amp;amp; Again&lt;/h2&gt;

&lt;p&gt;Then I added a single set of boards on the wall… and started to expand out from there.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/iterate-again-collage.jpg&quot; alt=&quot;expanding the wall&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;empty wall, mounts, one board, and more&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;project-planning&quot;&gt;Project Planning&lt;/h1&gt;

&lt;h2 id=&quot;negotiate-with-the-team&quot;&gt;Negotiate with the Team&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Climbing Wall:&lt;/strong&gt;  I did have to discuss everything with my wife before I started drilling a bunch of holes in our wall. This was a bit of a process, starting with the training board, the wall holes (and an agreement that I would patch any holes… hmm I still have to do some of those. A discussion that became easy as she found she enjoyed adding frequent climbing into her exercise routine as much as I did.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Software:&lt;/strong&gt; In software you are always working with stakeholders, PMs, designers, other developers, and hopefully directly with some customers. Nearly everything built requires negotiation and compromises on time, features, UX, etc.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/erin_wall_reach.jpg&quot; alt=&quot;negotiation with my partner&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;negotiation with my partner&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;it-starts-with-a-plan&quot;&gt;It Starts with A Plan&lt;/h2&gt;

&lt;p&gt;Initially, I started with just a small idea, but it expanded out, especially after my wife decided she also really enjoyed climbing on the wall as well.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/planning_COLLAGE.jpg&quot; alt=&quot;planning and materials&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;planning and materials&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;ensure-your-project-has-a-reasonable-learning-curve&quot;&gt;Ensure Your Project has a Reasonable Learning Curve&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Climbing Wall:&lt;/strong&gt; On a climbing wall you want it to be fun for beginners, kids, and more experienced climbers. Just like in software you want a project to be accessible and learnable by new hires and developers of different skill levels.&lt;/p&gt;

&lt;p&gt;Our wall was a big hit with so many kids, I have added a lot of easier holds and built a lower route so small children can jump on the wall and have a great time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Software:&lt;/strong&gt; You can’t build a team, recruit, and mentor folks if you have a software project that is all expert level. Ensure it is easy to set up the apps development environment and it is easy to add features, test, and deploy safely so new folks can learn with confidence.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/theo_climbing_2.jpg&quot; alt=&quot;Theo Wall Climb&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;Theo Climbing, click for &lt;a href=&quot;/assets/img/theo_climb.mov&quot;&gt;video&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;set-milestones&quot;&gt;Set Milestones&lt;/h2&gt;

&lt;p&gt;On personal projects as in software, you want to set short, medium, &amp;amp; long term goals.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Short: something at home to help with training for climbing&lt;/li&gt;
  &lt;li&gt;Medium: I want to connect all the reasonable basement walls for a long route&lt;/li&gt;
  &lt;li&gt;Long: I want to connect the original training board to the route along with ceiling holds&lt;/li&gt;
  &lt;li&gt;Longer: I want to add a removable incline that can add a step grade when good climbers are visiting&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/ceiling_COLLAGE.jpg&quot; alt=&quot;Ceiling Holds&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;Adding Ceiling Holds was a longer-term goal&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;celebrate-your-wins&quot;&gt;Celebrate Your Wins&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Climbing Wall:&lt;/strong&gt; In general, whenever I expanded the climbing wall, I would quickly add some holds and celebrate by climbing my new longer route.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Software:&lt;/strong&gt; Your team should be proud and get to celebrate after shipping something big. Also, ensure developers are sharing the things they learned along the release with the team. Having space to make investments and to pay down debt, requires that everything can’t always be moving at maximum speed all the time. Celebrate the progress folks are making.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/iteration-ANIMATION.gif&quot; alt=&quot;climbing in progress&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;Climbing the Routes as the wall is in progress&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/erin_in_progress.jpg&quot; alt=&quot;Erin in progress&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;Erin testing our new “door crossing” problem&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;plan-for-growth&quot;&gt;Plan For Growth&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Climbing Wall:&lt;/strong&gt; On the wall, I would build and leave space to add more when I had the time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Software:&lt;/strong&gt; In software, you want code that is flexible and easy to adapt. This doesn’t mean to over optimize, but know when to be specific and when to offer flexibility (the rule or threes can help with this).&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/build_with_space.jpg&quot; alt=&quot;gaps when I was low on supplies&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;Gaps When, low on Supplies&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;make-it-work-make-it-right-make-it-fast&quot;&gt;Make it work, make it right, make it fast&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Climbing Wall:&lt;/strong&gt; The wall was built to keep up and extend my skill level… When I didn’t have the parts or the time, I would sometimes make something fast and leave gaps to extend the routes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Software:&lt;/strong&gt; In software, make sure you can get it working, this ensures you solve the hard problems. Then make it right soft the edge cases and the gotchas. Then make it fast and scalable.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/climbing_routes.jpg&quot; alt=&quot;color coded climbing routes&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;color coded climbing routes&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;learning--growth&quot;&gt;Learning &amp;amp; Growth&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Climbing Wall:&lt;/strong&gt; As I worked on the wall project I became better with tools, building, designing routes, &amp;amp; more. I got comfortable and started to think up some more complex projects.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Software:&lt;/strong&gt; Software takes practice, you will get better the more you build things. Learning which practices to follow and which don’t scale well.&lt;/p&gt;

&lt;h2 id=&quot;you-can-learn-anything&quot;&gt;You Can Learn Anything&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Climbing Wall:&lt;/strong&gt; I seriously know very little about building things, tools, construction, or really even climbing. All the information you need is available online to learn so much about any topic that interests you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Software:&lt;/strong&gt; You are always learning in software. A new framework, language, domain, etc… The field changes so fast that you have to keep learning to stay up to date. To know when something is a fad or is really worth investing time in deep learning.&lt;/p&gt;

&lt;h2 id=&quot;you-get-better-with-practice&quot;&gt;You Get Better with Practice&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Climbing Wall:&lt;/strong&gt; Originally, I barely knew what size drill bits, bolts, nuts, and holds… Now I can put all this together and set up a new board in almost no time at all.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Software:&lt;/strong&gt; It is good to keep practicing… Often this is how you learn to navigate all the grey areas of programming. The best solution to a problem isn’t always black and white, often the best practices have edge cases… Learning what to bend and what to break comes with practice and experience. Feeling the pain of maintaining systems over time, knowing what will stick around and what code often just gets removed.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/building_COLLAGE.jpg&quot; alt=&quot;Wall Teamword&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;over time, building became faster&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;it-takes-a-team&quot;&gt;It Takes A Team&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Climbing Wall:&lt;/strong&gt; You can’t hold up 4x4 plywood and drill it in yourself… Building a climbing wall requires teamwork. Collaboration to successfully complete the project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Software:&lt;/strong&gt; In software, most projects can’t be done by a single developer anymore. It takes collaboration, coordination, and teamwork to build something that lasts.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/wall_teamwork.jpg&quot; alt=&quot;Wall Teamword&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;A few of the friends who have helped out&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;learn-from-prior-art&quot;&gt;Learn From Prior Art&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Climbing Wall:&lt;/strong&gt; I read a number of things to learn how to build a climbing wall, this free &lt;a href=&quot;https://www.atomikclimbingholds.com/how-to-build-a-rock-climbing-wall&quot;&gt;build a climbing wall e-book&lt;/a&gt; from Atomik is great. No reason to try to learn from scratch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Software:&lt;/strong&gt; Not often are you building a program from scratch with no prior art. Learn from existing frameworks, applications, books, and open source. Build on the shoulders of giants as they say.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/google_climbing_wall.png&quot; alt=&quot;Google Image&apos;s Climbing Wall Teamword&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;&lt;a href=&quot;https://www.google.com/search?hl=en&amp;amp;tbm=isch&amp;amp;sxsrf=ACYBGNTkWwzduyRKONfKB9imoTsdKUe4GQ%3A1574220971131&amp;amp;source=hp&amp;amp;biw=931&amp;amp;bih=910&amp;amp;ei=q7TUXez-BIH0-gT5iaXYBg&amp;amp;q=diy+climbing+wall&amp;amp;oq=diy+climbing&amp;amp;gs_l=img.3.0.0l10.1881.3470..4288...0.0..0.94.872.12......0....1..gws-wiz-img.......0i131.KNPm58-za0M&quot;&gt;need inspiration? google climbing walls&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;start-cheap-and-upgrade-later&quot;&gt;Start Cheap and Upgrade Later&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Climbing Wall:&lt;/strong&gt;  I started with some &lt;a href=&quot;https://www.amazon.com/Rocky-Mountain-Climbing-Gear-QH-G3IN-ZLO6/dp/B0054RVYZ4/ref=sr_1_18?dchild=1&amp;amp;keywords=climbing+holds&amp;amp;qid=1574049341&amp;amp;sr=8-18&quot;&gt;cheap holds&lt;/a&gt;, but over time I upgraded to nicer holds over time as I spent more time on the wall and as I expanded it. In the end, I really love &lt;a href=&quot;https://www.atomikclimbingholds.com/&quot;&gt;atomik climbing holds&lt;/a&gt;, and I buy most of my equiptment there.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Software:&lt;/strong&gt; In a software startup or a feature, you want to find the fastest and cheapest way to verify the value of something. When you know there is a value and have been able to build something sustianable (or I guess in startup world, with a hockey stick growth), you might want to move on from “it works” to it is best in class… Particularly, for things that aren’t part of the core company business value.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/holds-COLLAGE.jpg&quot; alt=&quot;Wall Teamword&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;Cheap bulk grey holds, later upgraded to various specialty holds&lt;/p&gt;
&lt;/blockquote&gt;
</content>
 </entry>
 
 <entry>
   <title>Safely Removing Image Assets from Rails</title>
   <link href="https://www.mayerdan.com/ruby/2019/10/28/safely-removing-image-assets-from-rails"/>
   <updated>2019-10-28T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/ruby/2019/10/28/safely-removing-image-assets-from-rails</id>
   <content type="html">
&lt;blockquote&gt;
  &lt;p&gt;photo &lt;!--more--&gt; credit &lt;a href=&quot;https://pixabay.com/photos/vacuum-cleaner-vacuuming-cleaning-268179/&quot;&gt;cleaning: pixabay&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;why-cleanup-rails-image-assets&quot;&gt;Why Cleanup Rails Image Assets?&lt;/h1&gt;

&lt;p&gt;Why would we want to more safely delete image assets?&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;a clean repo is easier to jump into and maintain over time&lt;/li&gt;
  &lt;li&gt;cruft that isn’t in use can be confusing over time&lt;/li&gt;
  &lt;li&gt;image assets can slow down your test and deploy pipeline
    &lt;ul&gt;
      &lt;li&gt;Rails tests frequently need to dynamically or initialize building all assets, this is often a slow hit on a Rails test suite&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Deployment needs to work with assets as well, often multiple steps
    &lt;ul&gt;
      &lt;li&gt;building all assets &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rake assets:precompile&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;compressing asset bundle&lt;/li&gt;
      &lt;li&gt;uploading assets to a CDN&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of this time adds up, assets compilation on a large legacy Rails app still adds around 40 seconds to the build time, down from 1m30s in the past. While asset preparation and deployment before cleanup and optimization of that flow and files was adding over 3 minutes to our deploy time.&lt;/p&gt;

&lt;h1 id=&quot;how-to-safely-delete-image-assets&quot;&gt;How To Safely Delete Image Assets&lt;/h1&gt;

&lt;p&gt;OK, hopefully now you would love to delete all the images in your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;app/assets/images&lt;/code&gt; folder that you don’t use… What images are safe to delete or out of use? I have looked at a number of ideas for this.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;grepping with various scripts&lt;/li&gt;
  &lt;li&gt;using log aggregation search results to ensure no hits were being made of an image asset&lt;/li&gt;
  &lt;li&gt;using Sprockets options, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;unknown_asset_fallback&lt;/code&gt; alone, doesn’t make you entirely safe…&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What I wanted was a way to delete a folder of images or a single image that I believed was no longer in use, but have the build fail if there was any reference to the image. I wanted Rails to fail in these cases:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;a page is loaded in dev mode referencing a missing asset&lt;/li&gt;
  &lt;li&gt;tests are run against a page referencing a missing asset (ActionDispatch::IntegrationTest, request spec, etc)&lt;/li&gt;
  &lt;li&gt;bundle exec rake assets:precompile&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;sprockets-unknown-asset-fallback&quot;&gt;Sprockets Unknown Asset Fallback&lt;/h3&gt;

&lt;p&gt;Not surprisingly, other folks have wanted this and Sprockets has a built-in option &lt;a href=&quot;https://github.com/rails/sprockets-rails#initializer-options&quot;&gt;config.assets.unknown_asset_fallback&lt;/a&gt;, which gets close to what I wanted. From the docs, this option claims to:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;When set to a truthy value, a result will be returned even if the requested asset is not found in the asset pipeline. When set to a falsey value it will raise an error when no asset is found in the pipeline. Defaults to true.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So let’s set it to false: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rails.application.config.assets.unknown_asset_fallback = false&lt;/code&gt;. Now if you have a deleted image referenced like below:&lt;/p&gt;

&lt;div class=&quot;language-ruby 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;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;%= image_tag(&quot;deleted_image.svg&quot;) %&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You will get an error when visiting the page or running tests:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;bundle &lt;span class=&quot;nb&quot;&gt;exec &lt;/span&gt;rake

...S.......E

Error:
HomeControllerTest#test_should_get_index:
ActionView::Template::Error: The asset &lt;span class=&quot;s2&quot;&gt;&quot;deleted_image.svg&quot;&lt;/span&gt; is not present &lt;span class=&quot;k&quot;&gt;in &lt;/span&gt;the asset pipeline.
    app/views/home/index.html.erb:6:in &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;_app_views_home_index_html_erb___957919561084124106_70092585694780&lt;span class=&quot;s1&quot;&gt;&apos;
    test/controllers/home_controller_test.rb:5:in `block in &amp;lt;class:HomeControllerTest&amp;gt;&apos;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This doesn’t make one entirely safe, as images that are referenced in your scss, css, or other styles would still not cause an error. They would silently lead to broken images.&lt;/p&gt;

&lt;h3 id=&quot;patch-to-force-asset-compilation-to-fail-on-unknown-assets&quot;&gt;Patch To Force Asset Compilation To Fail on Unknown Assets&lt;/h3&gt;

&lt;p&gt;Sadly, I couldn’t find any option or configuration that would cause compiling stylesheets to fail. I thought this would block my goal of being able to remove a ton of assets safely with confidence… Well, after lots of digging, I figured out how to patch &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sprockets-rails&lt;/code&gt; so that it will blow up and fail when it references an unknown asset.&lt;/p&gt;

&lt;p&gt;You can apply this patch in your: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config/initializers/assets.rb&lt;/code&gt;&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/danmayer/96ec7c37d1a775e021deea88fd804429.js&quot;&gt;&lt;/script&gt;

&lt;p&gt;Now if you have a file in your styles, like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;app/assets/stylesheets/application.scss&lt;/code&gt; reference an image, your asset pipeline will blow up when the image is missing.&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;.broken-image-demo {
  background-image: image-url(&apos;deleted_image.svg&apos;);
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Depending on how your tests run, they will fail when precompiling assets, or a failure will occur when you call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rake assets:precompile&lt;/code&gt; as shown below.&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;bundle exec rake assets:precompile
...
Done in 1.32s.
rake aborted!
Sprockets::Rails::Helper::AssetNotFound: path not resolved: deleted_image.svg
/Users/danmayer/projects/coverband_demo/config/initializers/assets.rb:56:in `rescue in compute_asset_path&apos;
/Users/danmayer/projects/coverband_demo/config/initializers/assets.rb:51:in `compute_asset_path&apos;
/Users/danmayer/.rvm/gems/ruby-2.6.2/gems/actionview-5.2.2.1/lib/action_view/helpers/asset_url_helper.rb:200:in `asset_path&apos;
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;asset-failure-demo&quot;&gt;Asset Failure Demo&lt;/h1&gt;

&lt;p&gt;If you want to see this in action, feel free to clone &lt;a href=&quot;https://github.com/danmayer/coverband_demo&quot;&gt;coverband demo&lt;/a&gt;. Install gems and get the tests passing. Then read the comments and run tests or compile assets when uncommenting the example lines to trigger the expected errours. Key Files:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/danmayer/coverband_demo/blob/master/config/initializers/assets.rb&quot;&gt;config/initializers/assets.rb&lt;/a&gt;, this shows all the setuo and configuration needed&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/danmayer/coverband_demo/blob/master/app/assets/stylesheets/application.scss#L35&quot;&gt;app/assets/stylesheets/application.scss&lt;/a&gt;, an example of stylesheets referencing a missing image&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/danmayer/coverband_demo/blob/master/app/views/home/index.html.erb#L6&quot;&gt;app/views/home/index.html.erb&lt;/a&gt;, an example of a view file referencing a broken image&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;a-final-note&quot;&gt;A Final Note&lt;/h1&gt;

&lt;p&gt;On an old legacy application we were able to delete over 50% of the total asset disk size, by clearing out old unused image assets. This made it easier to find and navigate our assets folder, and it significantly sped up both our test suite and deployment. While I wouldn’t expect most projects to have as much image cruft sitting around, especially with older applications, it is easy for these unused assets to really build up over time.&lt;/p&gt;

&lt;p&gt;While the above, should make it easier to delete image assets and do housekeeping yourself, it still takes a bit of time. You need to go through a process:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;find a likely set of unused images&lt;/li&gt;
  &lt;li&gt;delete them, run tests&lt;/li&gt;
  &lt;li&gt;add back images that were still used&lt;/li&gt;
  &lt;li&gt;repeat until satisfied&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This obviously looks like a process that can be automated to help you clean up all your image assets automatically. That is totally true, and I will cover how to do that in a future post. This post covers what is a prerequisite to being able to automate the cleanup, ensuring that your app will properly and very loudly fail when an image was removed which is still required.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Flaky Ruby Tests</title>
   <link href="https://www.mayerdan.com/ruby/2019/09/07/flaky-ruby-tests"/>
   <updated>2019-09-07T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/ruby/2019/09/07/flaky-ruby-tests</id>
   <content type="html">
&lt;h1 id=&quot;restoration-of-a-ruby-test-suite&quot;&gt;Restoration of a Ruby Test Suite&lt;/h1&gt;

&lt;p&gt;I want to talk about some recent work to restore a Rails app’s test suite to a usable state. The goal went beyond the test suite, to restoring trust in the continuous deployment pipeline, but this post will mostly focus on the Rspec suite. The reason I started on this work was that the current state of deployment was “dangerous”, various folks preferred to avoid the project as it was difficult to work in &lt;!--more--&gt; and release, but still critical to our overall architecture. At it’s very worst, deploys were taking over 30 minutes, with a failure rate of the deployment pipeline of 45%. The issue became clear and high priority to me when one day, I had two small PRs to release, due to bad luck with the failure rate, it took me nearly 6 hours to get my changes deployed. A constant distraction that dragged on through meetings, and other work. Making the pains of the team extremely clear and personal, I decided an effort to get things back into a safe state should be taken on.&lt;/p&gt;

&lt;p&gt;I wanted to share some of what I learned as there has been some recent discussion in the Ruby community about fixing flaky tests &lt;a href=&quot;https://twitter.com/samsaffron/status/1125907310109323266&quot;&gt;@samsaffron&lt;/a&gt; &lt;a href=&quot;https://twitter.com/tenderlove/status/1167478551370592256&quot;&gt;@tenderlove&lt;/a&gt; &lt;a href=&quot;https://twitter.com/ctietze/status/1137064436961820673&quot;&gt;@ctietze&lt;/a&gt; &lt;a href=&quot;https://twitter.com/rubylandnews/status/1121554960142012416&quot;&gt;@SonjaBPeterson&lt;/a&gt; &lt;a href=&quot;https://twitter.com/saramic/status/1143617541048172544&quot;&gt;@saramic&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote class=&quot;twitter-tweet&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Over the years one of the most complex liabilities we carried in our test suite has been flaky tests. This problem is so hard some people just give up. We are slowly cataloging the problems here: &lt;a href=&quot;https://t.co/7AdwOGNfNw&quot;&gt;https://t.co/7AdwOGNfNw&lt;/a&gt; , I hope to write about it.&lt;/p&gt;&amp;mdash; Sam Saffron (@samsaffron) &lt;a href=&quot;https://twitter.com/samsaffron/status/1125907310109323266?ref_src=twsrc%5Etfw&quot;&gt;May 7, 2019&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async=&quot;&quot; src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;

&lt;h1 id=&quot;a-restoration-plan&quot;&gt;A Restoration Plan&lt;/h1&gt;

&lt;p&gt;Taking a step back, thinking about what needed to happen and how to get there was the first step. I thought this would fit in well to the old agile advice…&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Make It Work. Make It Right. Make It Fast.&lt;/p&gt;
  &lt;ul&gt;
    &lt;li&gt;&lt;a href=&quot;https://thetombomb.com/2018/05/17/make-it-work-make-it-right-make-it-fast/&quot;&gt;Kent Beck&lt;/a&gt;&lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;make-it-work&quot;&gt;Make It Work&lt;/h1&gt;

&lt;p&gt;What didn’t work about the deployment? In my opinion, it was broken because:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;doesn’t meet a 95% or better success rate&lt;/li&gt;
  &lt;li&gt;deploys are too slow, to watch and review if changes succeeded, 10 minutes of less&lt;/li&gt;
  &lt;li&gt;test suite relying on CI parallelism is to slow to ever run locally, local suite run needs to be possible in 1hr or less.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With a definition of what success looks like to make it work, then I was able to start to dig into the details of how to get there. &lt;/p&gt;

&lt;h2 id=&quot;delete-flaky-tests-that-arent-valuable&quot;&gt;Delete Flaky Tests That Aren’t Valuable&lt;/h2&gt;

&lt;p&gt;This is often a hard one for some teams. An honest discussion of the purpose and value of tests is likely needed. I found good success by having a small PR removing a few flaky tests, and pointing to similar tests in the suite that exercised functionality in a more reliable way. For example, moving from a complete feature spec that tested several endpoints in a long workflow, to a couple of feature tests exercising individual endpoints, along with unit tests for the underlying service providers. The team might need to have a discussion ahead of time, or you might be surprised that others quickly agree that really flaky tests aren’t providing value.&lt;/p&gt;

&lt;blockquote class=&quot;twitter-tweet&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Flaky tests are worse than useless tests&lt;/p&gt;&amp;mdash; Aaron Patterson (@tenderlove) &lt;a href=&quot;https://twitter.com/tenderlove/status/1167478551370592256?ref_src=twsrc%5Etfw&quot;&gt;August 30, 2019&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async=&quot;&quot; src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;

&lt;h2 id=&quot;fixing-flaky-tests&quot;&gt;Fixing Flaky Tests&lt;/h2&gt;

&lt;p&gt;The primary cause of our unstable deploy pipeline was flaky tests. Given we were having deployments fail 45% of the time we had a large number of flaky tests causing issues. Let’s dive into some of the techniques for resolving flaky test failures.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;ul&gt;
    &lt;li&gt;&lt;a href=&quot;https://www.flickr.com/photos/andrewhalliday/4599220963/in/photostream/&quot;&gt;andrewhalliday&lt;/a&gt;&lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;divide-and-conquer-with-rspec-retry&quot;&gt;Divide and Conquer, with Rspec-Retry&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;Initially quarantine helps to reduce their damage to other tests, but you still have to fix them soon.&lt;/p&gt;
  &lt;ul&gt;
    &lt;li&gt;&lt;a href=&quot;https://martinfowler.com/articles/nonDeterminism.html&quot;&gt;Martin Fowler, Eradicating Non-Determinism in Tests&lt;/a&gt;&lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;Since before I joined the project, it has used &lt;a href=&quot;https://github.com/NoRedInk/rspec-retry&quot;&gt;rspec-retry&lt;/a&gt; as a way to isolate some flaky tests. Yes, this is a band-aid, but in terms of getting back to a “make it work” baseline, it is a wonderful tool. This is how I used it. For a period of about a month, I watched every failure on our CI spec suite. Every time a test failed more than once, I would add it to an internal wiki marking the test as flaky. Myself and other folks from the team, when they had time, would “adopt” a test and try to fix it, if one timeboxed an hour or two and couldn’t figure out and fix the underlying issue, we would tag it flaky, so that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rspec-retry&lt;/code&gt; would run the test multiple times trying to achieve success. We ran our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;flaky&lt;/code&gt; tag specs in a special CI job, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bundle exec rspec --tag retry_flaky_test&lt;/code&gt; isolated from our other tests. This CI job had a success rate of 99%, so the flaky tests would pass on retry, and be split off from others. Then with logging, and debugging we could dig in deeper in resolve the underlying issues and move the test back into the standard test suite. This is great because it very quickly got the test suite back into usable condition and tags all the future work still needing to be addressed and captures metrics about which tests are most flaky, or no longer are flaky as we resolve issues. At our current stage, we still need to go back and resolve a number of flaky tests, but they no longer slow down or block our CI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Isolate Flaky Test Recipe:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;capture data and identify flaky tests (use metric tracking, or just eyeball if from your CI history)&lt;/li&gt;
  &lt;li&gt;quickly try to fix them timeboxed to a short amount of time&lt;/li&gt;
  &lt;li&gt;if you can’t fix them, tag them for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rspec-retry&lt;/code&gt;, to isolate them and remove them as a blocker for CI&lt;/li&gt;
  &lt;li&gt;Find a way to distribute this process across folks on the team, and explain the plan on how to follow through with cleanup.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;fix-the-worst-offenders&quot;&gt;Fix the Worst Offenders&lt;/h3&gt;

&lt;p&gt;From above you likely will find some worst offender tests or hopefully patterns that impact multiple tests. These even with flaky test &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rspec-retry&lt;/code&gt; may still fail to often to be reliable. If you dig into a few of the tests (during the timeboxing) you are likely to find some patterns. For example &lt;a href=&quot;https://twitter.com/TildeWill&quot;&gt;@TildeWill&lt;/a&gt;, fixed a whole class of flaky tests related to Capybara negative matchers. We also fixed entire categories of failing tests that weren’t properly using &lt;a href=&quot;https://github.com/teamcapybara/capybara#asynchronous-javascript-ajax-and-friends&quot;&gt;Capybara’s asynchronous matchers&lt;/a&gt;, each of these fixes added around 6% to the success rate of our CI suite per PR.&lt;/p&gt;

&lt;h3 id=&quot;common-flaky-test-issues&quot;&gt;Common Flaky Test Issues&lt;/h3&gt;

&lt;p&gt;I won’t cover all the various types of flaky tests in as much detail as you can find in &lt;a href=&quot;https://twitter.com/samsaffron&quot;&gt;@samsaffron&lt;/a&gt;’s post, &lt;a href=&quot;https://samsaffron.com/archive/2019/05/15/tests-that-sometimes-fail&quot;&gt;Tests that sometimes fail&lt;/a&gt;. Here are some of the most common issues we found while resolving issues.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;fix timing issues (timezone dependant)&lt;/li&gt;
  &lt;li&gt;stale state issues due to non DB stores (global vars, redis, memcache, Rails.cache, etc)&lt;/li&gt;
  &lt;li&gt;fix order / dependency issues… Tests that only pass in a specific order
    &lt;ul&gt;
      &lt;li&gt;running specs in documentation mode can really help find ordering issues… Run it this way every time so you have a clear log of what ran when ``time bundle exec rspec –format documentation`&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Capybara
    &lt;ul&gt;
      &lt;li&gt;devise auth issues &lt;a href=&quot;https://github.com/plataformatec/devise/wiki/How-To:-Test-with-Capybara&quot;&gt;1&lt;/a&gt; &lt;a href=&quot;https://github.com/plataformatec/devise/wiki/How-To:-sign-in-and-out-a-user-in-Request-type-specs-(specs-tagged-with-type:-:request)&quot;&gt;2&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;not using the &lt;a href=&quot;https://github.com/teamcapybara/capybara#asynchronous-javascript-ajax-and-friends&quot;&gt;aysnch matchers&lt;/a&gt;
        &lt;ul&gt;
          &lt;li&gt;example:  &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;expect(page.current_url).to eq(/something/)&lt;/code&gt; is bad, switch to waiting version &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;expect(page).current_path.to eq(/something/)&lt;/code&gt; which is good.&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;using sleep opposed to correct waiting matchers&lt;/li&gt;
      &lt;li&gt;in geneal &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wait_for_ajax&lt;/code&gt; is dangerous&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;VCR allowing non-stubbed network requests can be dangerous, try to get your suite passing with, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VCR.configure { |c| c.allow_http_connections_when_no_cassette = false }&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;tips-and-tools-to-fix-flaky-tests&quot;&gt;Tips and Tools to Fix Flaky Tests&lt;/h3&gt;

&lt;p&gt;A few tips to help debug and fix flaky tests. I found each of these scripts extremely valuable in moving forward our success rate.&lt;/p&gt;

&lt;h4 id=&quot;single-flaky-test-case&quot;&gt;Single Flaky Test Case&lt;/h4&gt;

&lt;p&gt;Quickly, verify a test that fails randomly even in isolation, with this great tip.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;img src=&quot;/assets/img/spec_check.png&quot; alt=&quot;Bugs&quot; /&gt; &lt;br /&gt;
from &lt;a href=&quot;https://twitter.com/_swanson/status/1161734593730568198&quot;&gt;@_swanson&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4 id=&quot;single-flaky-spec-file&quot;&gt;Single Flaky Spec File&lt;/h4&gt;

&lt;p&gt;Quickly, check the success rate of a single file. I use this to report a before and after of a fix in a PR.&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/danmayer/d28c98a6b6cd2882bf90102bd76f0cbf.js&quot;&gt;&lt;/script&gt;

&lt;h4 id=&quot;calculate-success-rate-of-a-branch-on-circleci&quot;&gt;Calculate Success Rate of a Branch on CircleCI&lt;/h4&gt;

&lt;p&gt;Push a fix on a branch and run it a number of times to see if it improves the overall success rate.&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/danmayer/4f3dd95adf2d01fb5b37b45b6ddc2515.js&quot;&gt;&lt;/script&gt;

&lt;h3 id=&quot;additional-resources-on-resolving-flaky-tests&quot;&gt;Additional Resources on Resolving Flaky Tests&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://buildpulse.io/&quot;&gt;BuildPulse&lt;/a&gt;, software service that helps identify flaky tests.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://devhints.io/capybara&quot;&gt;Capybara Cheat Sheet&lt;/a&gt;, ensure you are following the best practices, to avoid common mistakes.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://thoughtbot.com/blog/write-reliable-asynchronous-integration-tests-with-capybara&quot;&gt;Write Reliable, Asynchronous Integration Tests With Capybara&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/ngauthier/capybara-slow_finder_errors&quot;&gt;Capybara::SlowFinderErrors&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.salsify.com/blog/engineering/tearing-capybara-ajax-tests&quot;&gt;Tearing Down Capybara Tests of AJAX Pages&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;make-it-right&quot;&gt;Make It Right&lt;/h1&gt;

&lt;p&gt;Most of the remaining work on restoring this test suite now falls into this category. The deployment pipeline succeeds at over a 95% success rate at around 10m. These are acceptable numbers for our project. What we haven’t done is resolve all of the flaky tests which pass because of retry attempts. Until we can move all the tests to be fully reliable there is work to be done. &lt;/p&gt;

&lt;h1 id=&quot;make-it-fast&quot;&gt;Make It Fast&lt;/h1&gt;

&lt;p&gt;I will dive into this more in a future article, but with some effort, the team was able to get our CI deployment pipeline down from over 30m on avg to only taking 10m on average. The CI jobs to run just our tests are down around 5m, with the full deployment jobs taking longer. I expect as we continue to make improvements and fix some of the known bad actors in our test suite, this number will continue to go down. Why did we make so much progress on “Make It Fast” before finishing “Make It Right”? Well, we needed a better and faster feedback loop to find and fix flaky tests, as well as to make it right. A fast feedback loop is really required to make progress quickly. Until we could increase the iteration cycles, we could only have so many flaky fix PRs make it through the pipeline in a day, and at the beginning testing locallt wasn’t really possible. In terms of make it fast, I did want to mention there are still two efforts under way.&lt;/p&gt;

&lt;h2 id=&quot;local-test-suite-speed&quot;&gt;Local Test Suite Speed&lt;/h2&gt;

&lt;p&gt;If the test suite it to slow to ever run locally it is also hard to test and ensure it reliably runs anywhere other than on CI. Initially, the test suite was so slow, it would either crash or stall out most of the time. Occasionally with many failures, it would complete after 9+ hours… After using CI to drive most of the fixes, now the local spec suite reliably runs on a single machine in 45 minutes. This is still far to slow for my liking but is headed in the right direction.&lt;/p&gt;

&lt;h2 id=&quot;deployment-pipeline-improvements&quot;&gt;Deployment Pipeline Improvements&lt;/h2&gt;

&lt;p&gt;The CI deployment pipeline is the test suite, but also much more. This article isn’t going to focus on deployment improvements but without changes related to the tests or fixing flaky test failures. Various improvements cut our deployment in a third, I will detail this more in a future article. This involved breaking down all the continuous deployment steps finding inefficiencies, redundancy, and improving parallelization.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Building SVG Badges in Ruby</title>
   <link href="https://www.mayerdan.com/ruby/2019/04/10/building-svg-badges"/>
   <updated>2019-04-10T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/ruby/2019/04/10/building-svg-badges</id>
   <content type="html">
&lt;h1 id=&quot;building-svg-badges-in-ruby&quot;&gt;Building SVG Badges in Ruby&lt;/h1&gt;

&lt;p&gt;A while ago I needed to create a simple dynamic SVG. After a bit of tinkering, it was easy enough to build a simple Ruby class to do what I needed to do.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;SVG created from the below code&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It was a super quick thing to put together and solved a problem I was having. I was reminded of that today when I had another quick issue that I wanted to resolve. I wanted to pull some data not available by a services APIs, the data I needed was easily accessible to me in their webviews, so I quickly hacked together a web scraper, pulling the data I needed into a google sheet.&lt;/p&gt;

&lt;h2 id=&quot;being-a-developer-unblocks-you&quot;&gt;Being a Developer, Unblocks You&lt;/h2&gt;

&lt;p&gt;A thing I have always loved about being a developer is you can solve your own problems. You have to be careful to not get sucked into it and wasting a bunch of time, but you also aren’t blocked just because something you need isn’t already available. If you build a quick hack ensure it is just that a quick hack and that you won’t regret putting it into your workflow. The below SVG example was a tiny helper for devs, while today’ journey helped pull metrics on CircleCI related to metrics I want to pull for myself monthly. In either case, if it breaks it is no issue and can be fixed in a few minutes.&lt;/p&gt;

&lt;p&gt;Being able to solve the problems you run into along the way is one of the great parts of being a developer.&lt;/p&gt;

&lt;h3 id=&quot;full-ruby-svg-badge-code&quot;&gt;Full Ruby SVG Badge Code&lt;/h3&gt;

&lt;div class=&quot;language-ruby 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;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SvgFormatter&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;output&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@output&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;output&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;STDOUT&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;percentage&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;SOME_DATA_SOURCE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;round&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;badge/results.svg&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;w&apos;&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;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;percentage&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;rescue&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;RuntimeError&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@output&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;message&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@output&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;SvgFormatter was unable to generate a badge.&apos;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;kp&quot;&gt;private&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;percentage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;file_content&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;~&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;SVGTEMPLATE&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
      &amp;lt;?xml version=&quot;1.0&quot;?&amp;gt;
      &amp;lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;90&quot; height=&quot;20&quot;&amp;gt;
        &amp;lt;linearGradient id=&quot;a&quot; x2=&quot;0&quot; y2=&quot;100%&quot;&amp;gt;
          &amp;lt;stop offset=&quot;0&quot; stop-color=&quot;#bbb&quot; stop-opacity=&quot;.1&quot;/&amp;gt;
          &amp;lt;stop offset=&quot;1&quot; stop-opacity=&quot;.1&quot;/&amp;gt;
        &amp;lt;/linearGradient&amp;gt;
        &amp;lt;rect rx=&quot;3&quot; width=&quot;90&quot; height=&quot;20&quot; fill=&quot;#555&quot;/&amp;gt;
        &amp;lt;rect rx=&quot;3&quot; x=&quot;51&quot; width=&quot;39&quot; height=&quot;20&quot; fill=&quot;#007ec6&quot;/&amp;gt;
        &amp;lt;rect rx=&quot;3&quot; width=&quot;90&quot; height=&quot;20&quot; fill=&quot;url(#a)&quot;/&amp;gt;
        &amp;lt;g fill=&quot;#fff&quot; text-anchor=&quot;middle&quot; font-family=&quot;DejaVu Sans,Verdana,Geneva,sans-serif&quot; font-size=&quot;11&quot;&amp;gt;
          &amp;lt;text x=&quot;24.5&quot; y=&quot;15&quot; fill=&quot;#010101&quot; fill-opacity=&quot;.3&quot;&amp;gt;Title&amp;lt;/text&amp;gt;
          &amp;lt;text x=&quot;24.5&quot; y=&quot;14&quot;&amp;gt;Title&amp;lt;/text&amp;gt;
          &amp;lt;text x=&quot;68.5&quot; y=&quot;15&quot; fill=&quot;#010101&quot; fill-opacity=&quot;.3&quot;&amp;gt;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;percentage&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;%&amp;lt;/text&amp;gt;
          &amp;lt;text x=&quot;69.5&quot; y=&quot;14&quot;&amp;gt;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;percentage&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;%&amp;lt;/text&amp;gt;
        &amp;lt;/g&amp;gt;
      &amp;lt;/svg&amp;gt;
&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;    SVGTEMPLATE&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;file_content&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Zola Electric Career</title>
   <link href="https://www.mayerdan.com/ruby/2019/02/14/zola-electric"/>
   <updated>2019-02-14T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/ruby/2019/02/14/zola-electric</id>
   <content type="html">
&lt;h1 id=&quot;zola-electric-career&quot;&gt;Zola Electric Career&lt;/h1&gt;

&lt;p&gt;Tomorrow is my last day at &lt;a href=&quot;http://offgrid-electric.com/&quot;&gt;Zola Electric&lt;/a&gt;, it is time for a different challenge. It has been an amazing ride over the last 4+ years. Helping grown from a single country with non connected devices to smart devices that can power a large variety of appliances. During my time with Zola it has grown to provide energy access to over 1 million people without grid access in Africa. It has been a rewarding and challenging experience. I wanted to share my favorite story from working at Zola over the last few years.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;My first installation in Tanzania&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Want to see it in action, visit the &lt;a href=&quot;https://coverband-demo.herokuapp.com/&quot;&gt;Coverband Demo Site&lt;/a&gt;, visit a few pages and watch the coverage report change as you use the site.&lt;/p&gt;

&lt;h3 id=&quot;satisfying-work&quot;&gt;Satisfying Work&lt;/h3&gt;

&lt;p&gt;On my first work visit to Arusha, we had scheduled a number of activities for me to better understand the business and our operations. One of which was to go watch an installation occur near Arusha. On arrival, the children were playing in the yard and extremely excited to see us visit. After a bit of discussion and planning of where cabling, lights, and the M-series box will go we get started. I volunteer to install the panel, with no ladder available, I climbed a tree and jumped onto the tin roof attached the solar panel in place. Using a screwdriver to part the roof panels to run the panel cabling through the roof to the box. I came down to help finish install some of the lights. The mother holding the youngest baby turns on her Zola lights for the first time and the kids’ cheer and dance by her feet while a smile of pride and happiness fills her face. It is possibly the best and most satisfying work moment of my life.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/install_mother.jpg&quot; alt=&quot;Happy Mother&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;The happy mother after installation of lighting system&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;more-photos&quot;&gt;More Photos!&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/dan_climb.jpg&quot; alt=&quot;Climbing the tree&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;Climbing to the roof&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/dan_jump.jpg&quot; alt=&quot;Jumping over&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;Jumping over to the roof, sure hope it holds me!!!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/dan_light.jpg&quot; alt=&quot;Adding a light&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;Hanging a light as part of the installation, seen earlier with the Mother below&lt;/p&gt;
&lt;/blockquote&gt;
</content>
 </entry>
 
 <entry>
   <title>Coverband 3</title>
   <link href="https://www.mayerdan.com/ruby/2018/10/03/coverband-3"/>
   <updated>2018-10-03T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/ruby/2018/10/03/coverband-3</id>
   <content type="html">
&lt;h1 id=&quot;coverband-3&quot;&gt;Coverband 3&lt;/h1&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/danmayer/coverband_demo&quot;&gt;Coverband&lt;/a&gt; gives deep insight into the production usage of every line of code in your Ruby app. Find dead code, find hotspots, see code paths that might be unexpectedly in high usage.&lt;/p&gt;

&lt;p&gt;This release marks deep dives into Ruby performance, &lt;a href=&quot;https://www.mayerdan.com/ruby/2018/05/29/rubygems_benchmarks&quot;&gt;benchmarking&lt;/a&gt;, and driving almost all features based on improving performance. The release is a majory performance improvement, resolving some issues were Coverband 2 could have outliers that performed worse than the original Coverband 1 gem.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Me first and the Gimme Gimmes, a punk cover band, image from &lt;a href=&quot;https://commons.wikimedia.org/wiki/File:Me_first_and_the_Gimme_Gimmes_in_concert.jpg&quot;&gt;Wikimedia&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Want to see it in action, visit the &lt;a href=&quot;https://coverband-demo.herokuapp.com/&quot;&gt;Coverband Demo Site&lt;/a&gt;, visit a few pages and watch the coverage report change as you use the site.&lt;/p&gt;

&lt;h3 id=&quot;what-breaking-changes-are-there&quot;&gt;What breaking changes are there?&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;drops Tracepoint&lt;/li&gt;
  &lt;li&gt;drops Ruby &amp;lt;= 2.3.0&lt;/li&gt;
  &lt;li&gt;drops JSON Gem dependency&lt;/li&gt;
  &lt;li&gt;drops various other features not needed without Tracepoint
    &lt;ul&gt;
      &lt;li&gt;memory cache, sampling, restricted to app folders, etc&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;what-is-new&quot;&gt;What is new?&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;standardizes on Coverage array format vs sparse hash&lt;/li&gt;
  &lt;li&gt;rewrites store methods, for 60X perf!
    &lt;ul&gt;
      &lt;li&gt;implemented for Redis and File store&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;improved mountable web interface&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;what-exactly-is-faster&quot;&gt;What exactly is faster?&lt;/h2&gt;

&lt;h3 id=&quot;tldr&quot;&gt;tldr;&lt;/h3&gt;

&lt;p&gt;Storing Coverage data to a data store. While this didn’t happen often when it did it could generate thousands of Redis requests on large apps, &lt;!--more--&gt; which could make for a very slow outlier request…&lt;/p&gt;

&lt;h3 id=&quot;long-version&quot;&gt;Long Version&lt;/h3&gt;

&lt;p&gt;The primary improvements come down to changing the storage format and moving from method of storage that required one call per file and assummed a small list of files, to a 2 pass stoage for all data. Basically, the previous storage mechanism created an N+1 type issue. Coverband 2 had solved the issue of capturing Coverage data, which was no longer a performance concern, in fact it captured all files by default as there wasn’t really a reason to filter for performance anymore… This created a new problem when trying to store coverage data. Eventually a very specific benchmark was added that simulates trying to store coverage reports for apps with 2934 files tracking coverage. In Coverband 3 by changing the storage format and the storage plan the storage benchmark improved by nearly 60X!&lt;/p&gt;

&lt;p&gt;Simulate repeatedly storing coverage reports with Coverband 2.0.3:&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 benchmarks:redis_reporting
runs benchmarks on reporting large sets of files to redis
Warming up --------------------------------------
       store_reports     1.000  i/100ms
Calculating -------------------------------------
       store_reports      0.814  (± 0.0%) i/s -     13.000  in  15.982911s
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Simulate repeatedly storing coverage reports with Coverband 3.0.0:&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 benchmarks:redis_reporting
runs benchmarks on reporting large sets of files to redis
Warming up --------------------------------------
       store_reports     4.000  i/100ms
Calculating -------------------------------------
       store_reports     47.111  (± 4.2%) i/s -    708.000  in  15.066259s
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;how-did-this-impact-rails-apps&quot;&gt;How did this impact Rails Apps&lt;/h3&gt;

&lt;p&gt;In general, depending on various settings the outliers weren’t that noticable or it made Coverband unusable for folks. I configured a Rails app with non ideal, but not terrible options, to show what this could look like and how Coverband 3 resolves the issue. If configured idealy even Coverband 2 would mostly only show in outliers, so this test is showing how even in a bad setup V3 performs with nearly no impact.&lt;/p&gt;

&lt;p&gt;The benchmarks below are a further iteration of my &lt;a href=&quot;https://www.mayerdan.com/ruby/2018/03/25/ruby-benchmarking&quot;&gt;AB Benchmarking Rails apps process&lt;/a&gt;. Now much easier to read and compare, by graphing by following this &lt;a href=&quot;http://www.bradlanders.com/2013/04/15/apache-bench-and-gnuplot-youre-probably-doing-it-wrong/&quot;&gt;Apache Bench via Gnuplot guide&lt;/a&gt;.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;No Coverband&lt;/th&gt;
      &lt;th&gt;Coverband 2&lt;/th&gt;
      &lt;th&gt;Coverband 3&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;img src=&quot;/assets/img/no_coverband_timeseries.jpg&quot; alt=&quot;Bugs&quot; /&gt;&lt;/td&gt;
      &lt;td&gt;&lt;img src=&quot;/assets/img/coverband_2_2_timeseries.jpg&quot; alt=&quot;Bugs&quot; /&gt;&lt;/td&gt;
      &lt;td&gt;&lt;img src=&quot;/assets/img/coverband_3_timeseries.jpg&quot; alt=&quot;Bugs&quot; /&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;mean: 27.272 [ms]&lt;/td&gt;
      &lt;td&gt;mean: 35.762 [ms]&lt;/td&gt;
      &lt;td&gt;mean: 28.460 [ms]&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;36.67 [#/sec] (mean)&lt;/td&gt;
      &lt;td&gt;27.96 [#/sec] (mean)&lt;/td&gt;
      &lt;td&gt;35.14 [#/sec] (mean)&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Notice that with no Coverband and Coverband 3, the majority of requests show little variation, while Coverband 2 has frequent outliers pushing request timing all over the place. See full sized images of the benchmarks below.&lt;/p&gt;

&lt;h2 id=&quot;what-is-next&quot;&gt;What is next?&lt;/h2&gt;

&lt;p&gt;I am glad you asked, as we introduced a roadmap and set of feature discussions for upcoming Coverband improvements. Check out the &lt;a href=&quot;https://github.com/danmayer/coverband/blob/master/changes.md&quot;&gt;changes.md&lt;/a&gt; for the latest, but in short here is some of what we have in the works.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;full Railties support, near zero config Rails integration&lt;/li&gt;
  &lt;li&gt;solving the &lt;a href=&quot;https://github.com/danmayer/coverband/issues/118&quot;&gt;coverage drift problem&lt;/a&gt;…&lt;/li&gt;
  &lt;li&gt;adding &lt;a href=&quot;https://github.com/danmayer/coverband/issues/124&quot;&gt;out of band data collection methods&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;droppoing simplecov dependency&lt;/li&gt;
  &lt;li&gt;adding an Active Record adapter&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Please give Coverband a shot and add any issue you have to the repo.&lt;/p&gt;

&lt;h3 id=&quot;footnotes&quot;&gt;Footnotes&lt;/h3&gt;

&lt;p&gt;For those crazy few that really want all the data on the benchmarks…&lt;/p&gt;

&lt;h4 id=&quot;no-coverband&quot;&gt;No Coverband&lt;/h4&gt;

&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;img src=&quot;/assets/img/no_coverband_timeseries.jpg&quot; alt=&quot;No Coverband benchmark graph&quot; /&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&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 benchmarks:coverband_demo_graph
Benchmarking coverband-demo.herokuapp.com (be patient)

Concurrency Level:      10
Time taken for tests:   54.543 seconds
Complete requests:      2000
Failed requests:        0
Requests per second:    36.67 [#/sec] (mean)
Time per request:       272.717 [ms] (mean)
Time per request:       27.272 [ms] (mean, across all concurrent requests)

Percentage of the requests served within a certain time (ms)
  50%    262
  66%    269
  75%    273
  80%    276
  90%    288
  95%    302
  98%    328
  99%    350
 100%   1400 (longest request)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;coverband-2&quot;&gt;Coverband 2&lt;/h4&gt;

&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;img src=&quot;/assets/img/coverband_2_2_timeseries.jpg&quot; alt=&quot;Coverband 2 benchmark graph&quot; /&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&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 benchmarks:coverband_demo_graph
Benchmarking coverband-demo.herokuapp.com (be patient)

Concurrency Level:      10
Time taken for tests:   71.524 seconds
Complete requests:      2000
Failed requests:        0
Requests per second:    27.96 [#/sec] (mean)
Time per request:       357.619 [ms] (mean)
Time per request:       35.762 [ms] (mean, across all concurrent requests)

Percentage of the requests served within a certain time (ms)
  50%    325
  66%    353
  75%    378
  80%    398
  90%    489
  95%    545
  98%    622
  99%    682
 100%   1442 (longest request)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;coverband-3-1&quot;&gt;Coverband 3&lt;/h4&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/coverband_3_timeseries.jpg&quot; alt=&quot;Coverband 3 benchmark graph&quot; /&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;rake benchmarks:coverband_demo_graph
Benchmarking coverband-demo.herokuapp.com (be patient)

Concurrency Level:      10
Time taken for tests:   56.919 seconds
Complete requests:      2000
Failed requests:        0
Total transferred:      16565120 bytes
HTML transferred:       14974000 bytes
Requests per second:    35.14 [#/sec] (mean)
Time per request:       284.597 [ms] (mean)
Time per request:       28.460 [ms] (mean, across all concurrent requests)

Percentage of the requests served within a certain time (ms)
  50%    273
  66%    282
  75%    290
  80%    296
  90%    319
  95%    344
  98%    381
  99%    410
 100%   1358 (longest request)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Lead Developer London</title>
   <link href="https://www.mayerdan.com/programming/2018/07/18/lead-dev-conf-london"/>
   <updated>2018-07-18T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/programming/2018/07/18/lead-dev-conf-london</id>
   <content type="html">
&lt;h1 id=&quot;a-unique-conference&quot;&gt;A Unique Conference&lt;/h1&gt;

&lt;p&gt;I attended &lt;a href=&quot;https://twitter.com/hashtag/LeadDevLondon&quot;&gt;#LeadDevLondon&lt;/a&gt; this year, it was a nice and different conference. Generally, I have attended more language-specific conferences. There were basically no language-specific talks at all during the conf, as everything was a bit more general about building and working well with technology teams. Given that these days a large part of what I do is manage teams of devs, it was good to hear other folks talk through some of the challenges and solutions they have gone through during their journey.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;My swag bag full of stickers&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;some-favorite-slides&quot;&gt;Some Favorite Slides&lt;/h1&gt;

&lt;p&gt;For my very favorite slide see the section with my tweets below ;) Otherwise, enjoy some favorite slides with little to no context ;) You can generally find the full presentation slides on the &lt;a href=&quot;https://london2018.theleaddeveloper.com/schedule&quot;&gt;talks page&lt;/a&gt; and the full videos of the &lt;a href=&quot;https://www.youtube.com/playlist?list=PLBzScQzZ83I_VX8zgmLqIfma_kJs3RRmu&quot;&gt;#LeadDevLondon talks on youtube&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/leaddev_london/container_manual_1.jpg&quot; alt=&quot;leaddev london slides&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;&lt;a href=&quot;https://twitter.com/alicegoldfuss&quot;&gt;@alicegoldfuss&lt;/a&gt; explaining containers&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/leaddev_london/container_manual_2.jpg&quot; alt=&quot;leaddev london slides&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;&lt;a href=&quot;https://twitter.com/alicegoldfuss&quot;&gt;@alicegoldfuss&lt;/a&gt; explaining what else ops does ;)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/leaddev_london/art_of_reviews.jpg&quot; alt=&quot;leaddev london slides&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;&lt;a href=&quot;https://twitter.com/alexhillphd&quot;&gt;@alexhillphd&lt;/a&gt; going over the art of code reviews, this talk had my favorite slide of the conf&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/leaddev_london/scaling_yourself.jpg&quot; alt=&quot;leaddev london slides&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;&lt;a href=&quot;https://twitter.com/cmccarrick&quot;&gt;@cmccarrick&lt;/a&gt; how to scale yourself and make important decisions&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/leaddev_london/jr_next.jpg&quot; alt=&quot;leaddev london slides&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;&lt;a href=&quot;https://twitter.com/tara_ojo&quot;&gt;@tara_ojo&lt;/a&gt; on how to help Jr’s with good 1:1s&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/leaddev_london/destroyed_island.jpg&quot; alt=&quot;leaddev london slides&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;&lt;a href=&quot;https://twitter.com/nmeans&quot;&gt;@nmeans&lt;/a&gt; lessons learn from &lt;!--more--&gt; 3 mile island&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/leaddev_london/no_elitism.jpg&quot; alt=&quot;leaddev london slides&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;&lt;a href=&quot;https://twitter.com/ClareSudbery&quot;&gt;@ClareSudbery&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/leaddev_london/widely_distributed.jpg&quot; alt=&quot;leaddev london slides&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;&lt;a href=&quot;https://twitter.com/dbussink&quot;&gt;@dbussink&lt;/a&gt; shows how distributed his team is&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/leaddev_london/widely_distributed_2.jpg&quot; alt=&quot;leaddev london slides&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;&lt;a href=&quot;https://twitter.com/dbussink&quot;&gt;@dbussink&lt;/a&gt; explains how to make that an advantage&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/leaddev_london/awesome_internships.jpg&quot; alt=&quot;leaddev london slides&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;&lt;a href=&quot;https://twitter.com/WebDevBev&quot;&gt;@WebDevBev&lt;/a&gt; on how to run awesome internships&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/leaddev_london/edit_data.jpg&quot; alt=&quot;leaddev london slides&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;&lt;a href=&quot;https://twitter.com/jqiu25&quot;&gt;@jqiu25&lt;/a&gt; the journey to safe ways to edit production data&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;four-slides-from-the-legacy-code-talk&quot;&gt;Four Slides From The Legacy Code Talk&lt;/h3&gt;

&lt;p&gt;I guess I really liked what &lt;a href=&quot;https://twitter.com/ramtop&quot;&gt;@ramtop&lt;/a&gt; had to say about legacy code&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/leaddev_london/legacy_rewrite.jpg&quot; alt=&quot;leaddev london slides&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/leaddev_london/legacy_strangler.jpg&quot; alt=&quot;leaddev london slides&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/leaddev_london/legacy_modularity.jpg&quot; alt=&quot;leaddev london slides&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/leaddev_london/legacy_simplify.jpg&quot; alt=&quot;leaddev london slides&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;a-talk-pattern-i-want-to-see&quot;&gt;A Talk Pattern I Want To See&lt;/h1&gt;

&lt;p&gt;One thing I noticed and really came away with from the talks is that every piece of advice and best practice really fits into part of a companies growth curve. Even the most agreed on advice can’t and shouldn’t apply the same to a single person startup and a company of thousands of people and hundreds of engineers. This made me crave a talk that opposed to saying how some team approaches a challenge and the ways it works for them, but how a best practice or process changes and adapts to different growth stages of a company…&lt;/p&gt;

&lt;p&gt;As a single talk, an example let’s consider continuous delivery of the growth of a team. While the end best practices involve style checkers, peer code review, automated test suites, deployment to one or more staging, and feature flag or percentage based releases to production, before/after metric analysis on the impact of deploys… It would not be possible to start there nor would it be sensible to set up all that complexity when just getting started… I feel like the flow would naturally look something more like this.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;1 person startup -&amp;gt; manually run CLI deployment task from the dev machine&lt;/li&gt;
  &lt;li&gt;2-3 people -&amp;gt; CI test suite, manual CLI deployment&lt;/li&gt;
  &lt;li&gt;3-8 people -&amp;gt; static style checker, CI test suite, code review, staging deployment with stakeholder / QA review, push-button deployments and rollbacks automated on CI&lt;/li&gt;
  &lt;li&gt;8-16 -&amp;gt; all the above, with multiple staging environments, some integrated with 3rd party sandboxes, feature flag based releases, metrics tied to specific deployed releases, and deployment queue to help schedule deployments&lt;/li&gt;
  &lt;li&gt;etc, etc, etc…&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I think it would be great to see detailed talks on tech org structures, testing, monitoring, alerting, etc..&lt;/p&gt;

&lt;h1 id=&quot;my-tweets&quot;&gt;My Tweets&lt;/h1&gt;

&lt;p&gt;Some of the tweets I shared during the conf.&lt;/p&gt;

&lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Great to see some old friends at &lt;a href=&quot;https://twitter.com/hashtag/LeadDevLondon?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#LeadDevLondon&lt;/a&gt; and introduce them to Theo... &lt;a href=&quot;https://twitter.com/mariagutierrez?ref_src=twsrc%5Etfw&quot;&gt;@mariagutierrez&lt;/a&gt; got a nice hug... &lt;a href=&quot;https://t.co/bUIjOUrdff&quot;&gt;pic.twitter.com/bUIjOUrdff&lt;/a&gt;&lt;/p&gt;&amp;mdash; Dan Mayer (@danmayer) &lt;a href=&quot;https://twitter.com/danmayer/status/1012026404890988545?ref_src=twsrc%5Etfw&quot;&gt;June 27, 2018&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async=&quot;&quot; src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;

&lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;My favorite slide from &lt;a href=&quot;https://twitter.com/hashtag/LeadDevLondon?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#LeadDevLondon&lt;/a&gt; so far.. Also, why our team tries to have linters as part of our CI build... Have higher level PR discussions... &lt;a href=&quot;https://t.co/9gandnj3Q5&quot;&gt;pic.twitter.com/9gandnj3Q5&lt;/a&gt;&lt;/p&gt;&amp;mdash; Dan Mayer (@danmayer) &lt;a href=&quot;https://twitter.com/danmayer/status/1011991874259832833?ref_src=twsrc%5Etfw&quot;&gt;June 27, 2018&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async=&quot;&quot; src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;

&lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Thanks &lt;a href=&quot;https://twitter.com/hashtag/LeadDevLondon?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#LeadDevLondon&lt;/a&gt; after the conf, Theo got to see the analytical engine and difference engine at the London Science Museum... Which is fun since I am reading him a book about Ada Lovelace and Charles Babbage. &lt;a href=&quot;https://t.co/sK6VxRnhaQ&quot;&gt;pic.twitter.com/sK6VxRnhaQ&lt;/a&gt;&lt;/p&gt;&amp;mdash; Dan Mayer (@danmayer) &lt;a href=&quot;https://twitter.com/danmayer/status/1012420484158255104?ref_src=twsrc%5Etfw&quot;&gt;June 28, 2018&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async=&quot;&quot; src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;

&lt;h1 id=&quot;the-most-important-part-stickers&quot;&gt;The Most Important Part, Stickers&lt;/h1&gt;

&lt;p&gt;Seriously, thanks to all the sponsors and friends handing out stickers. I love collecting stickers at conferences to come back and cover my climbing wall. I am building a large collage covering the entire climbing wall in layers of stickers. After returning from a conference I always have a huge collection of stickers to use, which is great. In this case, I basically was able to make an entire tile of my climbing wall a #leaddevlondon tile full of stickers I picked up during the trip.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/leaddev_london/stickers_1.jpg&quot; alt=&quot;leaddev london stickers&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/leaddev_london/stickers_2.jpg&quot; alt=&quot;leaddev london stickers&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/leaddev_london/stickers_3.jpg&quot; alt=&quot;leaddev london stickers&quot; /&gt;&lt;/p&gt;

&lt;p&gt;If you don’t know what to do with all the stickers you have collected at various conferences, feel free to send them my way ;)&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Benchmarks Bugs</title>
   <link href="https://www.mayerdan.com/ruby/2018/06/10/benchmarking_bugs"/>
   <updated>2018-06-10T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/ruby/2018/06/10/benchmarking_bugs</id>
   <content type="html">
&lt;h1 id=&quot;benchmarking-bugs&quot;&gt;Benchmarking Bugs&lt;/h1&gt;

&lt;p&gt;On my previous post I covered &lt;a href=&quot;https://www.mayerdan.com/ruby/2018/05/29/rubygems_benchmarks&quot;&gt;benchmarking Rubygems&lt;/a&gt;… I was working on some follow up to improve the benchmarks and discovered a bug rendering one of the benchmark comparisons invalid. In this post we will cover what I had missed, how to avoid some gotchas, and ways to improve the readability of your benchmarks.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;image from &lt;a href=&quot;https://pixabay.com/en/ladybugs-ladybirds-bugs-insects-1593406/&quot;&gt;pixabay&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;spot-the-bug&quot;&gt;Spot The Bug&lt;/h2&gt;

&lt;p&gt;This is a simplified example from the source code. The bug is hard to notice without knowing the internals of the library, but if you understand Ruby’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Coverage&lt;/code&gt; library you might spot it.&lt;/p&gt;

&lt;div class=&quot;language-ruby 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;n&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:benchmarks&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;set up coverband with coverage redis&apos;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:setup_coverage&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;clone_classifier&lt;/span&gt;
    &lt;span class=&quot;vg&quot;&gt;$LOAD_PATH&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;unshift&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;classifier_dir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;lib&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;benchmark&apos;&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;classifier-reborn&apos;&lt;/span&gt;

    &lt;span class=&quot;no&quot;&gt;Coverband&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;configure&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;root&lt;/span&gt;               &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Dir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;pwd&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;percentage&lt;/span&gt;         &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;100.0&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;logger&lt;/span&gt;             &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;vg&quot;&gt;$stdout&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;collector&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;coverage&apos;&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;memory_caching&lt;/span&gt;     &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;MEMORY_CACHE&apos;&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;kp&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;store&lt;/span&gt;              &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Coverband&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Adapters&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;RedisStore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Redis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;run_work&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;benchmark for: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Coverband&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;inspect&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;store: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Coverband&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;inspect&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Benchmark&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;report&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;coverband&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
        &lt;span class=&quot;no&quot;&gt;SAMPLINGS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;times&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
          &lt;span class=&quot;no&quot;&gt;Coverband&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Collectors&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;sample&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;work&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

      &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;report&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;no coverband&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
        &lt;span class=&quot;no&quot;&gt;SAMPLINGS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;times&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;work&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Coverband&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Collectors&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;stop&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Coverband&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Collectors&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;reset_instance&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;runs benchmarks coverage&apos;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;run_coverage: :setup_coverage&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Coverband &amp;lt;!--more--&amp;gt; Coverage configured with to use default redis store&apos;&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;SAMPLINGS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;run_work&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The issue is that the benchmark is trying to compare running Coverband with Ruby’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Coverage&lt;/code&gt; lib against code, which doesn’t collect usage data. When using Ruby’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Coverage&lt;/code&gt; is loaded and started, it changes how &lt;strong&gt;ALL&lt;/strong&gt; other code is loaded and interpreted. Which means once the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Coverage&lt;/code&gt; library is loaded both benchmarks will run with the performance impact. The only difference is once is collecting and reporting the coverage to Redis, via Coverband. Since the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Coverage&lt;/code&gt; library effect the Ruby runtime it is best to run the benchmarks as entirely isolated processes to avoid blending the impacts of one benchmark into the other.&lt;/p&gt;

&lt;h1 id=&quot;spot-the-bug-in-the-stats&quot;&gt;Spot the Bug in the Stats&lt;/h1&gt;

&lt;p&gt;Even if one doesn’t notice the issue in the code, the output from the benchmarks should make the issue stand out. In my previous post I noticed that I &lt;strong&gt;could no longer detect&lt;/strong&gt; performance impacts of Coverband, that seemed to good to be true.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;                      user     system      total        real
coverband         0.320000   0.010000   0.330000 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;  0.322387&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
no coverband      0.320000   0.000000   0.320000 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;  0.321767&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;It also didn’t match with real world &lt;a href=&quot;https://www.mayerdan.com/ruby/2018/03/25/ruby-benchmarking&quot;&gt;Rails benchmarks&lt;/a&gt;. At the time in that post, I believed my benchmarks just weren’t sufficient to capture the small impact. Wanting to make my benchmarks easier to read and to be able to detect the known performance impact is what caused me to find the bug.&lt;/p&gt;

&lt;h1 id=&quot;making-benchmarks-more-readable&quot;&gt;Making Benchmarks More Readable&lt;/h1&gt;

&lt;p&gt;As I was working to detail the the performance impact of Coverband, I decided I should port my benchmarks from the std-lib benchmark, to the excellent &lt;a href=&quot;https://github.com/evanphx/benchmark-ips&quot;&gt;benchmark-ips&lt;/a&gt; gem by &lt;a href=&quot;https://twitter.com/evanphx&quot;&gt;@evanphx&lt;/a&gt;. The gem has output in the format below. The new format in comparison to the default format, makes it much easier to understand performance differences. In this case because of the bug to see clearly a missing and expected difference. The improved Gem also helped me find better iterations to detect changes on my existing and working benchmarks.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Coverband Coverage configured with to use default Redis store
Warming up &lt;span class=&quot;nt&quot;&gt;--------------------------------------&lt;/span&gt;
           coverband     1.000  i/100ms
        no coverband     1.000  i/100ms
Calculating &lt;span class=&quot;nt&quot;&gt;-------------------------------------&lt;/span&gt;
           coverband     14.690  &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;±27.2%&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; i/s -    149.000  &lt;span class=&quot;k&quot;&gt;in  &lt;/span&gt;12.045429s
        no coverband     15.112  &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;±33.1%&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; i/s -    151.000  &lt;span class=&quot;k&quot;&gt;in  &lt;/span&gt;12.240970s

Comparison:
        no coverband:       15.1 i/s
           coverband:       14.7 i/s - same-ish: difference falls within error
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The improved format along with increased iterations (5 seconds warmup and 12 seconds runtime) made it clear that there was truly &lt;strong&gt;ZERO&lt;/strong&gt; difference between the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Coverage&lt;/code&gt; benchmarks, which was wrong.  This makes it very clear a there was a bug in my methodology.&lt;/p&gt;

&lt;h1 id=&quot;fixing-the-issue&quot;&gt;Fixing the Issue&lt;/h1&gt;

&lt;p&gt;Once I realized &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Coverage&lt;/code&gt; was loaded for both runs, it was pretty clear what was wrong. I needed to do a couple things.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Coverage&lt;/code&gt; lib needs to be both &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;required&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;started&lt;/code&gt; before any code that we plan to track (this also wasn’t done in the previous benchmark, so actual coverage data wasn’t being collected correctly.)&lt;/li&gt;
  &lt;li&gt;Since &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Coverage&lt;/code&gt; changes how Ruby actually interprets the code when loaded. I needed to run the two timed tasks independently and finally compare the data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It was pretty easy to port my old benchmark code to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;benchmark-ips&lt;/code&gt;, and to clean things up along the way. As well as handle both of the mentioned issues. See the new benchmark code below (simplified here, see Github for the &lt;a href=&quot;https://github.com/danmayer/coverband/blob/feature/via_coverage/test/benchmarks/benchmark.rake&quot;&gt;full src code details&lt;/a&gt;).&lt;/p&gt;

&lt;div class=&quot;language-ruby 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;n&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:benchmarks&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;setup standard benchmark&apos;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:setup&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;clone_classifier&lt;/span&gt;
    &lt;span class=&quot;vg&quot;&gt;$LOAD_PATH&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;unshift&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;classifier_dir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;lib&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;benchmark&apos;&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;benchmark/ips&apos;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# NOTE: When we require files is what makes performance impact of Coverage interesting (moving this above or below Coverage.start gives detectable vs not detectable performance impacts&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;classifier-reborn&apos;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;COVERAGE&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Coverage library loaded and started&apos;&lt;/span&gt;
      &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;coverage&apos;&lt;/span&gt;
      &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Coverage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;start&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;redis&apos;&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;coverband&apos;&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dirname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;__FILE__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;dog&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;set up coverband with coverage Redis&apos;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:setup_coverage&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Coverband&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;configure&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;root&lt;/span&gt;               &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Dir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;pwd&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;percentage&lt;/span&gt;         &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;100.0&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;logger&lt;/span&gt;             &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;vg&quot;&gt;$stdout&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;collector&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;coverage&apos;&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;memory_caching&lt;/span&gt;     &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;MEMORY_CACHE&apos;&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;kp&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;store&lt;/span&gt;              &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Coverband&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Adapters&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;RedisStore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Redis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;run_work&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hold_work&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;suite&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;GCSuite&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;#puts &quot;benchmark for: #{Coverband.configuration.inspect}&quot;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;#puts &quot;store: #{Coverband.configuration.store.inspect}&quot;&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Benchmark&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ips&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:time&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:warmup&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:suite&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;suite&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;report&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;coverband&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
        &lt;span class=&quot;no&quot;&gt;Coverband&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Collectors&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;sample&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;work&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
      &lt;span class=&quot;no&quot;&gt;Coverband&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Collectors&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;stop&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;report&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;no coverband&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;work&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;hold!&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;temp_results&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hold_work&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;compare!&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Coverband&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Collectors&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;reset_instance&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  
  &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;runs benchmarks coverage&apos;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;run_coverage: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:setup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:setup_coverage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Coverband Coverage configured with to use default Redis store&apos;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;run_work&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;compare Coverband Ruby Coverage with normal Ruby&apos;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:compare_coverage&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;comparing with Coverage loaded and not, this takes some time for output...&apos;&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;sb&quot;&gt;`COVERAGE=true rake benchmarks:run_coverage`&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;sb&quot;&gt;`rake benchmarks:run_coverage`&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As you can see to handle the hard part of running the benchmarks independently and comparing them I used a cool feature of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;benchmark-ips&lt;/code&gt;. Loading the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Coverage&lt;/code&gt; library or not is now controlled by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ENV[&apos;COVERAGE&apos;]&lt;/code&gt; making it easy to run with or without a change to how Ruby interprets the code.&lt;/p&gt;

&lt;h1 id=&quot;using-benchmark-ips-hold&quot;&gt;Using benchmark-ips hold!&lt;/h1&gt;

&lt;p&gt;As I was starting to look at how to compare data on two runs, I checked to see if the new benchmarking gem I was using could help me. The documentation covered having a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hold!&lt;/code&gt; feature, which seemed exactly what I needed.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;If you are comparing multiple implementations of a piece of code you may want to benchmark them in separate invocations of Ruby so that the measurements are independent of each other. You can do this with the hold! command.&lt;br /&gt;
– &lt;a href=&quot;https://github.com/evanphx/benchmark-ips#independent-benchmarking&quot;&gt;benchmark-ips, independent-benchmarking&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Sounds great, but the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hold!&lt;/code&gt; wasn’t detailed very well in the documentation. In fact another user had &lt;a href=&quot;https://github.com/evanphx/benchmark-ips/issues/85&quot;&gt;created an issue&lt;/a&gt; for the project trying to figure out how to properly use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hold!&lt;/code&gt;. I decided to read through the source code to figure it out and verify that it could do what I needed.&lt;/p&gt;

&lt;h3 id=&quot;pr-with-example-benchmark-ips-hold-usage&quot;&gt;PR with example benchmark-ips &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hold!&lt;/code&gt; Usage&lt;/h3&gt;

&lt;p&gt;Well long as I take the time to figure it out, and I know others have struggled to understand the usage. We might as well try to make the usage clear to everyone. The project has an &lt;a href=&quot;https://github.com/evanphx/benchmark-ips/tree/master/examples&quot;&gt;examples folder&lt;/a&gt; detailing several usages. Adding a small &lt;a href=&quot;https://github.com/evanphx/benchmark-ips/pull/86&quot;&gt;PR with a hold usage example&lt;/a&gt;, gave a simple usage scenario. Which was a simplified version of the approach I used to benchmark the Ruby &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Coverage&lt;/code&gt; library performance impacts in Coverband.&lt;/p&gt;

&lt;h1 id=&quot;updated-benchmark-conclusions&quot;&gt;Updated Benchmark Conclusions&lt;/h1&gt;

&lt;p&gt;After fixing the bug, benchmarking &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Coverage&lt;/code&gt; is still very interesting. Even small changes in when &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Coverage&lt;/code&gt; is started and which files are required, can have a massive impact on the overall performance. Take a look at diff below, which shows moving a single line &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;require &apos;classifier-reborn&apos;&lt;/code&gt; to be above or below when we require and start &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Coverage&lt;/code&gt;, which means the libraries code is either included or not in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Coverage&lt;/code&gt; collection.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;-    require &lt;span class=&quot;s1&quot;&gt;&apos;classifier-reborn&apos;&lt;/span&gt;
     &lt;span class=&quot;k&quot;&gt;if &lt;/span&gt;ENV[&lt;span class=&quot;s1&quot;&gt;&apos;COVERAGE&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
       puts &lt;span class=&quot;s1&quot;&gt;&apos;Coverage library loaded and started&apos;&lt;/span&gt;
       require &lt;span class=&quot;s1&quot;&gt;&apos;coverage&apos;&lt;/span&gt;
       ::Coverage.start
     end
+    require &lt;span class=&quot;s1&quot;&gt;&apos;classifier-reborn&apos;&lt;/span&gt;
     require &lt;span class=&quot;s1&quot;&gt;&apos;redis&apos;&lt;/span&gt;
     require &lt;span class=&quot;s1&quot;&gt;&apos;coverband&apos;&lt;/span&gt;
     require File.join&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;File.dirname&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;__FILE__&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, &lt;span class=&quot;s1&quot;&gt;&apos;dog&apos;&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;The first benchmark shows when &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;classifier-reborn&lt;/code&gt; is required prior to setting up &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Coverage&lt;/code&gt; and therefor it’s data is excluded from the results.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;rake benchmarks:compare_coverage
comparing with Coverage loaded and not, this takes some &lt;span class=&quot;nb&quot;&gt;time &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;output...
Coverband Coverage configured with to use default Redis store
Warming up &lt;span class=&quot;nt&quot;&gt;--------------------------------------&lt;/span&gt;
           coverband     1.000  i/100ms
Calculating &lt;span class=&quot;nt&quot;&gt;-------------------------------------&lt;/span&gt;
           coverband     11.950  &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;±16.7%&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; i/s -    140.000  &lt;span class=&quot;k&quot;&gt;in  &lt;/span&gt;12.041935s

Pausing here &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; run Ruby again to measure the next benchmark...
Coverband Coverage configured with to use default Redis store
Warming up &lt;span class=&quot;nt&quot;&gt;--------------------------------------&lt;/span&gt;
        no coverband     1.000  i/100ms
Calculating &lt;span class=&quot;nt&quot;&gt;-------------------------------------&lt;/span&gt;
        no coverband     14.497  &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;±20.7%&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; i/s -    161.000  &lt;span class=&quot;k&quot;&gt;in  &lt;/span&gt;12.020487s

Comparison:
        no coverband:       14.5 i/s
           coverband:       12.0 i/s - same-ish: difference falls within error
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In this second benchmark we ensure the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Coverage&lt;/code&gt; library is loaded and started prior to requiring &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;classifier-reborn&lt;/code&gt;, which means we are capturing all the usage of that library code.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;rake benchmarks:compare_coverage
comparing with Coverage loaded and not, this takes some &lt;span class=&quot;nb&quot;&gt;time &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;output...
Coverband Coverage configured with to use default Redis store
Warming up &lt;span class=&quot;nt&quot;&gt;--------------------------------------&lt;/span&gt;
           coverband     1.000  i/100ms
Calculating &lt;span class=&quot;nt&quot;&gt;-------------------------------------&lt;/span&gt;
           coverband      9.117  &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;±11.0%&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; i/s -    109.000  &lt;span class=&quot;k&quot;&gt;in  &lt;/span&gt;12.108073s

Pausing here &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; run Ruby again to measure the next benchmark...
Coverband Coverage configured with to use default Redis store
Warming up &lt;span class=&quot;nt&quot;&gt;--------------------------------------&lt;/span&gt;
        no coverband     1.000  i/100ms
Calculating &lt;span class=&quot;nt&quot;&gt;-------------------------------------&lt;/span&gt;
        no coverband     14.184  &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;±14.1%&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; i/s -    164.000  &lt;span class=&quot;k&quot;&gt;in  &lt;/span&gt;12.017139s

Comparison:
        no coverband:       14.2 i/s
           coverband:        9.1 i/s - 1.56x  slower
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In these examples the only difference is that the library &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;classifier-reborn&lt;/code&gt; is either included in the recorded coverage or ignored by the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Coverage&lt;/code&gt; library, based on when we require it. The improved benchmark code:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;is easier to read than it was previously (both code and benchmark output)&lt;/li&gt;
  &lt;li&gt;it clearly captures and allows for easy performance tests of Coverband using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Coverage&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;It makes easily clear how much of a win &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Coverage&lt;/code&gt; is over the older &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TracePoint&lt;/code&gt; API
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Coverage&lt;/code&gt; showing either no diff or 1.56x slower (depending on the coverage scope)&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Tracepoint&lt;/code&gt; showing around 3.95x slower&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;full-benchmark-output&quot;&gt;Full Benchmark Output&lt;/h3&gt;

&lt;p&gt;Here is the full benchmark comparison output across all benchmarked configurations of Coverband.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Coverband tracepoint configured with file store
Warming up &lt;span class=&quot;nt&quot;&gt;--------------------------------------&lt;/span&gt;
           coverband     1.000  i/100ms
        no coverband     1.000  i/100ms
Calculating &lt;span class=&quot;nt&quot;&gt;-------------------------------------&lt;/span&gt;
           coverband      3.348  &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;±29.9%&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; i/s -     39.000  &lt;span class=&quot;k&quot;&gt;in  &lt;/span&gt;12.095922s
        no coverband     13.921  &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;±21.6%&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; i/s -    160.000  &lt;span class=&quot;k&quot;&gt;in  &lt;/span&gt;12.048469s

Comparison:
        no coverband:       13.9 i/s
           coverband:        3.3 i/s - 4.16x  slower

Coverband tracepoint configured with default Redis store
Warming up &lt;span class=&quot;nt&quot;&gt;--------------------------------------&lt;/span&gt;
           coverband     1.000  i/100ms
        no coverband     1.000  i/100ms
Calculating &lt;span class=&quot;nt&quot;&gt;-------------------------------------&lt;/span&gt;
           coverband      3.726  &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;± 0.0%&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; i/s -     45.000  &lt;span class=&quot;k&quot;&gt;in  &lt;/span&gt;12.286824s
        no coverband     14.736  &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;±27.1%&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; i/s -    150.000  &lt;span class=&quot;k&quot;&gt;in  &lt;/span&gt;12.059519s

Comparison:
        no coverband:       14.7 i/s
           coverband:        3.7 i/s - 3.95x  slower

comparing with Coverage loaded and not, this takes some &lt;span class=&quot;nb&quot;&gt;time &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;output...
Coverage library loaded and started
Coverband Coverage configured with to use default Redis store
Warming up &lt;span class=&quot;nt&quot;&gt;--------------------------------------&lt;/span&gt;
           coverband     1.000  i/100ms
Calculating &lt;span class=&quot;nt&quot;&gt;-------------------------------------&lt;/span&gt;
           coverband     12.173  &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;±16.4%&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; i/s -    143.000  &lt;span class=&quot;k&quot;&gt;in  &lt;/span&gt;12.017026s

Pausing here &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; run Ruby again to measure the next benchmark...
Coverband Coverage configured with to use default Redis store
Warming up &lt;span class=&quot;nt&quot;&gt;--------------------------------------&lt;/span&gt;
        no coverband     1.000  i/100ms
Calculating &lt;span class=&quot;nt&quot;&gt;-------------------------------------&lt;/span&gt;
        no coverband     15.045  &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;±13.3%&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; i/s -    177.000  &lt;span class=&quot;k&quot;&gt;in  &lt;/span&gt;12.024386s

Comparison:
        no coverband:       15.0 i/s
           coverband:       12.2 i/s - same-ish: difference falls within error
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;driving-performance-impact-improvements-based-on-learnings&quot;&gt;Driving Performance Impact Improvements Based on Learnings&lt;/h3&gt;

&lt;p&gt;Now with an ability to detect performance differences when running Coverband with Ruby’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Coverage&lt;/code&gt; library, I can build features and benchmarks to show how to reduce the performance impact on real world use cases.&lt;/p&gt;

&lt;p&gt;For example in general with Coverband the goal is to show the coverage of application code ignoring framework and gem code. Knowing that which files are loaded at what time related to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Coverage&lt;/code&gt; start can impact performance. I can build a feature to ensure that Coverband sets the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Coverage&lt;/code&gt; library to ensure &lt;strong&gt;ONLY&lt;/strong&gt; the application code is being tracked by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Coverage&lt;/code&gt;, which will help ensure the lowest possible performance impact.&lt;/p&gt;

&lt;p&gt;I will be working on this feature (Coverband Safelist) and new benchmarks to show how much application developers could reduce the performance burden on standard Rails apps by using a Safelist. Opposed to allowing various Rails framework code to be tracked along side their application code which is required by the current version of Coverband, given how Rails loads code. Look forward to another post, when that feature and benchmarks are ready.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Ruby Gems Benchmarks</title>
   <link href="https://www.mayerdan.com/ruby/2018/05/29/rubygems_benchmarks"/>
   <updated>2018-05-29T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/ruby/2018/05/29/rubygems_benchmarks</id>
   <content type="html">
&lt;h1 id=&quot;rubygems-benchmarks&quot;&gt;RubyGems Benchmarks&lt;/h1&gt;

&lt;p&gt;This follows up on my previous post focused on &lt;a href=&quot;https://www.mayerdan.com/ruby/2018/03/25/ruby-benchmarking&quot;&gt;Ruby Benchmarking&lt;/a&gt;. This post will focus on benchmarking a gem as part of it’s long term maintenance. Building a community &lt;a href=&quot;https://youtu.be/kJDOpucaUR4?t=31m28s&quot;&gt;standard around Gem Benchmarking&lt;/a&gt; has even been suggested as a good way to help Ruby fine tune performance over time.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;image from &lt;a href=&quot;https://pixabay.com/en/stopwatch-time-stop-time-training-3206383/&quot;&gt;pixabay&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;why-benchmark-a-gem&quot;&gt;Why Benchmark A Gem&lt;/h2&gt;

&lt;p&gt;If you maintain a Gem, which could have large performance impacts on folks integrating the gem. It can make sense to try to build performance testing into the Gem, helping to ensure that changes and new features don’t impact the performance in unexpected ways overtime. It also will help for you to publish performance impacts to give confidence to folks adding your gem as a dependency.&lt;/p&gt;

&lt;p&gt;For example I maintain a Gem, &lt;a href=&quot;https://github.com/danmayer/coverband&quot;&gt;Coverband&lt;/a&gt;, which records production code coverage. Obviously, this can have a major impact on performance as it tracks every line executed on production. During the life of Coverband, nearly every decision about features and Gem release involved some Benchmarking. The project was initially developed to help remove dead code from a large old monolithic rails app, before the first release could be put on production it went through a number of performance tests and only ran on staging until the perf impact could be acceptably &lt;!--more--&gt; controlled.&lt;/p&gt;

&lt;h2 id=&quot;coverband-benchmark-timeline&quot;&gt;Coverband Benchmark Timeline&lt;/h2&gt;

&lt;p&gt;Coverband code has gone through several strategies to mitigate the performance impact. Let’s take a look at quick timeline of major changes. As Coverband has changed, we can check and compare the performance impacts with the &lt;a href=&quot;https://github.com/danmayer/coverband/tree/master/test/benchmarks&quot;&gt;Coverband Benchmark Tasks&lt;/a&gt;, which are explained later in this post.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;2013: initial release
    &lt;ul&gt;
      &lt;li&gt;performance cost of 100% recorded coverage, around 8X slower Rails requests&lt;/li&gt;
      &lt;li&gt;initial release performance costs weren’t well explained with no released benchmarks&lt;/li&gt;
      &lt;li&gt;Performance costs were mitigated by sampling a percentage of requests&lt;/li&gt;
      &lt;li&gt;As well as safelist &amp;amp; blocklisting files to track&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;2014: released coverband_ext (C extension for fast access to tracepoint API) in
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;https://github.com/danmayer/coverband_ext#perf-improvements&quot;&gt;benchmarks for 100% recorded coverage&lt;/a&gt; showed this 1.25X slower Rails requests&lt;/li&gt;
      &lt;li&gt;This was the first time I released solid benchmarks on coverband
        &lt;ul&gt;
          &lt;li&gt;benchmarks were done on a sample Rails app &amp;amp; a large production app at my current place of employment&lt;/li&gt;
          &lt;li&gt;benchmarks were done by hand in a non repeatable process&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;2015: Various performance improvements introduced
    &lt;ul&gt;
      &lt;li&gt;Redis pipelining&lt;/li&gt;
      &lt;li&gt;Redis &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;zadd&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;benchmarking was done by hand, in a non repeatable way, to confirm actual performance impacts&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;2016: performance tests introduced into Coverband repository by &lt;a href=&quot;https://twitter.com/kbaum8&quot;&gt;@kbaum8&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;The new performance tests were used to propose that with various improvements between Ruby 1.9.x and Ruby 2.1.x that we could drop support for &lt;a href=&quot;https://github.com/danmayer/coverband_ext&quot;&gt;Coverband_ext&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;Moved from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;set_trace_func&lt;/code&gt; to Ruby &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;trace_point&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;The micro-benchmark showed that Ruby &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;trace_point&lt;/code&gt; was more than good enough an the C extension no longer provided significant performance improvements&lt;/li&gt;
      &lt;li&gt;See the &lt;a href=&quot;https://github.com/danmayer/coverband/tree/master/test/benchmarks&quot;&gt;source for integrated micro-benchmark performance tests&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;At these point most features and changes were checked with the performance tests to ensure the project was always getting faster&lt;/li&gt;
      &lt;li&gt;improved file filtering&lt;/li&gt;
      &lt;li&gt;Adding support for line usage count vs just used or unused for example didn’t incur additional overhead&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;2017:
    &lt;ul&gt;
      &lt;li&gt;Multiple backend stores added&lt;/li&gt;
      &lt;li&gt;The benchmark performance tests were refactored, so performance could be compared across multiple backend stores&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;2018:
    &lt;ul&gt;
      &lt;li&gt;Attempt to patch the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Coverage&lt;/code&gt; &lt;a href=&quot;https://bugs.ruby-lang.org/issues/9572&quot;&gt;reentrant Ruby bug&lt;/a&gt;
        &lt;ul&gt;
          &lt;li&gt;in this quest I propose &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Coverage.pause&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Coverage.resume&lt;/code&gt;&lt;/li&gt;
          &lt;li&gt;and in response &lt;a href=&quot;https://twitter.com/tenderlove&quot;&gt;@tenderlove&lt;/a&gt; says my goals are supported by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;peek_results&lt;/code&gt; which was added about 3 years ago, and that the performance impacts I had been assuming were not likely correct.&lt;/li&gt;
          &lt;li&gt;I attempt to prove that the performance impact would be significant, and end up proving @tenderlove is correct ;) Which is what started the deeper dive into Ruby Benchmarking&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;The results from the above cause me to update the Coverband benchmarks and use those benchmarks to help prove the significant win of using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Coverage&lt;/code&gt; vs &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;trace_point&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;Extending the benchmark code to report across different collector methods, shows massive win for the new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Coverage&lt;/code&gt; based collector, at least a 4X improvement.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;benchmark-code&quot;&gt;Benchmark Code&lt;/h2&gt;

&lt;p&gt;Below are some selected examples of the &lt;a href=&quot;https://github.com/danmayer/coverband/tree/master/test/benchmarks&quot;&gt;Coverband micro-benchmark performance tasks&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There are many ways to setup benchmarks for a Gem. In the Coverband examples are simple Rake tasks. This is opposed to performance tests, which folks have often employed for Rails app benchmarks. Given all the configuration and runtime impacts, the Rake approach worked well, but it requires reviewing the data by hand as nothing is tracked or charted in a machine readable format over time via CI.&lt;/p&gt;

&lt;p&gt;The example below has been simplified a bit from the full source linked above.&lt;/p&gt;

&lt;div class=&quot;language-ruby 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;n&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:benchmarks&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# leaving out some helper methods&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;set up coverband tracepoint collector to redis&apos;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:setup&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;clone_classifier&lt;/span&gt;
    &lt;span class=&quot;vg&quot;&gt;$LOAD_PATH&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;unshift&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;classifier_dir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;lib&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;benchmark&apos;&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;classifier-reborn&apos;&lt;/span&gt;

    &lt;span class=&quot;no&quot;&gt;Coverband&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;configure&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;redis&lt;/span&gt;              &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Redis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;root&lt;/span&gt;               &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Dir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;pwd&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;percentage&lt;/span&gt;         &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;100.0&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;logger&lt;/span&gt;             &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;vg&quot;&gt;$stdout&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;collector&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;trace&apos;&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;memory_caching&lt;/span&gt;     &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;MEMORY_CACHE&apos;&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;kp&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;store&lt;/span&gt;              &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Coverband&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Adapters&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;RedisStore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Redis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;set up coverband with coverage collector to redis&apos;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:setup_coverage&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;clone_classifier&lt;/span&gt;
    &lt;span class=&quot;vg&quot;&gt;$LOAD_PATH&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;unshift&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;classifier_dir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;lib&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;benchmark&apos;&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;classifier-reborn&apos;&lt;/span&gt;

    &lt;span class=&quot;no&quot;&gt;Coverband&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;configure&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;root&lt;/span&gt;               &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Dir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;pwd&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;percentage&lt;/span&gt;         &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;100.0&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;logger&lt;/span&gt;             &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;vg&quot;&gt;$stdout&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;collector&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;coverage&apos;&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;memory_caching&lt;/span&gt;     &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;MEMORY_CACHE&apos;&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;kp&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;store&lt;/span&gt;              &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Coverband&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Adapters&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;RedisStore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Redis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;work&lt;/span&gt;
    &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;times&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;bayes_classification&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;lsi_classification&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# simulate many calls to the same line&lt;/span&gt;
    &lt;span class=&quot;mi&quot;&gt;10_000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;times&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Dog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bark&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;run_work&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;benchmark for: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Coverband&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;inspect&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;store: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Coverband&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;inspect&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Benchmark&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;report&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;coverband&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
        &lt;span class=&quot;no&quot;&gt;SAMPLINGS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;times&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
          &lt;span class=&quot;no&quot;&gt;Coverband&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Collectors&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;sample&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;work&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

      &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;report&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;no coverband&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
        &lt;span class=&quot;no&quot;&gt;SAMPLINGS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;times&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;work&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Coverband&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Collectors&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;stop&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Coverband&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Collectors&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;reset_instance&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;runs benchmarks on default redis setup&apos;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;run: :setup&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Coverband tracepoint configured with default redis store&apos;&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;SAMPLINGS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;run_work&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;runs benchmarks coverage&apos;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;run_coverage: :setup_coverage&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Coverband Coverage configured with to use default redis store&apos;&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;SAMPLINGS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;run_work&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;runs all benchmarks&apos;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;benchmarks: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;benchmarks:run&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;benchmarks:run_coverage&apos;&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;h2 id=&quot;benchmark-results&quot;&gt;Benchmark Results&lt;/h2&gt;

&lt;p&gt;Below is a sample of the output generated when all the benchmarks are run. For each test, it configures Coverband and outputs the configuration settings, along with the same code executed with and without Coverband. The key point being the output below for the new Coverband implementation.&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;                      user     system      total        real
coverband         0.320000   0.010000   0.330000 (  0.322387)
no coverband      0.320000   0.000000   0.320000 (  0.321767)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;![Stopwatch](https://chart.googleapis.com/chart?chtt=Time%20in%20Sec%20Vs%20Benchmark%20Type&amp;amp;chxt=x,y&amp;amp;cht=bvs&amp;amp;chxr=1,0,1.5&amp;amp;chds=0,1.5&amp;amp;chd=t:1.350,1.3100,0.32,0.32&amp;amp;chco=76A4FB&amp;amp;chbh=23,100,75&amp;amp;chs=500x325&amp;amp;chxl=0:&lt;/td&gt;
      &lt;td&gt;Tracepoint%20to%20File&lt;/td&gt;
      &lt;td&gt;Tracepoint%20to%20Redis&lt;/td&gt;
      &lt;td&gt;Coverage%20to%20Redis&lt;/td&gt;
      &lt;td&gt;Without%20Coverband)&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;blockquote&gt;
  &lt;p&gt;User time from benchmark results below graphed&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;While it is easy to see the performance impact of the previous &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tracepoint collector&lt;/code&gt;, at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1.350000&lt;/code&gt; vs &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0.320000&lt;/code&gt; the current benchmark can’t even detect a performance slowdown using the new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Coverage&lt;/code&gt; collector. While this means, I should further extend the Gems performance tests, the new implementation is significantly and easily proved to be far more performant than the previous implementation.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;rake benchmarks
Coverband tracepoint configured with file store
benchmark &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt;: &lt;span class=&quot;c&quot;&gt;#&amp;lt;Coverband::Configuration:0x007fc069235c68 @root=&quot;/Users/danmayer/projects/coverband&quot;, @redis=nil, @root_paths=[], @ignore=[], @additional_files=[], @include_gems=false, @percentage=100.0, @verbose=false, @reporter=&quot;scov&quot;, @collector=&quot;trace&quot;, @logger=#&amp;lt;IO:&amp;lt;STDOUT&amp;gt;&amp;gt;, @startup_delay=0, @memory_caching=false, @store=#&amp;lt;Coverband::Adapters::FileStore:0x007fc0692359e8 @path=&quot;/tmp/benchmark_store.json&quot;&amp;gt;, @disable_on_failure_for=nil&amp;gt;&lt;/span&gt;
store: &lt;span class=&quot;c&quot;&gt;#&amp;lt;Coverband::Adapters::FileStore:0x007fc0692359e8 @path=&quot;/tmp/benchmark_store.json&quot;&amp;gt;&lt;/span&gt;
                      user     system      total        real
coverband         1.350000   0.000000   1.350000 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;  1.354026&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
no coverband      0.310000   0.000000   0.310000 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;  0.321970&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
Coverband tracepoint configured with default redis store
/Users/danmayer/projects/coverband/test/benchmarks/benchmark.rake:127: warning: already initialized constant SAMPLINGS
/Users/danmayer/projects/coverband/test/benchmarks/benchmark.rake:134: warning: previous definition of SAMPLINGS was here
benchmark &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt;: &lt;span class=&quot;c&quot;&gt;#&amp;lt;Coverband::Configuration:0x007fc069235c68 @root=&quot;/Users/danmayer/projects/coverband&quot;, @redis=#&amp;lt;Redis client v3.3.3 for redis://127.0.0.1:6379/0&amp;gt;, @root_paths=[], @ignore=[], @additional_files=[], @include_gems=false, @percentage=100.0, @verbose=false, @reporter=&quot;scov&quot;, @collector=&quot;trace&quot;, @logger=#&amp;lt;IO:&amp;lt;STDOUT&amp;gt;&amp;gt;, @startup_delay=0, @memory_caching=false, @store=#&amp;lt;Coverband::Adapters::RedisStore:0x007fc06911c8b8 @redis=#&amp;lt;Redis client v3.3.3 for redis://127.0.0.1:6379/0&amp;gt;&amp;gt;, @disable_on_failure_for=nil&amp;gt;&lt;/span&gt;
store: &lt;span class=&quot;c&quot;&gt;#&amp;lt;Coverband::Adapters::RedisStore:0x007fc06911c8b8 @redis=#&amp;lt;Redis client v3.3.3 for redis://127.0.0.1:6379/0&amp;gt;&amp;gt;&lt;/span&gt;
                      user     system      total        real
coverband         1.310000   0.000000   1.310000 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;  1.344789&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
no coverband      0.320000   0.000000   0.320000 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;  0.318863&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
Coverband Coverage configured with to use default redis store
/Users/danmayer/projects/coverband/test/benchmarks/benchmark.rake:141: warning: already initialized constant SAMPLINGS
/Users/danmayer/projects/coverband/test/benchmarks/benchmark.rake:127: warning: previous definition of SAMPLINGS was here
benchmark &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt;: &lt;span class=&quot;c&quot;&gt;#&amp;lt;Coverband::Configuration:0x007fc069235c68 @root=&quot;/Users/danmayer/projects/coverband&quot;, @redis=#&amp;lt;Redis client v3.3.3 for redis://127.0.0.1:6379/0&amp;gt;, @root_paths=[], @ignore=[], @additional_files=[], @include_gems=false, @percentage=100.0, @verbose=false, @reporter=&quot;scov&quot;, @collector=&quot;coverage&quot;, @logger=#&amp;lt;IO:&amp;lt;STDOUT&amp;gt;&amp;gt;, @startup_delay=0, @memory_caching=false, @store=#&amp;lt;Coverband::Adapters::RedisStore:0x007fc0691acfa8 @redis=#&amp;lt;Redis client v3.3.3 for redis://127.0.0.1:6379/0&amp;gt;&amp;gt;, @disable_on_failure_for=nil&amp;gt;&lt;/span&gt;
store: &lt;span class=&quot;c&quot;&gt;#&amp;lt;Coverband::Adapters::RedisStore:0x007fc0691acfa8 @redis=#&amp;lt;Redis client v3.3.3 for redis://127.0.0.1:6379/0&amp;gt;&amp;gt;&lt;/span&gt;
                      user     system      total        real
coverband         0.320000   0.010000   0.330000 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;  0.322387&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
no coverband      0.320000   0.000000   0.320000 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;  0.321767&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;additional-resources-on-performance-testing-gems&quot;&gt;Additional Resources on Performance Testing Gems&lt;/h2&gt;

&lt;p&gt;Some other examples of benchmarking a Gem or even single commits.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/gettalong/hexapdf/tree/master/benchmark&quot;&gt;hexapdf benchmark tests&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/piotrmurach/rspec-benchmark&quot;&gt;rspec-benchmark gem&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/rails/rails/commit/0c54fc460e52d2b9aa02e1e27a090dbe7ee98829&quot;&gt;benchmarking a single Rails commit&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>Ruby Benchmarking</title>
   <link href="https://www.mayerdan.com/ruby/2018/03/25/ruby-benchmarking"/>
   <updated>2018-03-25T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/ruby/2018/03/25/ruby-benchmarking</id>
   <content type="html">
&lt;h1 id=&quot;ruby-benchmarking&quot;&gt;Ruby Benchmarking&lt;/h1&gt;

&lt;p&gt;If you work on a large app, publish, gems, or are interested in the Ruby community at some point you will likely want to dig into performance and benchmarking. Often what you think will help performance doesn’t have as much of an impact as you would guess. Which is why being able to set up quick experiments to verify the impact of code changes on performance and memory can be important. Benchmarking is great as it can help prove, your change had the impact you want. Ruby performance and the measurement of it is getting more discussion at the moment because &lt;a href=&quot;https://blog.heroku.com/ruby-3-by-3&quot;&gt;Ruby 3X3&lt;/a&gt; set out a goal to make &lt;a href=&quot;http://engineering.appfolio.com/appfolio-engineering/2017/12/4/hows-progress-on-ruby-3x3&quot;&gt;Ruby 3 three times faster than Ruby 2&lt;/a&gt;. Learn more in this talk, &lt;a href=&quot;https://www.youtube.com/watch?v=kJDOpucaUR4&quot;&gt;Ruby3x3: How are we going to measure 3x?&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This post will talk about measuring the performance of Ruby and benchmarking your own code and applications. In a future post, I will dig more into good practices around benchmarking Ruby gems.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;image from &lt;a href=&quot;https://pixabay.com/en/stopwatch-time-stop-time-training-3206383/&quot;&gt;pixabay&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;ruby-benchmarking-projects&quot;&gt;Ruby Benchmarking Projects&lt;/h1&gt;

&lt;p&gt;A number of projects have been proposed and have support from the community for benchmarking Ruby… These project all will help the community improve the performance of Ruby, and can be used to help you measure the impact or Ruby or Gem changes you would like &lt;!--more--&gt; to try. If you are interested in contributing to Ruby or helping the community with improved performance or better benchmarking I encourage you to check out these projects. A great way to learn more about benchmarking is to dig into projects that are already out available and looking for more contributors.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://engineering.appfolio.com/appfolio-engineering/2017/9/22/optcarrot-an-excellent-cpu-benchmark-for-ruby-3x3&quot;&gt;OptCarrot (Ruby NES Emulator)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/noahgibbs/rails_ruby_bench&quot;&gt;Rails Ruby Bench&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://rubybench.org/&quot;&gt;Ruby Bench&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://gettalong.org/blog/2017/benchmarking-ruby-2-5.html&quot;&gt;Ruby HexaPDF Gem Benchmark&lt;/a&gt;, and &lt;a href=&quot;https://github.com/gettalong/hexapdf/tree/master/benchmark&quot;&gt;the benchmark source&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/schneems/derailed_benchmarks&quot;&gt;Derailed Benchmarks, a tool to benchmark full Rails apps&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;ruby-benchmarking-basics&quot;&gt;Ruby Benchmarking Basics&lt;/h1&gt;

&lt;p&gt;When I need to quickly benchmark something, I look no further than Ruby’s simple &lt;a href=&quot;https://ruby-doc.org/stdlib-2.5.0/libdoc/benchmark/rdoc/Benchmark.html&quot;&gt;Benchmark Module&lt;/a&gt;. I don’t have all the usage memorized off the top of my head, but have ended up at this great blog post, &lt;a href=&quot;https://www.skorks.com/2010/03/timing-ruby-code-it-is-easy-with-benchmark/&quot;&gt;timing Ruby code is easy with Benchmark&lt;/a&gt;, a number of times over the years. It is a quick and simple post that will help get basics up and running in no time at all. For some additional Ruby benchmarking support also see the resources below.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://rubylearning.com/blog/2013/06/19/how-do-i-benchmark-ruby-code/&quot;&gt;How to use Benchmark on Ruby code&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/luislavena/bench-micro&quot;&gt;a Small project to benchmark Ruby web frameworks&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/garybernhardt/readygo&quot;&gt;Alternative to Benchmark Module, ReadyGo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;learning-by-benchmarking-a-proposed-ruby-change&quot;&gt;Learning by Benchmarking A Proposed Ruby Change&lt;/h1&gt;

&lt;p&gt;I previously covered how to &lt;a href=&quot;https://www.mayerdan.com/ruby/2018/02/17/building-custom-ruby-from-scratch-on-OSX&quot;&gt;build Ruby from scratch on OS X&lt;/a&gt;. I posted that as I was working on a feature, I was hoping to get into Ruby. I proposed a change to Ruby’s Coverage that I hoped would lead to large performance improvements adding &lt;a href=&quot;https://bugs.ruby-lang.org/issues/9572#note-4&quot;&gt;Coverage Pause &amp;amp; Resume&lt;/a&gt; support. A little discussion with &lt;a href=&quot;https://twitter.com/tenderlove&quot;&gt;@tenderlove&lt;/a&gt;, lead me to try to prove the value of the feature in terms of performance. In the end, I actually proved my feature idea was unnecessary and that Ruby’s Coverage was significantly faster than &lt;a href=&quot;https://ruby-doc.org/core-2.0.0/TracePoint.html&quot;&gt;Ruby’s Tracepoint&lt;/a&gt;. Below, I will walk through some of the steps and code, I used while driving towards better benchmarks and understanding of what was really impacting performance while trying to collect the line of code runtime.&lt;/p&gt;

&lt;h3 id=&quot;first-attempt-a-micro-benchmark&quot;&gt;First Attempt: A Micro Benchmark&lt;/h3&gt;

&lt;p&gt;For some specific changes a micro benchmark, that focuses in very narrowly on the specific changes under test can be all that is needed and make for faster and easier iteration while testing changes. Often these are the easiest to setup and be more easily repeatable with various versions and settings to compare a number of changes.&lt;/p&gt;

&lt;p&gt;My first attempt was to use a small repository, that I had used to show an issue with Ruby’s coverage, see my &lt;a href=&quot;https://github.com/danmayer/coverage-bug/blob/master/example.rb&quot;&gt;Benchmarking Coverage Example&lt;/a&gt;. The code was extended it so it could be run in 3 modes:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Ruby without Coverage loaded: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ruby example.rb&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Ruby with Coverage as it exists in the current release: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;COVERAGE=true ruby example.rb&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Ruby with my suggested Coverage feature put to use: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ENHANCED_COVERAGE=true ruby example.rb&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-ruby 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;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;benchmark&apos;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;coverage&apos;&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;WITH_COVERAGE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!!&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;COVERAGE&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;WITH_ENHANCED_COVERAGE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!!&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;ENHANCED_COVERAGE&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;Coverage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;WITH_COVERAGE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;WITH_ENHANCED_COVERAGE&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;bigdecimal/math&apos;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;./app&apos;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;./app_proxy&apos;&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Coverage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;pause&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;WITH_ENHANCED_COVERAGE&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;ITERATIONS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2_000&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;UPTO&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1_000&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;coverage_data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# warm up&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;AppProxy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;App&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;ss&quot;&gt;iterations: &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;up_to: &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;UPTO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;Benchmark&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bm&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bm&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;WITH_ENHANCED_COVERAGE&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;bm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;report&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;coverage_data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;AppProxy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;App&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;ss&quot;&gt;iterations: &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ITERATIONS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;up_to: &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;UPTO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;coverage: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;enhanced_coverage: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;false&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;end&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;bm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;report&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;coverage_data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;AppProxy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;App&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;ss&quot;&gt;iterations: &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ITERATIONS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;up_to: &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;UPTO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;coverage: &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;WITH_COVERAGE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;enhanced_coverage: &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;WITH_ENHANCED_COVERAGE&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;end&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;coverage&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;coverage_data&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;done&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The initial results, even with a high number of iterations basically couldn’t show a real difference. In the case, I was trying to show, since it wasn’t a dramatic improvement and wasn’t obviously measurable like the number of objects allocated to memory, proved to not be a good case for a microbenchmark.&lt;/p&gt;

&lt;h3 id=&quot;second-attempt-build-on-rails-ruby-bench&quot;&gt;Second Attempt: Build On Rails Ruby Bench&lt;/h3&gt;

&lt;p&gt;As I abandoned the idea of a microbenchmark being able to show the differences of the performance impact when using Ruby’s code coverage. I sought a more realistic example of how Ruby code is frequently used in production. I found &lt;a href=&quot;https://twitter.com/codefolio&quot;&gt;@codefolio&lt;/a&gt;’s project, &lt;a href=&quot;https://github.com/noahgibbs/rails_ruby_bench&quot;&gt;rails_ruby_bench&lt;/a&gt;, which I mentioned previously intends to help measure performance impacts for Ruby 3X3. Unfortunately, I ran into some issues getting this benchmark to run locally and I would need to do a good deal of work to embed the code I wanted under test into this project and Discourse which the project uses as the Rails app under performance testing.&lt;/p&gt;

&lt;p&gt;While I didn’t end up pursuing this route, it did help push me towards a good direction to more realistically measure the performance impacts I wanted to see. By setting up a sample Rails application and testing full request cycles, I would have a much more realistic measure of my changes. I plan to follow up on this project more in the future and think it is an ideal way to test many Ruby or Gem changes that would have performance or memory impacts on standard Rails applications.&lt;/p&gt;

&lt;h3 id=&quot;third-attempt-sample-rails-app&quot;&gt;Third Attempt: Sample Rails App&lt;/h3&gt;

&lt;p&gt;Combining the learnings from the microbenchmark and the ideas in Rails Ruby Bench, I wanted to be able to quickly test a number of different scenarios by just setting a few environment variables, and then pull meaningful results on exercising the full Rails app with the changes. To measure the impact on the full Rails stack I turned to &lt;a href=&quot;https://httpd.apache.org/docs/2.4/programs/ab.html&quot;&gt;Apache’s AB&lt;/a&gt;, a simple HTTP benchmarking tool I have used many times over the years.&lt;/p&gt;

&lt;p&gt;For each test, the Rails application would be run in a different mode, then benchmarked via AB. I created a new repository &lt;a href=&quot;https://github.com/danmayer/coverage_rails_benchmark&quot;&gt;coverage_rails_benchmark&lt;/a&gt;. The project README covers all the steps for anyone to run their own set of benchmarks and records all the results of my performance tests.&lt;/p&gt;

&lt;p&gt;The benchmarking script itself is extremely simple, the script below will make 2000 requests making 5 concurrent requests at a time to the endpoint listed. The code can be found in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bin/benchmark.rb&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-ruby 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;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;sb&quot;&gt;`ab -n 2000 -c 5 &quot;http://127.0.0.1:3000/posts&quot;`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For example to get the most basic results of Ruby without Coverage loaded, one would follow the two steps below in two different terminals.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;start the Rails server in basic mode: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IGNORED_COVERAGE=true RAILS_ENV=production bin/rails server&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;execute the benchmark: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ruby ./bin/benchmark.rb&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This would output a bunch of data about the benchmark results.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Benchmarking 127.0.0.1 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;be patient&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;

Server Software:
Server Hostname:        127.0.0.1
Server Port:            3000

Document Path:          /posts
Document Length:        3631 bytes

Concurrency Level:      5
Time taken &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;tests:   8.391 seconds
Complete requests:      2000
Failed requests:        0
Total transferred:      8572000 bytes
HTML transferred:       7262000 bytes
Requests per second:    238.34 &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;#/sec] (mean)&lt;/span&gt;
Time per request:       20.978 &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;ms] &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;mean&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
Time per request:       4.196 &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;ms] &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;mean, across all concurrent requests&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
Transfer rate:          997.58 &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;Kbytes/sec] received

Connection Times &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;ms&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
              min  mean[+/-sd] median   max
Connect:        0    0   0.3      0      11
Processing:     6   21  10.8     19     192
Waiting:        6   20  10.7     19     190
Total:          6   21  10.8     19     192

Percentage of the requests served within a certain &lt;span class=&quot;nb&quot;&gt;time&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;ms&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
  50%     19
  66%     23
  75%     25
  80%     26
  90%     30
  95%     33
  98%     38
  99%     42
 100%    192 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;longest request&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;For my various comparisons, I ended up caring the most for the mean time per request in the above example &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;20.978 [ms] (mean)&lt;/code&gt; was the starting point baseline I comparing with all other results. My final sample benchmark support 7 distinct modes that could be run in two different distinct settings. Unlike with my microbenchmark performance differences were extremely obvious with a full spread of fasted benchmarking mode running at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;17.735 [ms] (mean)&lt;/code&gt; vs the slowest mode taking &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;85.069 [ms] (mean)&lt;/code&gt;. Showing the worst mode was nearly 5X slower in a simple benchmark.&lt;/p&gt;

&lt;h1 id=&quot;benchmarking-conclusions&quot;&gt;Benchmarking Conclusions&lt;/h1&gt;

&lt;p&gt;The various modes and options made it clear that I had initially been optimizing for the wrong thing, and that while there was significant performance improvements that could be made, it wouldn’t be related to my suggested Coverage feature proposal to support pause and resume. Instead, it showed that collecting coverage data was always very fast, but pulling and processing that data could be very slow. Opposed to trying to sample data collection the goal should be to reduce as much as possible the frequency one processes the results and to filter it down to the smallest set of results needed. Let’s take a look at a high-level summary below, or see the &lt;a href=&quot;https://github.com/danmayer/coverage_rails_benchmark#benchmark-conclusions&quot;&gt;full benchmark details&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;No Coverage Support: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;17.735&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Coverage Running (Ignore Coverage): &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;18.131&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Coverage Stopped (Ignore Coverage): &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;18.268&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Coverage Paused (Ignore Coverage): &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;18.717&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Coverband Coverage (Ignore Coverage): &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;18.759&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;New Pilot Version&lt;/strong&gt; Coveraband Coverage (Collect Coverage): &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;19.227&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Coverage Running (Collect Coverage, but only into memory): &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;21.141&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Coverage Resume (Ignore Coverage): &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;23.930&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Coverage Resume (Collect Coverage, but only into memory): &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;26.720&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Coverband Coverage (Collect Coverage): &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;39.421&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Coverband Tracepoint (Collect Coverage): &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;46.979&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Coverband Tracepoint (Ignore Coverage): &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;47.500&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Coverage (Collect Coverage, send to Rails.logger): &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;85.069&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the end, my goal of changing Ruby’s Coverage was to be able to significantly reduce the performance overhead of my gem &lt;a href=&quot;https://github.com/danmayer/coverband&quot;&gt;Coverband&lt;/a&gt;’s ability to collect runtime data. The results from the benchmark gave me all the data I needed to see that a different approach, where I dropped TracePoint in favor of Coverage but worked to reduce the frequency of checking the results would lead to far better performance improvements than my initially suggested feature. In the end, I pursued that approach, and was able to reduce the overhead with a sample rate of 100% from &lt;strong&gt;2.5X slower to only being 1.08X slower!&lt;/strong&gt; The details on that, I will cover in another post with some specifics on how to build performance benchmark testing into a Gem.&lt;/p&gt;

&lt;h1 id=&quot;ruby-benchmarking-learnings&quot;&gt;Ruby Benchmarking Learnings&lt;/h1&gt;

&lt;p&gt;I came away with a deeper understanding of Ruby performance benchmarking, and a much faster Gem that I will be able to release shortly. Beyond that some additional thoughts on Ruby benchmarking,&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;For some changes microbenchmarks aren’t helpful&lt;/li&gt;
  &lt;li&gt;There are some existing great projects to help folks benchmark changes to the Ruby language&lt;/li&gt;
  &lt;li&gt;Ruby’s Coverage is significantly Faster than collecting line usage via Ruby’s TracePoint functionality&lt;/li&gt;
  &lt;li&gt;Specific to Ruby’s Coverage library
    &lt;ul&gt;
      &lt;li&gt;The number of files instrumented with coverage has a big impact on performance, making micro benchmarks useless for that library&lt;/li&gt;
      &lt;li&gt;Calling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;coverage.peek_results&lt;/code&gt; to access the data is the biggest cost, much larger than collecting it.&lt;/li&gt;
      &lt;li&gt;Simply logging, processing, or trying to do anything with data is often slower than collecting it&lt;/li&gt;
      &lt;li&gt;Performance wins will come in reducing processing the data vs collecting it&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Writing an maintaining good benchmarks can be challenging, I didn’t touch on the issues of running benchmarks with “background noise” and running multiple times to tease that out&lt;/li&gt;
  &lt;li&gt;Without measuring performance, guessing what will have a large impact is often wrong&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;lets-go-faster&quot;&gt;Let’s Go Faster&lt;/h1&gt;

&lt;iframe src=&quot;https://giphy.com/embed/q6U4fbreC4Neo&quot; width=&quot;480&quot; height=&quot;339&quot; frameborder=&quot;0&quot; class=&quot;giphy-embed&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
&lt;p&gt;&lt;a href=&quot;https://giphy.com/gifs/q6U4fbreC4Neo&quot;&gt;via GIPHY&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In a follow-up post, I will dig into the details of benchmarking a Gem over time, and the specific changes that helped to reduce the performance overhead of Coverband making it an order of magnitude faster. While there are code changes and using different features of Ruby, large performance wins also can come from a fundamentally different approach to solving the problem, which is the case with the changes needed to improve Coverband.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Rack Proxy Tour</title>
   <link href="https://www.mayerdan.com/ruby/2018/02/26/rack-proxy-tour"/>
   <updated>2018-02-26T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/ruby/2018/02/26/rack-proxy-tour</id>
   <content type="html">
&lt;h1 id=&quot;rack-proxy-tour&quot;&gt;Rack Proxy Tour&lt;/h1&gt;

&lt;p&gt;I wanted to share a quick tour of a flexible Ruby tool. I have turned to &lt;a href=&quot;https://github.com/ncr/rack-proxy&quot;&gt;rack-proxy&lt;/a&gt; a number of times, throughout the years. It is a tiny and super useful Rack middleware that can quickly be adapted to perform a number of useful functions. While it is a small library it is a tool that has been handy for quick and lasting workarounds a number of times. It is a sharp tool, so be careful with it… Especially because it is severely lacking documentation… Perhaps I should send a documentation PR one of these days.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;image from &lt;a href=&quot;https://pixabay.com/en/moose-moose-rack-male-bull-animal-70254/&quot;&gt;pixabay&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;rack-proxy-examples&quot;&gt;Rack Proxy Examples&lt;/h3&gt;

&lt;p&gt;Some of the various ways I have used Rack Proxy over the years, while I wouldn’t recommend all of them, sometimes a quick hack is needed and Rack proxy can be a powerful tool for that.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;subdomain based pass-through to multiple apps&lt;/li&gt;
  &lt;li&gt;useful for handling awkward redirection rules for moved pages&lt;/li&gt;
  &lt;li&gt;fan out a single API request to multiple concurrent backend requests and merging results&lt;/li&gt;
  &lt;li&gt;authentication / authorization prior to proxying requests to a blindly trusting backend&lt;/li&gt;
  &lt;li&gt;avoiding CORs complications by proxying from same domain to another backend&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;example-code&quot;&gt;Example Code&lt;/h1&gt;

&lt;p&gt;In the example below we will have our rack proxy middleware, handle user authentication and authorization then make an authenticated request to another service. In this case, any request to our Rails applications host with the path &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/example_service/&lt;/code&gt; will pass through to the target service.&lt;/p&gt;

&lt;div class=&quot;language-ruby 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;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;rack-proxy&apos;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ExampleServiceProxy&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Rack&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Proxy&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;perform_request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Rack&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

	 &lt;span class=&quot;c1&quot;&gt;# path matches our target &amp;amp;&amp;amp;&lt;/span&gt;
	 &lt;span class=&quot;c1&quot;&gt;# user auth (devise in this case) found a logged in user&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=~&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;%r{^/example_service}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;warden&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;warden&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;user&lt;/span&gt;

		&lt;span class=&quot;c1&quot;&gt;# have a user but using CanCan check if user has needed permissions&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;warden&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;can?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:access&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:access_example_service&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;token&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Bearer &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Settings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;service.token&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;service_url&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Settings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;service.url&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

        &lt;span class=&quot;vi&quot;&gt;@backend&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;URI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;service_url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;rack.backend&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@backend&lt;/span&gt;
        
        &lt;span class=&quot;c1&quot;&gt;# while documentation says you only need on of these,&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# I needed to set them all to have the expected results&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;REQUEST_PATH&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;REQUEST_URI&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;PATH_INFO&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;/api/target_service_path&apos;&lt;/span&gt;
        
        &lt;span class=&quot;c1&quot;&gt;# target service fails on cookies&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;HTTP_COOKIE&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&apos;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;HTTP_AUTHORIZATION&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;token&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
        &lt;span class=&quot;no&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;info&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;example_service: 401 user &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;warden&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;inspect&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; denied&quot;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;401&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;s1&quot;&gt;&apos;Unauthorized!&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
      &lt;span class=&quot;vi&quot;&gt;@app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then you can just hook up the middleware as needed, using something like below (in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;application.rb&lt;/code&gt; for example).&lt;/p&gt;

&lt;div class=&quot;language-ruby 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;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;middleware&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ExampleServiceProxy&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;If you want to be able to access the devise user &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;env[&apos;warden&apos;].user&lt;/code&gt; you will need to make sure your middleware is inserted after the devise middleware. You can quickly check the order by printing out the Rails middleware stack.&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;&amp;gt; rake middleware

use Raven::Rack
use BufferedLoggingMiddleware
...
use Warden::Manager
use ExampleServiceProxy
...
use OtherExamples
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Building Your Own Ruby</title>
   <link href="https://www.mayerdan.com/ruby/2018/02/17/building-custom-ruby-from-scratch-on-OSX"/>
   <updated>2018-02-17T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/ruby/2018/02/17/building-custom-ruby-from-scratch-on-OSX</id>
   <content type="html">
&lt;h1 id=&quot;building--using-your-own-ruby&quot;&gt;Building &amp;amp; Using Your Own Ruby&lt;/h1&gt;

&lt;p&gt;If you want to work on changes or learn more about the internals of the  Ruby language, you can alter the source and build your own from scratch. It isn’t that hard or scary, you will learn a bit more about Ruby just by building it. This is a quick start to building Ruby from scratch on OSX, and using it to run your local apps.&lt;/p&gt;

&lt;p&gt;If you want to read a great &lt;a href=&quot;http://www.sihui.io/how-to-contribute-to-ruby/&quot;&gt;guide about contributing to Ruby&lt;/a&gt;, it also covers how to build even easier via a Dockerfile.&lt;/p&gt;

&lt;h1 id=&quot;get-the-source&quot;&gt;Get the Source&lt;/h1&gt;

&lt;p&gt;Use git or SVN to pull the source repo.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;I like git, so I forked the Ruby project.&lt;/li&gt;
  &lt;li&gt;Then clone it: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git clone git@github.com:danmayer/ruby.git&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;go into the directory and take a look around.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;build-trunk-before-modifying&quot;&gt;Build Trunk Before Modifying&lt;/h1&gt;

&lt;p&gt;Before you make any modifications, I recommend you get the current trunk building and test running your apps with it. Then you can create a branch and see the impact of any modifications you would like to try out.&lt;/p&gt;

&lt;h3 id=&quot;building-yourself&quot;&gt;Building Yourself&lt;/h3&gt;

&lt;p&gt;I recommend using the Ruby-buil instructions below as I continued to hit issues with building from scatch with my own options related to SSL.&lt;/p&gt;

&lt;p&gt;If you want to give it a shot thought, run the 4 commands below and it will compile and install Ruby from src &lt;!--more--&gt; on your system.&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;aclocal
autoconf
bash -c &apos;./configure&apos;
make &amp;amp;&amp;amp; make install
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you get this error about OpenSSL:&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;openssl:
	Could not be configured. It will not be installed.
	Check ext/openssl/mkmf.log for more details.
*** Fix the problems, then remove these directories and try again if you want.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It is a bit hard to resolve on OS X yourself, but luckily we can just use ruby-build to do the work for us, see below.&lt;/p&gt;

&lt;h3 id=&quot;building-via-ruby-build&quot;&gt;Building via Ruby-build&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/rbenv/ruby-build&quot;&gt;Ruby build&lt;/a&gt; will detect and use SSL from homebrew and avoid the broken OSX implementation.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;install ruby-build: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brew install ruby-build&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;copy the current dev trunk target: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cp  /usr/local/Cellar/ruby-build/20171226/share/ruby-build/2.6.0-dev /usr/local/Cellar/ruby-build/20171226/share/ruby-build/2.6.0-mine&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;edit the file (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;...ruby-build/2.6.0-mine&lt;/code&gt;) to point to your fork: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;... install_git &quot;ruby-trunk&quot; &quot;https://github.com/danmayer/ruby.git&quot; ...&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;build from your git to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.rubies/&lt;/code&gt; directory: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ruby-build 2.6.0-mine ~/.rubies/ruby-2.6.0mine&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;run from the build dir: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/usr/local/Cellar/ruby-build/20171226/share/ruby-build&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;make sure to open a new shell so ChRuby (or RbEnv) find the new ruby.&lt;/li&gt;
  &lt;li&gt;you can now reference the new build in your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.ruby-version&lt;/code&gt; file in any project&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want to build from your local git to avoid pushing to a remote branch while testing this is how your build file should end up.&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;install_package &quot;openssl-1.1.0g&quot; &quot;https://www.openssl.org/source/openssl-1.1.0g.tar.gz#de4d501267da39310905cb6dc8c6121f7a2cad45a7707f76df828fe1b85073af&quot;  mac_openssl --if has_broken_mac_openssl
install_git &quot;coverage_pause&quot; &quot;/Users/danmayer/projects/ruby&quot; &quot;feature/coverage_pause&quot; ldflags_dirs autoconf standard_build standard_install_with_bundled_gems verify_openssl
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If all is working as expected you should see 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;ruby-build 2.6.0-coverage ~/.rubies/ruby-2.6.0coverage
ruby-build: use openssl from homebrew
Cloning https://github.com/danmayer/ruby.git...
Installing coverage_pause...
ruby-build: use readline from homebrew
Installed coverage_pause to /Users/danmayer/.rubies/ruby-2.6.0coverage
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;playing-nice-with-other-rubies&quot;&gt;Playing Nice with Other Rubies?&lt;/h3&gt;

&lt;p&gt;If you are like most Rubyists you have a number of Rubies installed, beyond the default OSX Ruby, using something like RBenv, RVM, or ChRuby.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The default OS X Ruby should be: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/usr/bin/ruby&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;If you build from scratch the default target will be installed to: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/usr/local/bin&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;This can cause some issues with various Ruby environment managers&lt;/li&gt;
      &lt;li&gt;adjust your path to target this Ruby or your normal one&lt;/li&gt;
      &lt;li&gt;Another reason I recommend building via ruby-build, as it will work easier with most Ruby environment managers&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Ruby build will put ruby into the ~/.rubies
    &lt;ul&gt;
      &lt;li&gt;with ChRuby it will automatically pick these up&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then you can reference your new custom ruby in a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.ruby-version&lt;/code&gt; file at the root project directory, for example:&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;# .ruby-version
ruby-2.6.0coverage
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;running-an-app-with-your-custom-ruby&quot;&gt;Running an App with your Custom Ruby&lt;/h3&gt;

&lt;p&gt;After building your own Ruby and setting the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.ruby-version&lt;/code&gt; you should be good to go. You can verify you are running your Ruby by adding some simple print statement. Pick a favorite Ruby method and just add a print statement like so…&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;printf( &quot;hello from my method!\n&quot; );&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;push the change to the branch your referenced in your Ruby build steps
    &lt;ul&gt;
      &lt;li&gt;there are ways to build locally, but I have just targetted git branches&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;rebuild Ruby: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ruby-build 2.6.0-coverage ~/.rubies/ruby-2.6.0coverage&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;enter your project iwth the set ruby-version&lt;/li&gt;
  &lt;li&gt;run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ruby -v&lt;/code&gt; to make sure it matches expectations
    &lt;ul&gt;
      &lt;li&gt;if you get &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chruby: unknown Ruby: ruby-2.6.0coverage&lt;/code&gt; either refresh chruby or open a new terminal so it picks up the new build&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;now run things as you normally would…&lt;/li&gt;
&lt;/ul&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;# in Rakefile
desc &quot;call coverage running&quot;
task :call_coverage_running do
  require &apos;coverage&apos;
  Coverage.running?
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&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 call_coverage_running
hello from Coverage.running
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In the above output, I can see that I am runnin my custom Ruby branch as I had added a print messaged to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Coverage.running?&lt;/code&gt; method.&lt;/p&gt;

&lt;h1 id=&quot;testing-your-custom-ruby&quot;&gt;Testing your Custom Ruby&lt;/h1&gt;

&lt;p&gt;If you are trying to do some real work on Ruby other than exploring, you will want to be able to run the tests on files after you modify them, and add your own tests.&lt;/p&gt;

&lt;h3 id=&quot;running-tests&quot;&gt;Running Tests&lt;/h3&gt;

&lt;p&gt;For example to run a specific test file, you can run the below command.&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;make test-all TESTS=test/coverage/test_coverage.rb
Run options: &quot;--ruby=./miniruby -I./lib -I. -I.ext/common  ./tool/runruby.rb --extout=.ext  -- --disable-gems&quot; --excludes-dir=./test/excludes --name=!/memory_leak/

# Running tests:

Finished tests in 1.580204s, 12.0238 tests/s, 74.6739 assertions/s.
19 tests, 118 assertions, 0 failures, 0 errors, 0 skips

ruby -v: ruby 2.6.0dev (2018-01-13 trunk 61811) [x86_64-darwin15]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;running-specs&quot;&gt;Running Specs&lt;/h3&gt;

&lt;p&gt;Below we can see how to run a single specific spec file&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;make test-spec MSPECOPT=spec/ruby/library/coverage/start_spec.rb
generating x86_64-darwin15-fake.rb
x86_64-darwin15-fake.rb updated
$ /Users/danmayer/projects/ruby/miniruby -I/Users/danmayer/projects/ruby/lib /Users/danmayer/projects/ruby/tool/runruby.rb --archdir=/Users/danmayer/projects/ruby --extout=.ext -- /Users/danmayer/projects/ruby/spec/mspec/bin/mspec-run -B ./spec/default.mspec spec/ruby/library/coverage/start_spec.rb
ruby 2.6.0dev (2018-01-13 trunk 61811) [x86_64-darwin15]
[\ | ==================100%================== | 00:00:00]      0F      0E

Finished in 0.002753 seconds

1 file, 0 examples, 0 expectations, 0 failures, 0 errors, 0 tagged
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>January Software Links</title>
   <link href="https://www.mayerdan.com/programming/2018/02/03/jan-software-links"/>
   <updated>2018-02-03T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/programming/2018/02/03/jan-software-links</id>
   <content type="html">
&lt;p&gt;Some of my favorite links from January. Also, I will use this month to highlight a group of folks who have been sharing advice and helping me grow for a number of years now. Thanks!&lt;/p&gt;

&lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;If you are growing as an engineering leader find a set of mentors. Thanks, &lt;a href=&quot;https://twitter.com/glv?ref_src=twsrc%5Etfw&quot;&gt;@glv&lt;/a&gt; &lt;a href=&quot;https://twitter.com/mariagutierrez?ref_src=twsrc%5Etfw&quot;&gt;@mariagutierrez&lt;/a&gt;  &lt;a href=&quot;https://twitter.com/mad_typist?ref_src=twsrc%5Etfw&quot;&gt;@mad_typist&lt;/a&gt; &lt;a href=&quot;https://twitter.com/SteveAkers?ref_src=twsrc%5Etfw&quot;&gt;@SteveAkers&lt;/a&gt; &lt;a href=&quot;https://twitter.com/dramsay?ref_src=twsrc%5Etfw&quot;&gt;@dramsay&lt;/a&gt; &lt;a href=&quot;https://twitter.com/ryan_owens?ref_src=twsrc%5Etfw&quot;&gt;@ryan_owens&lt;/a&gt;  &lt;a href=&quot;https://twitter.com/ioutfitter?ref_src=twsrc%5Etfw&quot;&gt;@ioutfitter&lt;/a&gt; &lt;a href=&quot;https://twitter.com/chadfowler?ref_src=twsrc%5Etfw&quot;&gt;@chadfowler&lt;/a&gt; I always appreciate the thoughtful advice. &lt;a href=&quot;https://t.co/zJyBM9cNlo&quot;&gt;pic.twitter.com/zJyBM9cNlo&lt;/a&gt;&lt;/p&gt;&amp;mdash; Dan Mayer (@danmayer) &lt;a href=&quot;https://twitter.com/danmayer/status/959811287776608258?ref_src=twsrc%5Etfw&quot;&gt;February 3, 2018&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async=&quot;&quot; src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;

&lt;blockquote&gt;
  &lt;p&gt;The above folks in a mentoring group recommend good books, bounce ideas off each other, and help folks prep for big talks, events, or projects. Thanks!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;software-development&quot;&gt;Software Development&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://codeburst.io/events-as-first-class-citizens-646989808785&quot;&gt;Events As First-Class Citizens&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://regexper.com/&quot;&gt;Regular expression visualizer using railroad diagrams&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://medium.com/@citybase/elixir-a-big-picture-programming-language-755dcef2fa6a&quot;&gt;Elixir a big picture programming language&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://medium.freecodecamp.org/rest-is-the-new-soap-97ff6c09896d&quot;&gt;Rest is the new Soap&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;data&quot;&gt;Data&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Two great Postgres Links from &lt;a href=&quot;https://twitter.com/tpitale&quot;&gt;@tpitale&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;https://rcoh.me/posts/postgres-indexes-under-the-hood/&quot;&gt;Postgres Indexes Under the Hood&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;http://pgconfigurator.cybertec.at/&quot;&gt;Postgres Configuration Recommendation Tool&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;ruby&quot;&gt;Ruby&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://samsaffron.com/archive/2018/01/18/my-production-ruby-on-rails-cpu-is-at-100-now-what&quot;&gt;Debugging 100% CPU usage in production Ruby on Rails systems&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/AaronLasseigne/polyfill&quot;&gt;Ruby Polyfill back-porting Ruby features&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://tech.offgrid-electric.com/rubocop-getting-to-green-in-a-huge-rails-app-12d1ad6678eb&quot;&gt;Rubocop: Getting to Green in a Huge Rails App&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/rbspy/rbspy&quot;&gt;A really exciting sampling profiler&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://yukimotopress.github.io/blockchains&quot;&gt;learning block chains with Ruby&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;tech-management&quot;&gt;Tech Management&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.chaddickerson.com/2018/01/09/best-books-for-new-first-time-managers/&quot;&gt;best books for first time managers&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;random&quot;&gt;Random&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.ted.com/talks/amar_inamdar_the_thrilling_potential_for_off_grid_solar_energy&quot;&gt;The Thrilling Potential for off grid solar energy in Africa&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.recode.net/2018/1/31/16944976/new-parents-tech-companies-google-hp-facebook-twitter-netflix&quot;&gt;Amazon &amp;amp; Netflix leading the way on paternity leave&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;Links by &lt;a href=&quot;https://pixabay.com/en/chain-metal-chain-link-257490/&quot;&gt;wsyperek&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
</content>
 </entry>
 
 <entry>
   <title>December Software Links</title>
   <link href="https://www.mayerdan.com/programming/2017/12/31/december-software-links"/>
   <updated>2017-12-31T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/programming/2017/12/31/december-software-links</id>
   <content type="html">
&lt;p&gt;Some of my favorite links from December. If you have any good links I missed pass them my way.&lt;/p&gt;

&lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;What you learn over 5 years is that every dependency, every line of code, every test, every feature is a DEBT or outage, waiting to happen.&lt;/p&gt;&amp;mdash; David Copeland (@davetron5000) &lt;a href=&quot;https://twitter.com/davetron5000/status/934803721917222913?ref_src=twsrc%5Etfw&quot;&gt;November 26, 2017&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async=&quot;&quot; src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;

&lt;blockquote&gt;
  &lt;p&gt;continuing my trend of having something from &lt;a href=&quot;https://twitter.com/davetron5000&quot;&gt;@davetron5000&lt;/a&gt; each month, and this month I have two ;)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;software-development&quot;&gt;Software Development&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://elements.heroku.com/buttons/kimmobrunfeldt/url-to-pdf-api&quot;&gt;one-click PDF microservice&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://medium.com/@timothy_downs/introduction-to-redis-streams-133f1c375cd3&quot;&gt;on Kafka and Redis streams&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.infoq.com/presentations/elixir-distributed-system#.WkVr2AWFpx4.twitter&quot;&gt;distributed system with one process representing every user / shared resource in Elixir&lt;/a&gt;, great talk &lt;a href=&quot;https://twitter.com/myobie&quot;&gt;@myobie&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://what-problem-does-it-solve.com/serverless/index.html&quot;&gt;Understand why Serverless matters&lt;/a&gt; another great write up by &lt;a href=&quot;https://twitter.com/davetron5000&quot;&gt;@davetron5000&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;OK, updated my pre and post production testing diagram based on feedback I got. Still something I missed out? &lt;a href=&quot;https://t.co/c3Q2wUSQdI&quot;&gt;pic.twitter.com/c3Q2wUSQdI&lt;/a&gt;&lt;/p&gt;&amp;mdash; Cindy Sridharan (@copyconstruct) &lt;a href=&quot;https://twitter.com/copyconstruct/status/946179034982989824?ref_src=twsrc%5Etfw&quot;&gt;December 28, 2017&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async=&quot;&quot; src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;

&lt;h3 id=&quot;ruby&quot;&gt;Ruby&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.codeship.com/graphql-and-performance-in-rails/&quot;&gt;GraphQL Performance in Rails&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://collectiveidea.com/blog/archives/2015/03/05/optimizing-rails-for-memory-usage-part-3-pluck-and-database-laziness&quot;&gt;Memory and Performance improvements for Pluck&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;If you have to iterate through huge collections and don’t need the full object this is a huge win&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.speedshop.co/2017/12/04/malloc-doubles-ruby-memory.html&quot;&gt;Malloc Can Double Multi-threaded Ruby Program Memory Usage&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;Sidekiq users can reduce memory use by 1.5-4x with a few config/environment changes&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;devops&quot;&gt;DevOps&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://code.facebook.com/posts/270314900139291/rapid-release-at-massive-scale&quot;&gt;Rapid Release at Massive Scale deployment at Facebook&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;tech-management&quot;&gt;Tech Management&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://twitter.com/ThePracticalDev/status/900162683688935424&quot;&gt;What does a CTO do?&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://medium.com/@9len/on-code-review-16ea85f7c585&quot;&gt;On Timely Code Reviews&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;random&quot;&gt;Random&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://hackernoon.com/ten-years-in-nobody-has-come-up-with-a-use-case-for-blockchain-ee98c180100&quot;&gt;10 years in and nobody has a use case for blockchains&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;yes this is a bit of a clickbait title, but many points still hold true. The claims of distributed aren’t holding up to well with real world usage of blockchains.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;Links by &lt;a href=&quot;https://pixabay.com/en/chain-metal-chain-link-257490/&quot;&gt;wsyperek&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
</content>
 </entry>
 
 <entry>
   <title>On Tech Challenges</title>
   <link href="https://www.mayerdan.com/management/2017/12/18/on-tech-challenges"/>
   <updated>2017-12-18T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/management/2017/12/18/on-tech-challenges</id>
   <content type="html">
&lt;h1 id=&quot;coding-challenge&quot;&gt;Coding Challenge&lt;/h1&gt;

&lt;p&gt;The company I am with, &lt;a href=&quot;http://tech.offgrid-electric.com&quot;&gt;Offgrid-Electric&lt;/a&gt;, has evolved our hiring process over time to try to ensure we can find a diverse and strong distributed team. There are many &lt;!--more--&gt; interesting processes related to hiring a distributed team, but I will quickly focus on the coding challenge, technical challenge, or whatever folks are calling it these days.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;image by &lt;a href=&quot;https://pixabay.com/en/to-reach-looking-career-job-sun-2697951/&quot;&gt;FotografieLink&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;offer-challenge-options&quot;&gt;Offer Challenge Options&lt;/h1&gt;

&lt;p&gt;This post is kicked off by the thread below.&lt;/p&gt;

&lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;We accept any that the applicant is most comfortable with... Share existing code and walk us through what was done, pairing, code exercise take home, or tech challenge live on a call. I find some people are more comfortable with one method or another so we try to do anything&lt;/p&gt;&amp;mdash; Dan Mayer (@danmayer) &lt;a href=&quot;https://twitter.com/danmayer/status/942582518196424705?ref_src=twsrc%5Etfw&quot;&gt;December 18, 2017&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async=&quot;&quot; src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;

&lt;p&gt;Folks followed up asking for more details on other challenge options we provide and why, which I will try to breakdown below.&lt;/p&gt;

&lt;h1 id=&quot;what-is-the-point&quot;&gt;What is the point?&lt;/h1&gt;

&lt;p&gt;First off why have a coding exercise in person, take-home, or other? The goal is to allow a candidate to demonstrate skills beyond being great to talk with during interviews. That they can back up the conversation with problem-solving skills that match the team’s expectations. There are far too many posts and stories joking about how few developers can code, which I will skip over, but often a team wants a way to help ensure that a candidate has skills that will make them successful if they join. At OGE we try to allow for the widest possible options to prove out that a candidate would be technically qualified or the position.&lt;/p&gt;

&lt;h1 id=&quot;tech-challenges-options&quot;&gt;Tech Challenges Options&lt;/h1&gt;

&lt;p&gt;We offered a few standard options and have added more and evolved our challenges over time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Take Home Challenge:&lt;/strong&gt; We have someone on our team develop a challenge for any job description we are hiring for. The challenge is prepared before we even post a job opening. In general, we have someone on the team develop the challenge and complete it themselves to ensure they fully understand the challenge and have a good estimate of the time required to complete it (generally 2-4 hrs). This falls somewhere into the category of building a small application that does X, Y, and Z given this sample data set. Given folks likely have a current job we give plenty of time to work on the challenge and include instructions for how to submit the challenge and things we expect to see.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pair Programming:&lt;/strong&gt; We had some folks talk to us that enjoy pair programming and find it a good way to understand the way our current team works and thinks. We offer to pair on the take-home challenge and work on it with the candidate at a scheduled time, or to work on some of the code we are currently working on, or to pair on some code that the candidate has recently been working on. Some folks are very comfortable with this kind of exercise while others get very nervous pairing with someone they are just getting to know. Some folks have paired a lot in the past, or it might be a fairly new experience for them. Unless your team practices 100% pair programming, I believe you should offer some alternatives to just pairing. I like having a take-home challenge that can also be offered as a pairing exercise.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Open Source Work:&lt;/strong&gt; This is a great option for folks that already have a lot of publicly available code they are proud of and showcases their skills. There is no reason to make someone spend extra hours of their time when you could have them walk you through code they are passionate about and have already been working on. I have enjoyed learning about some candidates projects even if it wasn’t a great fit and the enthusiasm for something they have recently worked on in their own time shows through. It is &lt;strong&gt;important to have other options&lt;/strong&gt; beyond just open source. While this is a great alternative, it can’t be the primary or expected way to check off the technical challenge. You can find more detailed reasoning about this on various diversity hiring posts, but not everyone has the same privilege to spend extra time outside of work on open source, some jobs don’t allow it, it isn’t everyone’s cup of tea.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Technical Questioning:&lt;/strong&gt; While I prefer to do something with code like the options listed above, sometimes that isn’t the best fit for whatever reason. In these cases, we have done some technical white boarding type interviews over video chat. In my interview, at OGE, I walked through how to split out a monolith application into smaller component applications and discussed the kind of DevOps you need to support such transitions. This format has also worked well for QA interviews. I try to avoid trick questions / brain teasers. Asking data modeling questions tends to work pretty well over a whiteboard for an example of good technical questions.&lt;/p&gt;

&lt;h1 id=&quot;choose-your-own-adventure&quot;&gt;Choose your own Adventure&lt;/h1&gt;

&lt;p&gt;Taking this even further, given our only goal with this part of the interview process is to come away with some confidence that the person is technically capable in a set of skills… I often ask the candidate after presenting our standard challenge options listed above if that have any other way they would like to prove out technical skills. I will list some things candidates have suggested or done before.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Technical Writing / Educational Materials:&lt;/strong&gt; We have had some folks apply that have created online materials for courses. Sharing video series that teach programming concepts, blog posts detailing how to use some technology, or videos of talks given at a conference covering technically relevant material.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;School Projects / Papers:&lt;/strong&gt; If you are talking to someone recently coming out of university they might have some great projects they could share with you. If it is a group project just get into specifics about what pieces they directly contributed. We recently had a candidate submit their Ph.D. thesis as a way to confirm their technical skill.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code Reading / Walkthrough:&lt;/strong&gt; A few candidates that didn’t have open source code but as consultants had learned to quickly look at projects and distill how they worked. In this case, they were also curious to see how our systems worked. After some discussion, we walked through sections of our companies source code with the candidate reading the code and explaining what it did and the recommendations they would have to improve that section of code. While this is a bit more challenging on the interviewer’s side, it can give candidates a great picture of the kind of code and the quality of the project they will be getting involved in. If the person can quickly jump in and absorb parts of the project’s domain and explain it back to you, it can give you the confidence you need to move forward.&lt;/p&gt;

&lt;h1 id=&quot;comfortable-candidates-can-share-their-skills&quot;&gt;Comfortable Candidates Can Share Their Skills&lt;/h1&gt;

&lt;p&gt;In the end, it comes down to just making sure you set a candidate up to show you their best work. You don’t want to setup a situation that will have a great candidate so nervous they don’t show what they are capable of. Being a globally distributed team, in the end, I don’t expect all our team to work the same way, so I wouldn’t expect all candidates to succeed in the same style of challenges. You can always find a developer that loves to pair or hates it, or gets very nervous working out a problem on a whiteboard or could lecture in front of a whiteboard for hours on end. All of that is fine, let em shine.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>November Software Links</title>
   <link href="https://www.mayerdan.com/programming/2017/12/02/november-software-links"/>
   <updated>2017-12-02T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/programming/2017/12/02/november-software-links</id>
   <content type="html">
&lt;p&gt;Some of my favorite links from November. If you have any good links I missed pass them my way.&lt;/p&gt;

&lt;h3 id=&quot;software-development&quot;&gt;Software Development&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;A follow up to last months &lt;a href=&quot;https://mtlynch.io/human-code-reviews-1/&quot;&gt;part one&lt;/a&gt;, &lt;a href=&quot;https://mtlynch.io/human-code-reviews-2/&quot;&gt;part two of human code reviews&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/blog/2470-introducing-security-alerts-on-github&quot;&gt;Security Alerts from Github&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.udacity.com/grow-with-google&quot;&gt;50K scholarships to Udacity via Google&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://evilmartians.com/chronicles/embracing-metrics-as-new-tests&quot;&gt;embracing metrics vs testing&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;☠️ &amp;quot;Dead Code&amp;quot; can rot a code base over time. Here are a few common types of dead code. Be diligent and thorough about removing them as the code evolves. Even dead code can still cause problems. &lt;a href=&quot;https://t.co/vPQ7njyhEv&quot;&gt;pic.twitter.com/vPQ7njyhEv&lt;/a&gt;&lt;/p&gt;&amp;mdash; Jason McCreary (@gonedark) &lt;a href=&quot;https://twitter.com/gonedark/status/933363670494666752?ref_src=twsrc%5Etfw&quot;&gt;November 22, 2017&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async=&quot;&quot; src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;

&lt;hr /&gt;

&lt;blockquote&gt;
  &lt;p&gt;intentionally and proactively providing an extra-normal level of support and manual help to users in order to learn about the barriers people are hitting&lt;/p&gt;

  &lt;p&gt;&lt;a href=&quot;https://medium.com/code-for-america/on-self-service-and-support-driven-development-in-government-technology-5f1fcc889864&quot;&gt;service and support driven development&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;data&quot;&gt;Data&lt;/h3&gt;

&lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Running PGBouncer turned a potential catastrophe into a non-event for us. &lt;a href=&quot;https://t.co/IIbk3vPzit&quot;&gt;https://t.co/IIbk3vPzit&lt;/a&gt;&lt;/p&gt;&amp;mdash; David Copeland (@davetron5000) &lt;a href=&quot;https://twitter.com/davetron5000/status/935564668822622208?ref_src=twsrc%5Etfw&quot;&gt;November 28, 2017&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async=&quot;&quot; src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://pgbouncer.github.io/&quot;&gt;more about PgBouncer&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://www.citusdata.com/blog/2017/05/10/scaling-connections-in-postgres/&quot;&gt;scaling connections in Postgres&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;Querying across RDS and Redshift, checkout &lt;a href=&quot;https://aws.amazon.com/blogs/big-data/join-amazon-redshift-and-amazon-rds-postgresql-with-dblink/&quot;&gt;dblink&lt;/a&gt;. Enabling some powerful data warehouse jobs. Thanks Alex from &lt;a href=&quot;https://twitter.com/AnjLab&quot;&gt;@anjlabs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;A story of rolling out &lt;a href=&quot;https://evilmartians.com/chronicles/partition-and-conquer&quot;&gt;DB partitioning on a live DB&lt;/a&gt; and notes on how to do it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;ruby&quot;&gt;Ruby&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Reduce open DB connections &lt;a href=&quot;https://github.com/rails/rails/pull/28057&quot;&gt;from Sidekiq in Rails&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://weblog.rubyonrails.org/2017/11/27/Rails-5-2-Active-Storage-Redis-Cache-Store-HTTP2-Early-Hints-Credentials/&quot;&gt;Rails 5.2 beta&lt;/a&gt; lots of good stuff
    &lt;ul&gt;
      &lt;li&gt;huge improvements in Redis cache&lt;/li&gt;
      &lt;li&gt;active storage&lt;/li&gt;
      &lt;li&gt;faster boot time with bootsnap&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;devops&quot;&gt;Devops&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Things &lt;a href=&quot;https://medium.com/@hbarcelos/things-i-wish-i-knew-about-terraform-before-jumping-into-it-43ee92a9dd65&quot;&gt;I wish I knew about terraform&lt;/a&gt; before jumping into it&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://hub.docker.com/r/iridakos/duckrails/&quot;&gt;DuckRails: quickly mock API endpoints&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://dependabot.com/&quot;&gt;dependbot automate dependency updates&lt;/a&gt; thanks &lt;a href=&quot;https://twitter.com/dmitrytsepelev&quot;&gt;@dmitrytsepelev&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;tech-management&quot;&gt;Tech Management&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.cnbc.com/2015/01/26/working-more-than-50-hours-makes-you-less-productive.html&quot;&gt;working more than 50 hours makes you less productive&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://firstround.com/review/veteran-cto-with-20-years-experience-answers-your-top-startup-building-questions/&quot;&gt;thoughts from a 20 year startup CTO veteran&lt;/a&gt; thanks &lt;a href=&quot;https://twitter.com/dramsay&quot;&gt;@dramsay&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://tech.offgrid-electric.com/book-review-remote-office-not-required-e75e19c68529&quot;&gt;Review of Remote: Office Not Required&lt;/a&gt; by &lt;a href=&quot;https://twitter.com/moyheen&quot;&gt;@moyheen&lt;/a&gt; who is part of our OGE team.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://m.signalvnoise.com/case-study-how-complexity-creeps-in-cba48023e6a1&quot;&gt;Case study: How complexity creeps in&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;random&quot;&gt;Random&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://twitter.com/patio11/status/936616624378978304&quot;&gt;Things Many People Find Too Obvious To Have Told You&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.investmentzen.com/data-visualization/parental-leave-in-the-united-states-vs-the-world/&quot;&gt;Parental Leave in the US vs the World&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;Links by &lt;a href=&quot;https://pixabay.com/en/chain-metal-chain-link-257490/&quot;&gt;wsyperek&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
</content>
 </entry>
 
 <entry>
   <title>Active Record Database Documentation</title>
   <link href="https://www.mayerdan.com/ruby/2017/11/12/active-record-documentation-in-rails"/>
   <updated>2017-11-12T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/ruby/2017/11/12/active-record-documentation-in-rails</id>
   <content type="html">
&lt;blockquote&gt;
  &lt;p&gt;Documentation Folders &lt;a href=&quot;https://pixabay.com/en/ring-binders-aligned-organization-2654130/&quot;&gt;CC image from Pixabay&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;active-record-database-documentation&quot;&gt;Active Record Database Documentation&lt;/h1&gt;

&lt;p&gt;This post will cover how to use the Active Record feature to add comments to your tables, columns, and indexes. This feature makes it easy to keep your database documentation up to date as you can add descriptions at the same time as you add or update tables and columns.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The best documentation about the feature is contained in the original Pull Request &lt;a href=&quot;https://github.com/rails/rails/pull/22911&quot;&gt;support comments in database&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;The feature was also well covered in &lt;a href=&quot;https://blog.bigbinary.com/2016/06/21/rails-5-supports-adding-comments-migrations.html&quot;&gt;Rails 5 supports adding comments in migrations&lt;/a&gt; by Prajakta Tambe&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;documentation-embedded-with-change-process&quot;&gt;Documentation Embedded with Change Process&lt;/h1&gt;

&lt;p&gt;Why would we want to use Rails to build our database documentation?&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;I believe documentation close to code and embedded in the code change process has a better chance of staying up to date and relevant.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I also think we want to add human context to our information in same tools we build our database. We want to do this in source code that is trackable for the same reason we run database migrations in Rails opposed to just having a DBA make schema changes outside of our application code change process.&lt;/p&gt;

&lt;p&gt;When we embed our database documentation in our standard code change process we easily get many advantages.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;See &lt;!--more--&gt; DB comments change over time because they are part of Git&lt;/li&gt;
  &lt;li&gt;Search in code editor tools (and github)&lt;/li&gt;
  &lt;li&gt;Documentation can be reviewed as part of PRs by a data team, analysts, or other folks who might be the target documentation audience&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By having the documentation embedded in the database directly, other values can be unlocked.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The documentation is embedded in most DB explorer tools (SQL workbench, Postico, etc).&lt;/li&gt;
  &lt;li&gt;Single source of truth documentation. It is easy to generate and push to documentation repositories (markdown, html, confluence). Either from Rails, CI, or any other tool in your workflow (see examples below).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/postico_coments.png&quot; alt=&quot;Database Documentation in Postico&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;Postico OSX Postgres client showing comments as you explore DB structure&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;code-samples&quot;&gt;Code Samples&lt;/h1&gt;

&lt;p&gt;Below are some code samples to help you get started with a workflow around database documentation.&lt;/p&gt;

&lt;h3 id=&quot;migration&quot;&gt;Migration&lt;/h3&gt;

&lt;p&gt;A migration adding comments to a previously existing table. You can add descriptions to call out deprecated fields, gotchas, planned refactorings, or add historical context that may be helpful to the next developer trying to understand what the field means.&lt;/p&gt;

&lt;div class=&quot;language-ruby 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;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;AddContactComments&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Migration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;5.1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;change&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Contacts table holds individual details about our contacts, it is associated with leads and customers&apos;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;change_table_comment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:contacts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;change_column_comment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:contacts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:first_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;the contacts first name&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;change_column_comment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:contacts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:last_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;the contacts last name&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;change_column_comment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:contacts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:house_latitude&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;the house_latitude the contact lives at&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;change_column_comment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:contacts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:house_longitude&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;the house_longitude the contact lives at&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;change_column_comment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:contacts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:house_location_accuracy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;the accuracy range we captured the GPS with&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;change_column_comment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:contacts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:deleted_at&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;the date the contact was &apos;hidden&apos; from our DB&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    
    &lt;span class=&quot;c1&quot;&gt;# call out gotchas&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# tasks have assignee_id while contacts still use agent_id in the DB, this is a recommended refactoring&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;the agent_id field is for who the contact is currently assigned to, various places in the code and API it is referenced by assignee_id&apos;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;change_column_comment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:leads&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:agent_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;ruby-db-docs-access&quot;&gt;Ruby DB Docs Access&lt;/h3&gt;

&lt;p&gt;To generate documentation which could be pushed to a wiki, html, confluence, or elsewhere you can iterate through a tables columns and fetch the comments.&lt;/p&gt;

&lt;div class=&quot;language-ruby 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;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;connection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;table_comment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;leads&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Leads table holds individual details about leads, related associations, event timestamps, and joins to contact&quot;&lt;/span&gt;	
&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Contact&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;columns_hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;literacy&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;comment&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reading&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;level: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;-1&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;none&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;0&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;no_read&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;1&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;limited_read&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;2&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;read_fluent&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;none&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;none&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;no_read&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;no_read&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;limited_read&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;limited_read&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;read_fluent&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;read_fluent&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# iterate through all the columns on a table and output them to your documentation file or API&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Contact&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;columns&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;comment&lt;/span&gt; 
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;sql-db-docs-access&quot;&gt;SQL DB Docs Access&lt;/h3&gt;

&lt;p&gt;Obviously you don’t need Rails to get at this information, you can pull it out with raw SQL as well. Covered in this post: &lt;a href=&quot;http://www.postgresonline.com/journal/archives/215-Querying-table,-view,-column-and-function-descriptions.html&quot;&gt;Querying table, view, column and function descriptions&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;language-sql 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;o&quot;&gt;#&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;the&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;all&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;the&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;table&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;comments&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;your&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DB&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;relname&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;As&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;CASE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;WHEN&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;relkind&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;v&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;THEN&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;view&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ELSE&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;table&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;As&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
    &lt;span class=&quot;n&quot;&gt;pg_get_userbyid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;relowner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;towner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;spcname&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tspace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
    &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nspname&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;description&lt;/span&gt;
   &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pg_class&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;As&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;c&lt;/span&gt;
   &lt;span class=&quot;k&quot;&gt;LEFT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;JOIN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pg_namespace&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oid&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;relnamespace&lt;/span&gt;
   &lt;span class=&quot;k&quot;&gt;LEFT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;JOIN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pg_tablespace&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oid&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reltablespace&lt;/span&gt;
   &lt;span class=&quot;k&quot;&gt;LEFT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;JOIN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pg_description&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;As&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objoid&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oid&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objsubid&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;relkind&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;r&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;v&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&apos;&lt;/span&gt;
   &lt;span class=&quot;k&quot;&gt;ORDER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nspname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;relname&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;#&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;all&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;the&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;comments&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;the&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;contacts&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;table&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;attname&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;As&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;column_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;description&lt;/span&gt;
   &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pg_class&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;As&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;c&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;INNER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;JOIN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pg_attribute&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;As&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oid&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;attrelid&lt;/span&gt;
   &lt;span class=&quot;k&quot;&gt;LEFT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;JOIN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pg_namespace&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oid&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;relnamespace&lt;/span&gt;
   &lt;span class=&quot;k&quot;&gt;LEFT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;JOIN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pg_tablespace&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oid&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reltablespace&lt;/span&gt;
   &lt;span class=&quot;k&quot;&gt;LEFT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;JOIN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pg_description&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;As&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objoid&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oid&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objsubid&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;attnum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt;  &lt;span class=&quot;k&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;relkind&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;r&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;v&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nspname&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;public&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;relname&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;contacts&apos;&lt;/span&gt;
   &lt;span class=&quot;k&quot;&gt;ORDER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nspname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;relname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;attname&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;
</content>
 </entry>
 
 <entry>
   <title>October Software Links</title>
   <link href="https://www.mayerdan.com/programming/2017/11/02/october-software-links"/>
   <updated>2017-11-02T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/programming/2017/11/02/october-software-links</id>
   <content type="html">
&lt;p&gt;A few days late for the end of the month, but that is OK… Some of my favorite links from this month. Also, if you aren’t following our new &lt;a href=&quot;https://tech.offgrid-electric.com/&quot;&gt;OGE tech blog&lt;/a&gt;, it has some great content. I included a few posts from our blog this month.&lt;/p&gt;

&lt;h3 id=&quot;software-development&quot;&gt;Software Development&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://davidcel.is/posts/the-story-of-my-redis-database/&quot;&gt;Redis from 1.5gb to 50mb&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://anbasile.github.io/programming/2017/06/25/jupyter-venv/&quot;&gt;Using jupyter notebooks with a virtual environment&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://tech.offgrid-electric.com/domain-directory-structure-for-react-apps-why-its-worth-trying-b3855ee77a1e&quot;&gt;Domain directory structure for React apps&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://dev-blog.apollodata.com/graphql-schema-stitching-8af23354ac37&quot;&gt;GraphQL schema stitching&lt;/a&gt; thanks &lt;a href=&quot;https://twitter.com/jegiffin&quot;&gt;@jegiffin&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://mtlynch.io/human-code-reviews-1/&quot;&gt;How to Do Code Reviews Like a Human&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;data&quot;&gt;Data&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.newrelic.com/2017/10/23/optimizing-k-means-clustering/&quot;&gt;Optimizing k-means Clustering for Time Series Data&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;ruby&quot;&gt;Ruby&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://medium.com/@kirill_shevch/lint-your-ruby-code-with-overcommit-and-static-analysis-tools-bd36d3147d2e&quot;&gt;Lint your Ruby code with Overcommit and static analysis tools&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;android&quot;&gt;Android&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=704qQzEnT0I&amp;amp;index=12&quot;&gt;Seeing is Believing: Mobile Vision API Deep Dive&lt;/a&gt; Hooray &lt;a href=&quot;https://twitter.com/moyheen&quot;&gt;@moyheen&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=rUEAXFO1hgg&amp;amp;index=33&amp;amp;t=109s&quot;&gt;Android: No Internet? No Problem!&lt;/a&gt; Hooray &lt;a href=&quot;https://twitter.com/moyheen&quot;&gt;@moyheen&lt;/a&gt; &amp;amp; &lt;a href=&quot;https://twitter.com/brwngrldev&quot;&gt;@brwngrldev&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;tech-management&quot;&gt;Tech Management&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://tech.offgrid-electric.com/planning-a-remote-team-summit-9464755b0fdc&quot;&gt;Planning A Remote Team Summit&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://medium.com/@meganberry/the-complete-guide-to-working-on-a-remote-team-6bb11213e37c&quot;&gt;The Complete Guide to Working On A Remote Team&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;random&quot;&gt;Random&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.bloomberg.com/news/articles/2017-10-30/record-winds-in-germany-spur-free-electricity-at-weekend-chart&quot;&gt;So Much Wind Power In Germany This Weekend, Consumers Got Free Energy&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://slidemodel.com/google-slides/&quot;&gt;Helpful Google Slides templates&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://pixabay.com/&quot;&gt;Great Free stock photos, I use frequently on my blog&lt;/a&gt;, thanks &lt;a href=&quot;https://twitter.com/brwngrldev&quot;&gt;@brwngrldev&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;Links by &lt;a href=&quot;https://pixabay.com/en/chain-metal-chain-link-257490/&quot;&gt;wsyperek&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
</content>
 </entry>
 
 <entry>
   <title>Databases Across Environments</title>
   <link href="https://www.mayerdan.com/ruby/2017/10/26/db-syncs-for-staging-and-development"/>
   <updated>2017-10-26T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/ruby/2017/10/26/db-syncs-for-staging-and-development</id>
   <content type="html">
&lt;blockquote&gt;
  &lt;p&gt;DB Syncs &lt;a href=&quot;https://pixabay.com/p-1954920/?no_redirect&quot;&gt;CC image from Pixabay&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;syncing-databases-across-environments&quot;&gt;Syncing Databases Across Environments&lt;/h1&gt;

&lt;p&gt;It seems that often when a business grows, at some point it is hard to create useful QA, development, and staging data to cover all the cases that can occur on production. Eventually, there is a need to try to replicate certain cases or debug various issues that is much easier when you can pull the production data to another system to experiment and debug safely. Nearly, everywhere I have worked eventually we need to clone large portions of the production DB to staging… Quickly followed by wanting to pull staging data to local development. While sometimes various privacy and security concerns must be taken into account, in general we are just talking about replicating a database or &lt;!--more--&gt; tables from one place to another. I will cover some approaches to moving DBs.&lt;/p&gt;

&lt;h1 id=&quot;db-rotation-on-aws&quot;&gt;DB Rotation on AWS&lt;/h1&gt;

&lt;p&gt;If you are in a cloud environment use the tools they provide when you can and avoid building a custom solution. We use AWS, and we run our Postgres DB via the RDS service. We have backup and retention rules and the ability to restore snapshots to different names. We leverage snapshots to move the DBs from one environment to another. If you are using Google cloud or Heroku there are similar options. I am not going to layout the code, but you can also do it easily via the AWS GUI. Our basic setup when in the cloud is detailed below.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Production: has a nightly snapshot taken&lt;/li&gt;
  &lt;li&gt;Staging:
    &lt;ul&gt;
      &lt;li&gt;about once every other month we restore the staging DB from a production snapshot&lt;/li&gt;
      &lt;li&gt;after the snapshot is restored some scripts run (clean up, sanitize, and truncate large un-needed tables) so it will run better on a smaller DB instance&lt;/li&gt;
      &lt;li&gt;Careful about various test data that is expected by managers and your QA team. You might have destroyed a lot of work&lt;/li&gt;
      &lt;li&gt;We handle that by QA regression suite will detect our QA team data is missing and recreate example QA data for the team&lt;/li&gt;
      &lt;li&gt;staging has nightly snapshots taken&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Dev Stacks: deployed development environments
    &lt;ul&gt;
      &lt;li&gt;these are created dynamically by developers from git branches&lt;/li&gt;
      &lt;li&gt;they restore a new DB from the latest staging snapshot&lt;/li&gt;
      &lt;li&gt;then run migrations and whatnot on the branch to get it to the correct state&lt;/li&gt;
      &lt;li&gt;we don’t take any back ups of these stacks which are traditionally destroyed after a few days of use&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;full-db-to-local-development&quot;&gt;Full DB to local Development&lt;/h1&gt;

&lt;p&gt;While the AWS toolchain is great you can’t restore an AWS snapshot to your local Postgres DB. For that we just created a simple script that will dump Postgres and upload it to S3 in a compressed format. We have some options to exclude some extra large tables that we generally don’t care about for development, you could always pull those later using the method for individual tables mentioned later.&lt;/p&gt;

&lt;h3 id=&quot;full-postgres-to-s3-dump-rake-task&quot;&gt;Full Postgres to S3 Dump Rake Task&lt;/h3&gt;

&lt;div class=&quot;language-ruby 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;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;db_to_dump&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;connection_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:database&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;db_host&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;connection_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:host&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;db_port&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;connection_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:port&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;db_user&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;connection_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:username&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;db_pass&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;connection_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:password&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;###&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# Run this on the target environment you wish to dump (staging dev stack)&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# bundle exec rake data:dump_full_db&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;#&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# The DB files should get uploaded to S3 ready &lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# to be pulled into another environment&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;###&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;dump full DB to S3&apos;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;dump_full_db: :environment&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;tables_to_exclude&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;%w[really_big_tables papertrail_versions]&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;exclude_lines&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tables_to_exclude&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--exclude-table-data=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos; &apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;full_db_file&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;/tmp/full_dev_dump_data.sql&apos;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;begin&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;s3&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;AWS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;S3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;access_key_id: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;S3_ACCESS_KEY_ID&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
                     &lt;span class=&quot;ss&quot;&gt;secret_access_key: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;S3_SECRET_ACCESS_KEY&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;bucket&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;buckets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;dev-db-bucket&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    
      &lt;span class=&quot;c1&quot;&gt;# HACK this is needed so you don&apos;t pass the password&lt;/span&gt;
      &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pg_pass_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;w&apos;&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;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db_host&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db_port&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db_to_dump&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db_user&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db_pass&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&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;sb&quot;&gt;`chmod 600 &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pg_pass_file&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
      
      &lt;span class=&quot;sb&quot;&gt;`pg_dump &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exclude_lines&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt; -O -v -x -F c -f &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;full_db_file&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt; -h &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db_host&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt; -p &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db_port&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt; -U &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db_user&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt; &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db_to_dump&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;

      &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;full_db_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;exists?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;uploading &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Pathname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bucket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;basename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;ensure&lt;/span&gt;
      &lt;span class=&quot;sb&quot;&gt;`rm &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pg_pass_file&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt; &amp;amp;&amp;gt; /dev/null`&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  
  &lt;span class=&quot;c1&quot;&gt;###&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# Run this on the environment you wish to create or restore&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# the most recent dump.&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;#&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# bundle exec rake reload_full_from_s3&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;###&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;reload dev DB from S3&apos;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;reload_full_from_s3: :environment&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;SKIP_DOWNLOAD&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;s3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bucket&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s3_and_bucket&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;full_db_file&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;/tmp/full_dev_dump_data.sql&apos;&lt;/span&gt;

      &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;full_db_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;downloading &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Pathname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bucket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;basename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

        &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;wb&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s3_file&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;read&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;chunk&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;s3_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;chunk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;db_to_dump&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;connection_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:database&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;remove_connection&lt;/span&gt;

    &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;sb&quot;&gt;`psql -c &quot;drop database &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db_to_dump&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;;&quot;`&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vg&quot;&gt;$?&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
      &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;drop DB failed (you likely need to close console or app)&apos;&lt;/span&gt;
      &lt;span class=&quot;nb&quot;&gt;exit&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;sb&quot;&gt;`psql -c &quot;create database &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db_to_dump&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;;&quot;`&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;sb&quot;&gt;`pg_restore  --verbose --dbname &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db_to_dump&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt; -F c &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;full_db_file&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;single-table-to-staging-or-development&quot;&gt;Single table to Staging or Development&lt;/h1&gt;

&lt;p&gt;OK that makes it easy for a new dev to get a copy of the latest full DB, what if you just want to pull the most recent payments, customers, or products… A much faster way it to load a table or two.&lt;/p&gt;

&lt;h3 id=&quot;postgres-single-table-s3-dump--load-rake-task&quot;&gt;Postgres Single table S3 Dump &amp;amp; Load Rake Task&lt;/h3&gt;

&lt;div class=&quot;language-ruby 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;###&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# Run on target environment to dump a table&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# TABLE_NAME=phone_block_lists bundle exec rake db:dump_db_table&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;###&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;dump single DB table to S3&apos;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;dump_db_table: :environment&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;table&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;TABLE_NAME&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;users&apos;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;table_file&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/tmp/&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.sql&quot;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;begin&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;s3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bucket&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s3_and_bucket&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;write_pg_pass&lt;/span&gt;
      &lt;span class=&quot;sb&quot;&gt;`pg_dump --no-owner --no-acl -v -F c -h &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db_host&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt; -p &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db_port&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt; -U &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db_user&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt; --table public.&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt; &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db_to_dump&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt; &amp;gt; &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table_file&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;

      &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;exists?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;uploading &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Pathname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bucket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;basename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;ensure&lt;/span&gt;
      &lt;span class=&quot;sb&quot;&gt;`rm &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pg_pass_file&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt; &amp;amp;&amp;gt; /dev/null`&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  
  &lt;span class=&quot;c1&quot;&gt;###&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# Run on environment where you want to load the table&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# TABLE_NAME=phone_block_lists bundle exec rake db:reload_table_from_s3&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;###&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;reload dev DB table from S3&apos;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;reload_table_from_s3: :environment&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;table&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;TABLE_NAME&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;administrative_areas&apos;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;table_file&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/tmp/&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.sql&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;db_to_dump&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;connection_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:database&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;SKIP_DOWNLOAD&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;s3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bucket&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s3_and_bucket&lt;/span&gt;

      &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;downloading &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Pathname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bucket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;basename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

        &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;wb&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s3_file&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
          &lt;span class=&quot;c1&quot;&gt;# this is slow perhaps move to AWS CLI download&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;read&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;chunk&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;s3_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;chunk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;sb&quot;&gt;`psql --dbname &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db_to_dump&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt; -c &quot;truncate table &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;&quot;`&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;sb&quot;&gt;`pg_restore  --verbose --data-only --dbname &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db_to_dump&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;  -F c &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table_file&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;database-helper-scripts&quot;&gt;Database Helper Scripts&lt;/h1&gt;

&lt;p&gt;These are small examples extracted out from our database helper rake task. We have a number of other little helpers as well. It can be useful to build up some small simple tools to help you team move data around environments. It can also really simplify the process of bring on new developers to the team. Hope the simple examples above will be helpful to some folks.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Planning A Remote Team Summit</title>
   <link href="https://www.mayerdan.com/management/2017/10/25/planning-remote-team-summit"/>
   <updated>2017-10-25T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/management/2017/10/25/planning-remote-team-summit</id>
   <content type="html">
&lt;h1 id=&quot;planning-a-remote-team-summit&quot;&gt;Planning A Remote Team Summit&lt;/h1&gt;

&lt;p&gt;It is essential for distributed teams to have face to face time. We try to get the entire department to get together at least once a year. In general, smaller groups within our department also get together from time to time to collaborate on specific projects as well. If you read about organizations that do remote work well, they organize one or more team summits/retreats a year.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The team enjoying the rare chance to use post-its in person&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;value-of-distributed-team-summits&quot;&gt;Value of Distributed Team Summits&lt;/h2&gt;

&lt;p&gt;At our summits we work through pain points in our processes and structure, we build trust, and collaborate taking advantages of some of the in-person advantages inherent to being face to face… For example whiteboards (we miss you), post-it-notes, sketches, and more. The value of the summit will long outlast any specific decision or activity. The team will walk away more confident in having challenging discussions, reaching out across organizational boundaries, and assuming best intentions first when issues arise. The most valuable part and the prominent goal of bringing everyone together is team bonding and building trust.&lt;/p&gt;

&lt;h2 id=&quot;summit-planning&quot;&gt;Summit Planning&lt;/h2&gt;

&lt;p&gt;Planning required for a proper summit shouldn’t be underestimated. At OGE, we put together a small workgroup that met weekly to plan the event. The group developed a schedule, planned sessions, activities, and assigned follow up tasks to specific people. Our workgroup &lt;!--more--&gt; used slack, confluence, and weekly video chats to plan our summit.&lt;/p&gt;

&lt;h3 id=&quot;logistics&quot;&gt;Logistics:&lt;/h3&gt;

&lt;p&gt;The logistics around planning a summit for a distributed team summit gets increasingly complicated as your team grows. Early gatherings might start as simple as flying a handful of people and booking hotel rooms. As an organization expands, it will require coordination among dozens of people across time zones, countries, and schedules.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Start planning early:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Finding dates for your event that work across dozens of people and countries holidays will be a challenge.&lt;/li&gt;
  &lt;li&gt;Many folks may need visas and you will want to start on this with plenty of time (OGE, unfortunately, ended up having a Visa issue preventing one person from attending this year)&lt;/li&gt;
  &lt;li&gt;Avoid high hotel and airfare costs by being able to book well in advance&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Travel Help:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Depending on your organization it may be helpful to coordinate flights, hotels, etc.&lt;/li&gt;
  &lt;li&gt;Also, it might be easier to let some things be handled ad-hoc and allowing for expensing ad-hoc costs (ex: travel to/from the Airport)&lt;/li&gt;
  &lt;li&gt;A reminder on guidelines around expensing will help put people at ease and avoid unexpected expenses&lt;/li&gt;
  &lt;li&gt;Depending on the situation folks might need help with Visas (ex: letters from businesses requesting their travel for an event, proof of hotel accommodations, etc.)&lt;/li&gt;
  &lt;li&gt;Booking blocks of rooms at hotels or whole Airbnb houses can help with cost and logistics by keeping the group close to each other and the venue&lt;/li&gt;
  &lt;li&gt;Booking a venue, when small you might meet at a local office, but as you grow a local office likely can’t handle an influx of the full international team&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Day to Day help:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Having someone from the local team available to help with planning can be very valuable&lt;/li&gt;
  &lt;li&gt;Coordinating lunches can save a lot of time in the schedule. (self-exploring, forming groups, and finding lunch in an unknown city lead to lots of missed afternoon meetings)&lt;/li&gt;
  &lt;li&gt;As there will likely be needs for some communication and standard work, planning on reliable and fast internet connections is highly recommended&lt;/li&gt;
  &lt;li&gt;Some recommendations on dinners, things to do, how to use local transit, etc&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/team_summit_two.jpeg&quot; alt=&quot;Team Summit&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;Discussing our DevOps challenges in person&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;communications-keeping-everyone-up-to-speed&quot;&gt;Communications, keeping everyone up to speed&lt;/h2&gt;

&lt;p&gt;It is important to communicate about your event to various people in the organization before, during, and after the event. I will cover some essential communications to keep in mind.&lt;/p&gt;

&lt;h2 id=&quot;prior-to-the-event&quot;&gt;Prior to the event&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Exec / Budget:&lt;/strong&gt; This communication has to happen far before any event. This happens as you build a team and invest in a distributed workforce. If a growing budget expense for an international summit isn’t understood as necessary all the way up the organization, your group probably shouldn’t be building an international team. Make sure folks know while sometimes you save on salary by not hiring everyone in expensive markets or having office space for all employees there are some overhead costs, such as increased travel budgets to ensure the team can get together from time to time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stakeholders and Partners:&lt;/strong&gt; Let stakeholders know they will have limited access to the team and deliverables likely won’t be released (or make progress) during the event. Sharing a bit about why it is necessary for a distributed team to work on processes, planning, and collaboration in person from time to time. Let folks know it is coming up as soon as you have dates. Follow up sending reminders immediately before the event. The reminder email should include clear guidelines on expectation on what work will or will not happen that week. Also, officially cancel any regularly scheduled meetings, and explain how to raise any emergency issues that may arise (phone numbers, SMS, and contact info for backups that might not be able to attend the event, etc.).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Internal team:&lt;/strong&gt; set expectations that we are there to communicate and work together, and to not be on laptops on slack and in remote meetings much during the week. The costs and value of getting together are too valuable to end up spending it as a regular week. Share travel tips, schedules, a map with notable locations, and contact info to a local person to help resolve any unexpected travel issues.&lt;/p&gt;

&lt;h2 id=&quot;during-the-event&quot;&gt;During the Event&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Stakeholders and Partners:&lt;/strong&gt; Send a quick reminder as the event starts. Folks sometimes enjoy seeing some photos or updates as the event occurs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Internal team:&lt;/strong&gt; If any of the schedule plans change make sure to share with everyone. Makes sure folks have a way to access changes when away from venue internet. Either in person or async during the event take little check-ins to make sure the event is going well, the discussions are valuable and that nobody is struggling with some unexpected challenge related to travel, accommodations, etc.&lt;/p&gt;

&lt;h2 id=&quot;after-the-event&quot;&gt;After the Event&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Everyone:&lt;/strong&gt; We have both slack and workplace where the organization shares news. The team should share a write-up with the company explaining what happened, where, and some updates on things you did. Share some photos, let other departments know what your team is up to and celebrate a bit of the rate time you get together.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Internal team:&lt;/strong&gt; follow up on action items, plans and re-enforce decisions that were made at the event.&lt;/p&gt;

&lt;h2 id=&quot;setting-an-event-format&quot;&gt;Setting an Event Format&lt;/h2&gt;

&lt;p&gt;There are many formats and ways to plan events, so I won’t try to cover them all. Instead, I can say it is good for the group planning the event to find some structure they can agree on. Then work together to roll it out. Our planning group read The Advantage and loosely followed some of its guidelines as well as some of our ideas to plan the event. I think any book or document can help guide planning towards a successful event. In the end, our high-level agenda looked like below having topic theme focuses each day, with the last day planned to follow up on topics and questions that came up during the event.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Day 1: Trust&lt;/li&gt;
  &lt;li&gt;Day 2: Method&lt;/li&gt;
  &lt;li&gt;Day 3: Impact&lt;/li&gt;
  &lt;li&gt;Day 4: Innovation&lt;/li&gt;
  &lt;li&gt;Day 5: Acceleration&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;was-the-summit-a-success&quot;&gt;Was the summit a success?&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/team_summit_three.jpeg&quot; alt=&quot;Team Summit Success&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;The primary meeting room&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;How did our event go? I think it went well and accomplished many of our goals. Everyone came away saying it was a great event and that we should do them more often. We sent out a survey to all the attendees and scored higly across the board. For a bit, more details on our most recent event read our &lt;a href=&quot;https://tech.offgrid-electric.com/recap-digital-platforms-summit-2017-390922a124de&quot;&gt;Distributed Teams Amsterdam Summit Recap&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>September Software Links</title>
   <link href="https://www.mayerdan.com/programming/2017/09/25/software-links"/>
   <updated>2017-09-25T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/programming/2017/09/25/software-links</id>
   <content type="html">
&lt;h3 id=&quot;software-development&quot;&gt;Software Development&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.sandimetz.com/blog/2017/9/13/breaking-up-the-behemoth&quot;&gt;Breaking Up the Behemoth&lt;/a&gt; thanks &lt;a href=&quot;https://twitter.com/tpitale&quot;&gt;@ tpitale&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://segment.com/blog/the-right-way-to-manage-secrets/&quot;&gt;How to manage secrets&lt;/a&gt; thanks &lt;a href=&quot;https://twitter.com/tpitale&quot;&gt;@ tpitale&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://medium.com/@erand/we-are-all-product-owners-an-impact-guide-for-engineers-76a2b4342c74&quot;&gt;We are all product owners an impact guide&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.smashingmagazine.com/2017/08/nine-principles-design-implementation/&quot;&gt;The Nine Principles Of Design Implementation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://zapier.com/blog/remote-work-challenges/&quot;&gt;Avoiding remote work pitfalls&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://medium.com/@daniel.heller/ten-principles-for-growth-69015e08c35b&quot;&gt;10 principles for engineering growth&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.kitchensoap.com/2017/08/12/multiple-perspectives-on-technical-problems-and-solutions/&quot;&gt;Multiple Perspectives On Technical Problems and Solutions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;data&quot;&gt;Data&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.dataquest.io/blog/making-538-plots/&quot;&gt;Readable plots with python, with 538 style&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Data Mapping, &lt;a href=&quot;https://www.ciesin.columbia.edu/data/hrsl/&quot;&gt;High Resolution Settlement Layer&lt;/a&gt; thanks &lt;a href=&quot;https://twitter.com/alepietrobon&quot;&gt;@alepietrobon&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;How to grow a &lt;a href=&quot;https://medium.com/towards-data-science/how-to-grow-data-7d1892792b6f&quot;&gt;data driven team&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://robots.thoughtbot.com/reading-an-explain-analyze-query-plan&quot;&gt;Reading an explain analyze query plan&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;ruby&quot;&gt;Ruby&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/rails/rails/pull/28057&quot;&gt;reduce open DB connection from Sidekiq&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://schneems.com/2017/07/18/how-i-reduced-my-db-server-load-by-80/&quot;&gt;reduce DB load with constraints&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;random&quot;&gt;Random&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://jasmcole.com/2017/09/20/the-moon-blew-up-without-warning-and-for-no-apparent-reason/&quot;&gt;The moon blew up without warning&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;Random Links by &lt;a href=&quot;https://www.flickr.com/photos/smemon/5144379598&quot;&gt;Sean MacEntee&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
</content>
 </entry>
 
 <entry>
   <title>Markdown to Confluence Wikis</title>
   <link href="https://www.mayerdan.com/programming/2017/08/27/markdown-to-confluence"/>
   <updated>2017-08-27T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/programming/2017/08/27/markdown-to-confluence</id>
   <content type="html">
&lt;blockquote&gt;
  &lt;p&gt;Markdown converted for Atlassian’s Confluence&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you ever have a need to convert a bunch of markdown or html files to confluence it is really easy. Their Rest-API is pretty clean and if you use Ruby there is a small &lt;a href=&quot;https://github.com/amishyn/confluence-api-client&quot;&gt;Confluence API gem&lt;/a&gt; to make it even easier.&lt;/p&gt;

&lt;h1 id=&quot;why-move-to-confluence&quot;&gt;Why Move To Confluence?&lt;/h1&gt;

&lt;p&gt;Why would we move our documentation from Markdown in a git repo, to Confluence? Well our team is growing and we interact with more and more non developers, who could benefit and contribute to our documentation. While some of our documentation was very technical much of it wasn’t. It would be helpful to our Support, IT, Project Management, and Stakeholders. Markdown and git aren’t the best way to get other folks comfort	able reading or contributing to our documentation.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Increased collaboration&lt;/li&gt;
  &lt;li&gt;Simplified access control to read&lt;/li&gt;
  &lt;li&gt;Ability to mix ticketing, widgets, and wiki docs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That was enough for us to decide on moving our documentation over.
&lt;!--more--&gt;&lt;/p&gt;

&lt;h1 id=&quot;bulk-conversion--upload&quot;&gt;Bulk Conversion &amp;amp; Upload&lt;/h1&gt;

&lt;p&gt;I didn’t want to take the time to move over 80+ documents by hand. So
I wrote a script to help. I created a page in our confluence space for
imported docs. I set that as a parent page and then just converted all
of the Markdown to HTML and uploaded it in a single step. Linking to
the original document in case anything was lost in translation. Now we
can pull content and link to the old content, but start building out
newer documentation on our confluence space.&lt;/p&gt;

&lt;h1 id=&quot;show-me-the-code&quot;&gt;Show Me the Code&lt;/h1&gt;

&lt;p&gt;You can view the &lt;a href=&quot;https://gist.githubusercontent.com/danmayer/9629fa62e8e855472f5dc7dda1ac29bc/raw/fcfdcfbbede3675121b33855b0278a85b3bf0a71/markdown_to_confluence.rb&quot;&gt;gist&lt;/a&gt; or see the code below.&lt;/p&gt;

&lt;div class=&quot;language-ruby 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;#!/usr/bin/env ruby&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;rubygems&apos;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;confluence/api/client&apos;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;kramdown&apos;&lt;/span&gt;


&lt;span class=&quot;n&quot;&gt;username&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;your@email.com&apos;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;password&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;your_pass&apos;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;      &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;https://your_co.atlassian.net/wiki&apos;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;space&lt;/span&gt;    &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;DP&apos;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# if you don&apos;t want your files at the root give a parent page to nest them under&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;parent_page_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5564541&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;     &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;/Users/danmayer/projects/DevDocuments&apos;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;original_root_path&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;https://github.com/OffgridElectric/DevDocuments/blob/master&apos;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;###&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# see https://github.com/amishyn/confluence-api-client for usage&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;###&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Confluence&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Api&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;errors&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;files&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Dir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;glob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/**/*&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;converted&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;files&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;.md&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;converted&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;next&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;converted&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;relative_path&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;gsub&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;gsub&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;gsub&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;/&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;_&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;gsub&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;.md&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;content&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;content&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n\n\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; [original document](&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;original_root_path&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;relative_path&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;)&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;html_content&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Kramdown&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_html&lt;/span&gt;

    &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;creating &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;page&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:&quot;page&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                      &lt;span class=&quot;ss&quot;&gt;title: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                      &lt;span class=&quot;ss&quot;&gt;space: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;key: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;space&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
                      &lt;span class=&quot;nb&quot;&gt;ancestors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:[{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;id: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parent_page_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}],&lt;/span&gt;
                      &lt;span class=&quot;ss&quot;&gt;body: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;storage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;value: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;html_content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                      &lt;span class=&quot;ss&quot;&gt;representation: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;storage&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}})&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;errors&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;page&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;page&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;statusCode&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;converted &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;converted&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;errors:&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;inspect&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;done&apos;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Sharing Knowledge & Breaking Silos on Distributed Teams with Brown Bags</title>
   <link href="https://www.mayerdan.com/management/2017/08/24/sharing-knowledge-breaking-silos-on-distributed-teams-with-brown-bag"/>
   <updated>2017-08-24T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/management/2017/08/24/sharing-knowledge-breaking-silos-on-distributed-teams-with-brown-bag</id>
   <content type="html">
&lt;blockquote&gt;
  &lt;p&gt;Over the last 2 years or so our team has built up a collection of about 80 internal talks -credit &lt;a href=&quot;https://www.flickr.com/photos/arcticpenguin/3489763194&quot;&gt;arcticpenguin&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I have worked with distributed teams for years now. At Offgrid-Electric (OGE), our tech team is extremely distributed (across timezones, continents, and cultures). One thing that can always be a challenge is feeling like a part of the team. One thing that I have found useful both with team building, and to help with knowledge sharing through the team is brown bags. The term might take you back a bit, but the concept is easy enough. A less formal presentation, talk, or discussion generally with coffee, lunch, or a snack. Obviously, this can be done with co-located teams as well, but with a distributed team and a few best practices they can be very effective.&lt;/p&gt;

&lt;h2 id=&quot;brown-bags-at-oge&quot;&gt;Brown Bags at OGE&lt;/h2&gt;

&lt;p&gt;We don’t have too many formal rules, but we try to keep things humming along and have found some good ideals along the way.&lt;/p&gt;

&lt;p&gt;The most important is &lt;strong&gt;record your brown bags&lt;/strong&gt;, this unlocks a number of great things. If people can’t all make it across timezones they can catch up later. You can catch up on talks you missed while on vacation. Most importantly, you are building an &lt;strong&gt;amazing onboarding resource over time&lt;/strong&gt;. When folks start at OGE, I have a short standard list of brown bags I always recommend. Then I personalize other recommendations based on their interests and projects they will be getting involved with.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/brown_bag_two.png&quot; alt=&quot;Brown Bags&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;Diving Deep into our Call Center Automation with &lt;a href=&quot;https://medium.com/@dmitrytsepelev&quot;&gt;Dmitry Tsepelev&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As you build up a team video collection &lt;strong&gt;keep both the video recordings and the slides&lt;/strong&gt; somewhere accessible by the whole team. We have a shared google drive folder, and dump all the materials in there. It is getting to the point we might need categorized folders, or at least we should adopt a naming convention like &lt;strong&gt;2017–06–15-data-changes.mp4&lt;/strong&gt; to help highlight what is dated over time.&lt;/p&gt;

&lt;p&gt;Try to follow some scheduled cadence. Our loose schedule is to &lt;strong&gt;have a brown bag every two weeks&lt;/strong&gt;. While we don’t achieve this all the time, if there are meeting heavy weeks or behind schedule projects, we generally catch up and get back into the flow.&lt;/p&gt;

&lt;p&gt;To help keep the talks coming and inspire additional ideas create a &lt;strong&gt;brown bag signup board&lt;/strong&gt;. We have talks people want to give as well as talks people want to hear that folks can try to get other people to give. We have a simple shared Asana board for this, but any task management tool would do.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/brown_bag_three.png&quot; alt=&quot;Brown Bags&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;An early version of &lt;a href=&quot;https://medium.com/u/9f96e9614042?source=post_page-----fda5e2eda375----------------------&quot;&gt;Annyce Davis&lt;/a&gt; “&lt;a href=&quot;https://academy.realm.io/posts/360-andev-2017-annyce-davis-first-do-no-harm/&quot;&gt;First Do No Harm&lt;/a&gt;” talk given as a brown bag.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Another great thing to come out of our team brown bags over the years, has been helping people prep conference talks. If you work with new or experienced speakers &lt;strong&gt;encourage folks to practice their talks to the team&lt;/strong&gt; and get some initial feedback. It is a great way to figure out what is working and what isn’t before giving a talk to a bigger group and to help calm your nerves.&lt;/p&gt;

&lt;p&gt;Invite other folks, &lt;strong&gt;break the silos, and share cross-functional knowledge&lt;/strong&gt; that might be a bit outside of everyone’s day to day. Originally our talks were generally just the software team. Then occasionally with the hardware or data teams. Now we generally, depending on the topics, invite a larger group involving IT, Data, Software, Project Managers, Hardware, and Stakeholders.&lt;/p&gt;

&lt;p&gt;Make it fun, &lt;strong&gt;keep it loose&lt;/strong&gt;, talks don’t have to be long, be formal slide presentations, or even be about technology. We have occasionally paired a few short lightening talks into a single brown bag. Folks have talked about travel or just given accounts of their experiences while visiting our consumer markets in Africa.&lt;/p&gt;

&lt;h2 id=&quot;how-do-other-team-share-knowledge-and-break-silos&quot;&gt;How do other team share knowledge and break silos?&lt;/h2&gt;

&lt;p&gt;While this has worked for us well, obviously there are always ways to improve. What other ways do team’s use to share knowledge? What ways could we improve our brownbags even more? We would love to hear how it goes if your team starts up internal brown bags, let us know.&lt;/p&gt;

&lt;p&gt;Finally, to help get you started I can share some slides from one of our recent internal talks, &lt;a href=&quot;https://docs.google.com/presentation/d/1IjZATQlvL8hT7r3OUWPJpMAE10cQyWhJTK5l7Mpcgzw/edit?usp=sharing&quot;&gt;information architecture change process&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Information Architecture Changes</title>
   <link href="https://www.mayerdan.com/programming/2017/08/13/information-architecture-changes"/>
   <updated>2017-08-13T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/programming/2017/08/13/information-architecture-changes</id>
   <content type="html">
&lt;blockquote&gt;
  &lt;p&gt;Visualizing DB Schema Changes, even at a quick glance better structures stand out&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;information-architecture-changes&quot;&gt;Information Architecture Changes&lt;/h1&gt;

&lt;p&gt;Creating a robust Data Model for your business &amp;amp; application is one of the most important things to get right. We still don’t have great tools available for discussing proposed database schema changes. Fixing a bad data model after data has started flowing on a production system is more complicated &amp;amp; time consuming than folks estimate. A series of not fully thought out data decisions early on can cut a team’s velocity significantly. This is due to time being spent on trying to fix bad &amp;amp; invalid data, adding missing &lt;a href=&quot;http://naildrivin5.com/blog/2015/11/15/rails-validations-vs-postgres-check-constraints.html&quot;&gt;DB constraints &amp;amp; validations&lt;/a&gt;, and refactoring towards a more appropriate data model.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What can we do to ensure a more robust model from the start and increase the confidence we have in data model changes?&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;a-proposal-for-more-communicative-data-model-changes&quot;&gt;A Proposal For More Communicative Data Model Changes&lt;/h2&gt;

&lt;p&gt;Below, I will lay out a proposal we are iterating on as part of our OGE team best practices.&lt;/p&gt;

&lt;p&gt;Our Goals:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Ability to communicate anticipated impacts of our data model changes clearly across Development, Project Management, &amp;amp; Business Intelligence teams&lt;/li&gt;
  &lt;li&gt;Ability to quickly visualize changes (faster feedback loop)&lt;/li&gt;
  &lt;li&gt;Reduce bad data models making it to production&lt;/li&gt;
  &lt;li&gt;Support both interactive &amp;amp; asynchronous communication styles that work best for our distributed team&lt;/li&gt;
  &lt;li&gt;Ability to see high and low level details using the same data&lt;/li&gt;
  &lt;li&gt;Easily integrates into our tool chain
&lt;!--more--&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While this does add additional overhead while working on data changes to our system. It helps us to collaborate and socialize the data model. Working towards ubiquitous language across our teams and with our stakeholders.&lt;/p&gt;

&lt;p&gt;It is important to note this is for &lt;a href=&quot;https://www.mayerdan.com/programming/2016/11/21/managing-rails-migrations&quot;&gt;data schema changes not for data migrations&lt;/a&gt;, which I have written about how we handle in the past.&lt;/p&gt;

&lt;h2 id=&quot;data-model-change-process&quot;&gt;Data Model Change Process&lt;/h2&gt;

&lt;p&gt;The process creates some artifacts that help support a robust understanding and conversation around the data changes.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;A Pull Request (PR) is used to group the artifacts, description and reasoning for the change, and to focus the discussion&lt;/li&gt;
  &lt;li&gt;A high level visual asset on changes, to quickly understand the lay of the land, so to speak (including before and after images)&lt;/li&gt;
  &lt;li&gt;A low level view showing column level removals and additions to the schema, colorized via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git diff&lt;/code&gt; support&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;high-level-visual-assets&quot;&gt;High Level Visual Assets&lt;/h3&gt;

&lt;p&gt;The high level view are images that can include class or class and field information about the models under change. The PR will typically include both the before and after images so one can see how the classes and associations will change over time. If fields are moved you can also see a class shrinking as fields move to newly associated objects. This type of view can help see the bigger relationships and structures of the data model.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/customer_phone_clean.png&quot; alt=&quot;visual details customer cleanup&quot; width=&quot;100%&quot; /&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;A high detail visual representation of the expected end result&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;low-level-details&quot;&gt;Low Level Details&lt;/h3&gt;

&lt;p&gt;By building image models from a textual diagramming format (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DOT&lt;/code&gt; files), one can get much more details and control for the diagrams.&lt;/p&gt;

&lt;p&gt;The low level textual details will result in a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git diff&lt;/code&gt; easily viewed as part of the PR. It makes clear specific fields and associations that are added and removed. While this might be verbose for some folks, it is the level of details needed for a developer implementing and the business intelligence analyst reviewing. Seeing the specific fields makes it clear if the data will meet their needs. It can also be very helpful to discuss low level details such as field type.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/db_refactor_out_phone.png&quot; alt=&quot;low level details refactoring out phone number&quot; width=&quot;100%&quot; /&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;A textual diff showing low level details when refactoring out a phone number&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;example-live-refactoring-your-db-model&quot;&gt;Example Live Refactoring Your DB Model&lt;/h2&gt;

&lt;p&gt;It might be easier to understand the goal with a quick animation to show the flow, than just describing it. Here is the flow one can expect to get into when working on DB model / schema changes.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/refactor_db_loop.gif&quot; alt=&quot;image DB Model Change Animation&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;team-discussion-workflow&quot;&gt;Team Discussion Workflow&lt;/h2&gt;

&lt;p&gt;This flow allows for a quick feedback loop, where folks in a room or on a screen share meeting could quickly diagram a model together. Making quick and small edits to the textual representation and generating new versions to discuss. This flow requires some initial work by a developer to prep the original model and DOT files.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Discuss original model image&lt;/li&gt;
  &lt;li&gt;Edit DOT files during discussion&lt;/li&gt;
  &lt;li&gt;Generate new model image&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Loop until the group is satisfied with the results&lt;/p&gt;

&lt;h2 id=&quot;detailed-developer-workflow&quot;&gt;Detailed Developer Workflow&lt;/h2&gt;

&lt;p&gt;This should walk through the steps required to implement this process on your own project. This process could easily be implemented and integrated into various frameworks, but our specific details use tooling for Ruby on Rails to further simplify the efforts of generating these artifacts.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Generate or update initial model on dev branch
    &lt;ul&gt;
      &lt;li&gt;generate dot file: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FOCUS=true VERBOSE=true OUTPUT=doc/customers.dot rake diagram:models:customers&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;generate image: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;neato -Tpng doc/customers.dot &amp;gt; doc/customers.png&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;check in the un-edited DOT &amp;amp; image files&lt;/li&gt;
      &lt;li&gt;commit to dev branch&lt;/li&gt;
      &lt;li&gt;this will serve as your before to diff against later&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Create a feature branch to work on your changes
    &lt;ul&gt;
      &lt;li&gt;new branch: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git checkout -b feature/db_phone_change&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;edit to reflect your changes: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;open doc/customer.dot&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;convert updated DOT file to updated image: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;neato -Tpng doc/customer.dot &amp;gt; doc/customers.png&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;view PNG &amp;amp; refactor until happy&lt;/li&gt;
      &lt;li&gt;create a PR and upload the before and after images, diff will be automatically created for the DOT file changes.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;change-process-presentation&quot;&gt;Change Process Presentation&lt;/h2&gt;

&lt;p&gt;This is a version of the presentation I gave internally when getting
our team on board. Feel free to have a look
&lt;a href=&quot;https://www.mayerdan.com/ia_change/IA_Change_Process.pdf&quot;&gt;Information Architecture Change Process&lt;/a&gt;&lt;/p&gt;

&lt;iframe src=&quot;https://docs.google.com/presentation/d/1IjZATQlvL8hT7r3OUWPJpMAE10cQyWhJTK5l7Mpcgzw/embed?start=false&amp;amp;loop=false&amp;amp;delayms=5000&quot; frameborder=&quot;0&quot; width=&quot;960&quot; height=&quot;569&quot; allowfullscreen=&quot;true&quot; mozallowfullscreen=&quot;true&quot; webkitallowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;

&lt;h2 id=&quot;code--development-details&quot;&gt;Code &amp;amp; Development Details&lt;/h2&gt;

&lt;p&gt;See below for details on how to implement this yourself.&lt;/p&gt;

&lt;h3 id=&quot;dependencies&quot;&gt;Dependencies&lt;/h3&gt;

&lt;p&gt;There are many tools that could do this. If you don’t have the ability to automatically create diagrams from your code or access to graphviz, you could build out much of the diagrams by hand with a tool like &lt;a href=&quot;https://www.draw.io/&quot;&gt;draw.io&lt;/a&gt;. If you are using Rails, I recommend the below setup.&lt;/p&gt;

&lt;h3 id=&quot;add-to-your-gemfile-in-your-devtest-group&quot;&gt;Add to your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gemfile&lt;/code&gt; in your dev/test group&lt;/h3&gt;

&lt;p&gt;We use the &lt;a href=&quot;https://github.com/preston/railroady&quot;&gt;Railroady&lt;/a&gt; gem to generate our initial DOT files &amp;amp; images.&lt;/p&gt;

&lt;div class=&quot;language-ruby 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;n&quot;&gt;group&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:development&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;railroady&apos;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;install-graphviz-and-tools-if-you-dont-already-have-it&quot;&gt;Install &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;graphviz&lt;/code&gt; and tools if you don’t already have it&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brew install graphviz&lt;/code&gt;&lt;/p&gt;

&lt;h3 id=&quot;setup-some-rake-tasks-for-custom-focused-diagrams&quot;&gt;Setup some rake tasks for custom focused diagrams&lt;/h3&gt;

&lt;p&gt;By default &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Railroady&lt;/code&gt; has some great Model diagrams.&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 diagram:models:all
rake diagram:models:brief
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;They tend to be to verbose by default. You can easily customize the output to focus in on the models under discussion. Below is some quick code to highlight how to customize the output.&lt;/p&gt;

&lt;div class=&quot;language-ruby 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;n&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:diagram&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:models&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;excluded_models&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;PaperTrail::Version&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;EXCLUDE_MODELS&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;,&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;,&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;####&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;#&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Convert dot files to images&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# neato -Tpng doc/customer.dot &amp;gt; tmp/customers.png&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;#&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;####&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;generate_for_files&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;files&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;output_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;files&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;files&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;app/models/user.rb&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;WITH_USER&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;VERBOSE&apos;&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;s1&quot;&gt;&apos;--alphabetize&apos;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;--brief&apos;&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;output_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ends_with?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;dot&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;sb&quot;&gt;`EXCLUDED_MODELS=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;excluded_models&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt; railroady -M -s &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;files&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;,&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt; --show-belongs_to &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt; &amp;gt; &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;output_file&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;doc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;output_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;doc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;doc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;gsub&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;\l&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;output_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;w&apos;&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;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;doc&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;else&lt;/span&gt;
        &lt;span class=&quot;sb&quot;&gt;`EXCLUDED_MODELS=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;excluded_models&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt; railroady -M -s &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;files&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;,&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt; --show-belongs_to &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt; | neato -Tpng &amp;gt; &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;output_file&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
      &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;find the file in &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;output_file&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Generates an class diagram for all customer models.&apos;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:customers&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;output&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;OUTPUT&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;tmp/customers.png&apos;&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;customer_ignores&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;FOCUS&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
                           &lt;span class=&quot;sx&quot;&gt;%w(Tagging Tag Ticket Task)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;,&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                         &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
                           &lt;span class=&quot;s1&quot;&gt;&apos;&apos;&lt;/span&gt;
                         &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
      &lt;span class=&quot;no&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;EXCLUDE_MODELS&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;EXCLUDE_MODELS&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_s&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;customer_ignores&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;files&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;%w(
      app/models/customer.rb
      app/models/contact.rb
      app/models/lead.rb
      app/models/sms_notification.rb
      app/models/phone_block_list.rb
      app/models/call.rb
      )&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;generate_for_files&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;files&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;output&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Most Valuable Code</title>
   <link href="https://www.mayerdan.com/programming/2017/07/20/most-valuable-code"/>
   <updated>2017-07-20T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/programming/2017/07/20/most-valuable-code</id>
   <content type="html">
&lt;blockquote&gt;
  &lt;p&gt;Obama writing JS with &lt;a href=&quot;https://twitter.com/codeorg/status/541997370356269057?ref_src=twsrc%5Etfw&amp;amp;ref_url=http%3A%2F%2Fmashable.com%2F2014%2F12%2F09%2Fpresident-obama-code%2F&quot;&gt;code.org&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;the-value-of-code&quot;&gt;The Value of Code&lt;/h1&gt;

&lt;p&gt;While both of the below examples are obviously a bit exaggerated they do capture the point. The code we write has value because it solves some challenge.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The Best Code is No Code At All&lt;br /&gt;
-&lt;a href=&quot;https://blog.codinghorror.com/the-best-code-is-no-code-at-all/&quot;&gt;Jeff Atwood&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;or&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Programming is a means to an end, not an end in itself. You should be trying to do as little of it as possible to make the thing that you want.&lt;br /&gt;
-&lt;a href=&quot;http://www.niemanlab.org/2013/03/jake-levine-why-learning-to-code-isnt-as-important-as-learning-to-build-something/&quot;&gt;Jake Levine&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;most-valuable-code-ive-written&quot;&gt;Most Valuable Code I’ve Written&lt;/h1&gt;

&lt;p&gt;Given that I think the most valuable code I have currently ever
written is a stupid and simple method. This is a recreation of the
method below avoiding un-important internals.
&lt;!--more--&gt;&lt;/p&gt;

&lt;div class=&quot;language-ruby 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;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ensure_safe&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;begin&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;rescue&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exception&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;message_exception&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;ensure_safe rendered the page, saved exception: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;message_exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;set_backtrace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;backtrace&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;production?&lt;/span&gt;
      &lt;span class=&quot;no&quot;&gt;ExceptionReporter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;report&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;message_exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
      &lt;span class=&quot;no&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;******* Error *******&quot;&lt;/span&gt;
      &lt;span class=&quot;no&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;error safe render, on production this would report the error, but render nothing&quot;&lt;/span&gt;
      &lt;span class=&quot;no&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;error: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;message_exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;message_exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;message_exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;backtrace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exception&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&apos;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The above method when added to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;application_helper.rb&lt;/code&gt; could be called in view wrapping a render method.&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;&amp;lt;div&amp;gt;
 &amp;lt;%= ensure_safe { render &apos;show_results&apos;, results: data } %&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;When used on important pages, it would generally result in all the primary content being rendered. Then it would silently return an empty partial to the user, while reporting the exceptions up to our developer team. Often the bug was in a navigational, promotional, advertising, or upsell element. Many times the error would only occur in specific data combinations which is why it wasn’t caught in testing. It prevented many smaller error from ever taking down our primary content or purchase flow ever again, which was huge. In total this probably prevented hundreds of thousands of errors from ever reaching our end users eyes.&lt;/p&gt;

&lt;h1 id=&quot;stands-the-test-of-time&quot;&gt;Stands the Test of Time&lt;/h1&gt;

&lt;p&gt;My thoughts about this particular piece of code isn’t new. In face below is what I had to say about it when I moved on from LivingSocial a few years ago in a email farewell to fellow engineers.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;ensure_safe: I still consider this the most valuable piece of code I have ever written. Luckily, the era of frequent exceptions and downtime has passed, but back when we were pushing 12+ minute deploys and we didn’t have ls-deploy rollback when a bug went live we would often have 20+ minutes of purchase flow downtime. Knocking out deals#show or a purchase page would halt all revenue for about 20 minutes while we scrambled to revert and redeploy. The silly method would let some portions of the view safely explode while sending us exceptions but keeping the site running. It often headed off 10-30K exceptions per incident often with no users the wiser that an issue occurred. I wish there was a graphite stat counter on how many times that method saved the site from going down ;)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;what-piece-of-code-surprised-you&quot;&gt;What Piece of Code Surprised you?&lt;/h1&gt;

&lt;p&gt;What silly piece of code has surprised you over the years? Something that was perhaps thought of as a quick fix or temporary solution that has stood the test of time of proved so valuable that it has endured? I am sure other folks have some great examples of the method that has become a myth or perhaps infamous for it’s craziness.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Phone Number DB Types</title>
   <link href="https://www.mayerdan.com/programming/2017/06/26/db_phone_types"/>
   <updated>2017-06-26T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/programming/2017/06/26/db_phone_types</id>
   <content type="html">
&lt;p&gt;&lt;a href=&quot;https://twitter.com/danmayer/status/879033778169151488&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;tweeting about DB phone number formats&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;how-to-best-store--query-phone-numbers-in-postgres-db&quot;&gt;How to best store &amp;amp; query phone numbers in Postgres DB&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Update: 06-27-2017, See the update &amp;amp; round 2 section below for improved and corrected data&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After some frustration cleaning up some of our information architecture related to phone numbers. I posted the tweet at the top of this article.&lt;/p&gt;

&lt;p&gt;It actually lead to far more responses, questions, and discussion about the best way to store numbers… It made me think that perhaps our quick attempts to extract the best process out of multiple production table cleanups, could be missing something. I decided it would be pretty easy to try to dig in a bit deeper for a more data backed answer. I definitely don’t claim to be a DB performance expert, so perhaps we had reached some wrong conclusions.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://twitter.com/danmayer/status/879034663867748352&quot;&gt;&lt;img src=&quot;/assets/img/twitter_convo.png&quot; alt=&quot;image columns&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;a portion of the sub-discussion from the original tweet&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href=&quot;https://twitter.com/danmayer/status/879033778169151488&quot;&gt;&lt;img src=&quot;/assets/img/twitter_convo_always.png&quot; alt=&quot;image columns&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;a pretty strong statement saying we got this wrong 
&lt;!--more--&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;open-questions&quot;&gt;Open Questions&lt;/h2&gt;

&lt;p&gt;The premise we ended up with was that it was worth a tradeoff of some complexity to store phone numbers as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bigint&lt;/code&gt; vs &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;varchar&lt;/code&gt; in our Postgres DB.&lt;/p&gt;

&lt;p&gt;Everyone admits it is easier to work with a string number and it avoids the leading &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0&lt;/code&gt; problem. Is there a valid reason to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bigint&lt;/code&gt; vs &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;varchar&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;The thread brought up a few open questions:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;What is the difference in DB table storage size?&lt;/li&gt;
  &lt;li&gt;What is the performance difference
    &lt;ul&gt;
      &lt;li&gt;the difference on insertion time?&lt;/li&gt;
      &lt;li&gt;the difference on query time?&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;OK, we should be able to answer these fairly easily.&lt;/p&gt;

&lt;h2 id=&quot;benchmark-setup&quot;&gt;Benchmark Setup&lt;/h2&gt;

&lt;p&gt;Create Simple Tables with our unique index&lt;/p&gt;

&lt;div class=&quot;language-ruby 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;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DemoTables&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Migration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;5.1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;change&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;create_table&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:demo_int_phone_numbers&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bigint&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:phone_number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;limit: &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;null: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;add_index&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:demo_int_phone_numbers&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;ss&quot;&gt;:phone_number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;unique: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;create_table&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:demo_string_phone_numbers&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:phone_number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;limit: &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;null: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;add_index&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:demo_string_phone_numbers&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;ss&quot;&gt;:phone_number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;unique: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Create our simple AR models&lt;/p&gt;

&lt;div class=&quot;language-ruby 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;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DemoIntPhoneNumber&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DemoStringPhoneNumber&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Build a simple benchmark script&lt;/p&gt;

&lt;div class=&quot;language-ruby 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;n&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:demo&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:perf&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:environment&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;DemoStringPhoneNumber&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;destroy_all&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;DemoIntPhoneNumber&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;destroy_all&lt;/span&gt;

    &lt;span class=&quot;no&quot;&gt;GC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;start&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;benchmarking insertions&quot;&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Benchmark&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bm&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;bm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;report&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;200_000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
          &lt;span class=&quot;no&quot;&gt;DemoStringPhoneNumber&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;phone_number: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;no&quot;&gt;GC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;start&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Benchmark&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bm&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;bm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;report&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;200_000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
          &lt;span class=&quot;no&quot;&gt;DemoIntPhoneNumber&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;phone_number: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;no&quot;&gt;GC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;start&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;benchmarking queries&quot;&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Benchmark&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bm&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;bm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;report&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;200_000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
          &lt;span class=&quot;no&quot;&gt;DemoStringPhoneNumber&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;phone_number: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;first&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;no&quot;&gt;GC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;start&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Benchmark&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bm&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;bm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;report&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1200_000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
          &lt;span class=&quot;no&quot;&gt;DemoIntPhoneNumber&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;phone_number: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;first&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;table-size-results&quot;&gt;Table Size Results&lt;/h2&gt;

&lt;p&gt;I was wrong on table size… Looking back as we moved phone numbers from many tables to a single &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;phone_numbers&lt;/code&gt; table a number of them were using different types, including &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;varchar&lt;/code&gt;, which did make the tables bigger… but that was because a number of the old tables hadn’t set a limit meaning it defaulted to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;varchar(255)&lt;/code&gt;. Once you set a limit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bigint&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;string&lt;/code&gt; with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;limit: 8&lt;/code&gt; will result in the same size of table.&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;select pg_size_pretty(pg_relation_size(&apos;demo_int_phone_numbers&apos;));
=&amp;gt; 8656 kB

select pg_size_pretty(pg_relation_size(&apos;demo_string_phone_numbers&apos;));
=&amp;gt; 8656 kB
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;insertion-performance-results&quot;&gt;Insertion Performance Results&lt;/h2&gt;

&lt;p&gt;I actually didn’t expect a difference on this but someone mentioned there could be a slower insertion time… It looks like nothing noticeable on insertion which is nice.&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;# benchmarking insertions
              user     system      total        real
        385.130000  35.700000 420.830000 (568.695180)
              user     system      total        real
        371.370000  34.110000 405.480000 (543.861259)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;query-performance-results&quot;&gt;Query Performance Results&lt;/h2&gt;

&lt;p&gt;While numerous performance blog posts say that a Postgres &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;btree&lt;/code&gt; index is more performant for integers than strings. Sample data doesn’t show as big of a difference as I was expecting. The performance benefit is questionable. It is there, but doesn’t seem worth the effort. Since this is a quick and dirty benchmark one would have to be more systematic to prove out a real difference. I think my simplification of just having sequential numbers inserted could not realistically simulate how a production index ends up.&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;# 200,000 queries against String Table
              user     system      total        real
        194.320000  15.400000 209.720000 (265.271056)
        
# 200,000 queries against Int Table        
              user     system      total        real
        160.390000  13.010000 173.400000 (210.774260)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In that end this does show a small difference, but nothing to be impressed about.&lt;/p&gt;

&lt;h2 id=&quot;bad-assumptions&quot;&gt;Bad Assumptions&lt;/h2&gt;

&lt;p&gt;From our cleanup of multiple tables we had multiple issues and conditions which lead me to conflate some of the issues.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Our DB had a number of columns that were &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;varchar(255)&lt;/code&gt; opposed to using a correct limit. This entirely counts for the table size difference.&lt;/li&gt;
  &lt;li&gt;I am curious if having unused space in a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;varchar&lt;/code&gt; field can still impact query performance, I am not sure about this.&lt;/li&gt;
  &lt;li&gt;We weren’t enforcing a unique index, instead sometimes relying on Rails &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;validates :field_name uniqueness: true&lt;/code&gt;, and sometimes not even enforcing uniqueness. Which I think could impact query time more significantly when there are duplicates. Proving this out would require more benchmarking.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;conclusions&quot;&gt;Conclusions&lt;/h2&gt;

&lt;p&gt;Let’s revisit our questions:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Is there a valid reason to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bigint&lt;/code&gt; vs &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;varchar&lt;/code&gt;?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Not really, there may be slight query performance wins, but far less than I thought.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;What is the difference in DB table storage size?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;None, I was wrong&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;What is the performance difference on insertion time?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;None, which I expected but some other folks expected to see.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;What is the performance difference on query time?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Slight difference, more detailed benchmarking would be good… Initial results show this to be far less significant than initially thought. I will say this one is dependent on the performance characteristics you see in production. That being said my assumptions here were still a bit flawed, and I was surprised by the results.&lt;/p&gt;

&lt;h2 id=&quot;update-limit-not-what-you-think&quot;&gt;Update: Limit Not What You Think…&lt;/h2&gt;

&lt;p&gt;OK so this post made sense to folks, but then people were asking why I am limiting the phone number to 8, which wouldn’t work in various countries… in fact it wouldn’t work for our countries either… I will need to rerun my benchmarks and I believe get different results.&lt;/p&gt;

&lt;p&gt;We have historically put limit in our int fields when doing something other than the default. The limit has a different meaning for numerical and string types for Rails &lt;a href=&quot;https://moeffju.net/blog/using-bigint-columns-in-rails-migrations&quot;&gt;DB migrations&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:limit&lt;/code&gt; on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bigint&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;int&lt;/code&gt; in &lt;a href=&quot;https://www.devroom.io/2009/08/20/once-and-for-all-rails-migrations-integer-limit-option/&quot;&gt;Rails refers to the byte size not a length limit&lt;/a&gt;. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bigint limit: 8&lt;/code&gt; is redundant as 8 bytes is the max. Prior to Rails supporting the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bigint&lt;/code&gt; type, the way to get a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bigint&lt;/code&gt; was to actually use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;int limit: 8&lt;/code&gt;. In fact if you look at the schema after creating a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bigint limit: 6&lt;/code&gt; the limit is just ignored by postgres (and by mysql it seems). The DB storage type ends up as a bigint with 8 bytes of storage. This post explains the &lt;a href=&quot;https://makandracards.com/makandra/15027-how-to-create-columns-like-bigint-or-longtext-in-rails-migrations-and-what-limit-means-for-column-migrations&quot;&gt;oddness of int limits&lt;/a&gt; well.&lt;/p&gt;

&lt;p&gt;Limit on string dos what you expect and limits the length of the string. The way I was generating phone numbers never tested this as I simply generated between &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(0...200_000)&lt;/code&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;# storing a typical TZ number for us
DemoIntPhoneNumber.create!(phone_number: 2551234567890) 

# storing an actual max, which is far larger than we need
DemoIntPhoneNumber.create!(phone_number: 9223372036854775807)

# lets try those with our string limit version
DemoStringPhoneNumber.create!(phone_number: 2550677939180)
ActiveRecord::ValueTooLong: PG::StringDataRightTruncation: ERROR:  value too long for type character varying(8)

# and clearly the max won&apos;t work at all...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;My benchmark isn’t valid as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;varchar(8)&lt;/code&gt; isn’t really the same as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bigint&lt;/code&gt;. The fact that tables size was previously the same was a bad coincidence, which I think shows that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bigint&lt;/code&gt; is actually more efficient.&lt;/p&gt;

&lt;p&gt;Let’s try again, varchar actually needs to hold 13 for our max length number, and 15 for German numbers. So we will compare &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bigint&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;varchar(15)&lt;/code&gt;, even though &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bigint&lt;/code&gt; can technically hold numbers of length 19.&lt;/p&gt;

&lt;h2 id=&quot;benchmarking-round-2&quot;&gt;Benchmarking Round 2&lt;/h2&gt;

&lt;p&gt;Setup Changes&lt;/p&gt;

&lt;p&gt;Let’s create two new tables the first verifies with the type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bigint&lt;/code&gt; that the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;limit&lt;/code&gt; option is ignored it just creates a table with an 8 byte &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bigint&lt;/code&gt; Postgres type. The second creates a new string table with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;varchar(15)&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-ruby 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;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DemoTablesTwo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Migration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;5.1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;change&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;create_table&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:demo_smallint_phone_numbers&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bigint&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:phone_number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;limit: &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;null: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;add_index&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:demo_smallint_phone_numbers&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;ss&quot;&gt;:phone_number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;unique: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;create_table&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:demo_bigstring_phone_numbers&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:phone_number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;limit: &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;null: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;add_index&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:demo_bigstring_phone_numbers&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;ss&quot;&gt;:phone_number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;unique: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;OK then we will need a quick AR class&lt;/p&gt;

&lt;div class=&quot;language-ruby 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;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DemoBigstringPhoneNumber&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then the benchmark improvements&lt;/p&gt;

&lt;div class=&quot;language-ruby 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;n&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:demo&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:perf&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:environment&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;DemoBigstringPhoneNumber&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;delete_all&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;DemoIntPhoneNumber&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;delete_all&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;base_number&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2550000000000&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;max_number&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;base_number&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;150_000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;no&quot;&gt;GC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;start&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;benchmarking insertions&quot;&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Benchmark&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bm&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;bm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;report&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;base_number&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
          &lt;span class=&quot;no&quot;&gt;DemoBigstringPhoneNumber&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;phone_number: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;no&quot;&gt;GC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;start&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Benchmark&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bm&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;bm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;report&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;base_number&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
          &lt;span class=&quot;no&quot;&gt;DemoIntPhoneNumber&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;phone_number: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# lets not just have sequential numbers lets get some better data by inserting arbitrary numbers&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;75_000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;times&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;max_number&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;rand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;90_000_000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;begin&lt;/span&gt;
        &lt;span class=&quot;no&quot;&gt;DemoBigstringPhoneNumber&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;phone_number: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;no&quot;&gt;DemoIntPhoneNumber&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;phone_number: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;rescue&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# ignore duplicates that occur because of rand&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;no&quot;&gt;GC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;start&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;benchmarking queries&quot;&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Benchmark&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bm&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;bm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;report&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
        &lt;span class=&quot;no&quot;&gt;DemoBigstringPhoneNumber&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;find_each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
          &lt;span class=&quot;no&quot;&gt;DemoBigstringPhoneNumber&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;phone_number: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;phone_number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;first&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;no&quot;&gt;GC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;start&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Benchmark&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bm&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;bm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;report&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
        &lt;span class=&quot;no&quot;&gt;DemoIntPhoneNumber&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;find_each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
          &lt;span class=&quot;no&quot;&gt;DemoIntPhoneNumber&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;phone_number: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;phone_number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;first&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;table-size-results-1&quot;&gt;Table Size Results&lt;/h2&gt;

&lt;p&gt;After running a number of tests, the tables size issue does become more clear. This now matches my original expectation.&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;select count(*) from demo_int_phone_numbers; =&amp;gt; 339669
select pg_size_pretty(pg_relation_size(&apos;demo_int_phone_numbers&apos;));
=&amp;gt; 14 MB

select count(*) from demo_bigstring_phone_numbers; =&amp;gt; 339669
select pg_size_pretty(pg_relation_size(&apos;demo_bigstring_phone_numbers&apos;));
=&amp;gt; 17 MB
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;insertion-performance-results-1&quot;&gt;Insertion Performance Results&lt;/h2&gt;

&lt;p&gt;Again no real difference.&lt;/p&gt;

&lt;h2 id=&quot;query-performance-results-1&quot;&gt;Query Performance Results&lt;/h2&gt;

&lt;p&gt;Results are are starting to show a bit more more of a difference. Likely, more valid as this time we aren’t just querying sequential numbers but we have some randomness introduced.&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;# queries against String Table varchar(15)
              user     system      total        real
        377.940000  28.550000 406.490000 (550.554212)
        
# queries against Int Table        
              user     system      total        real
        302.260000  23.320000 325.580000 (403.738332)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;revisited-conclusions&quot;&gt;Revisited Conclusions&lt;/h2&gt;

&lt;p&gt;Let’s revisit our questions:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Is there a valid reason to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bigint&lt;/code&gt; vs &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;varchar&lt;/code&gt;?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Perhaps, the query improvements are smaller than I thought, but noticeable. The table size difference is real, but also not that large. It would really depend on your needs and the size of your tables. In the end &lt;a href=&quot;https://github.com/googlei18n/libphonenumber/blob/master/FALSEHOODS.md&quot;&gt;Google says never store phone numbers as numeric data&lt;/a&gt; (rule #24), but given a known set of countries and query performance, you can examine the tradeoffs.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;What is the difference in DB table storage size?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Yes, there is a difference. The type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bigint&lt;/code&gt; is a more efficient way to store 15 digit numbers.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;What is the performance difference on insertion time?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;None, which I expected but some other folks expected to see.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;What is the performance difference on query time?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The larger set of test data and introducing randomness into the data seems to show an increasing improvement for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bigint&lt;/code&gt; queries. While it is still small than I anticipated, it does seem to have a real impact.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Unused DB Columns, via logs</title>
   <link href="https://www.mayerdan.com/programming/2017/06/20/unused-DB-columns-two"/>
   <updated>2017-06-20T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/programming/2017/06/20/unused-DB-columns-two</id>
   <content type="html">
&lt;blockquote&gt;
  &lt;p&gt;a search for ‘unused columns’ brings up this image from Flickr user &lt;a href=&quot;https://www.flickr.com/photos/82134796@N03/32824004562&quot;&gt;@denisbin&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;detecting-db-column-usage&quot;&gt;Detecting DB Column Usage&lt;/h1&gt;

&lt;p&gt;I have always been interested in ways to automate &amp;amp; &lt;a href=&quot;https://github.com/danmayer/coverband&quot;&gt;cleanup old unused code&lt;/a&gt;. Removing the clutter helps focus in on the things that matter and make clear patterns emerge.&lt;/p&gt;

&lt;p&gt;This post continuing on some database cleanup work. The first post of the series is, &lt;a href=&quot;https://www.mayerdan.com/programming/2017/02/08/cleanup-your-db&quot;&gt;cleanup unused tables in your database&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After taking on full tales, I wanted a way to find unused fields. I came up with two easy ways to try to automate detecting this in a Rails app.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Previously covered: Query the &lt;a href=&quot;(https://www.mayerdan.com/programming/2017/05/22/unused-DB-columns)&quot;&gt;DB’s paper_trail history&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;In this post: We will cover query logging and searching field usage via a log aggregator.
&lt;!--more--&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;db-field-usage-output&quot;&gt;DB Field Usage Output&lt;/h2&gt;

&lt;p&gt;The output depends on your log search tool. We currently use &lt;a href=&quot;http://sumologic.com&quot;&gt;Sumologic&lt;/a&gt; for our log aggregation so all the examples will focus on Sumologic. That being said it would be simple do to this in Splunk and other log aggregation tools.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/sumologic_db_fields.png&quot; alt=&quot;image columns&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;aggregated-search-log&quot;&gt;Aggregated Search Log&lt;/h2&gt;

&lt;p&gt;If you are running a system that is on more than a single server you should setup aggregated log search. &lt;a href=&quot;https://github.com/magnhaug/blogposts/blob/master/distributed-logging.md&quot;&gt;Distributed Logging&lt;/a&gt; is basically a requirement for micro-services, and honestly the moment your logs aren’t on a single server.&lt;/p&gt;

&lt;p&gt;In Rails to extract column usage out of your logs you need to enable &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActiveRecord&lt;/code&gt; logging on the application you are targeting. In general this isn’t enabled on production, but you can enable it for a period of time to query later. There can be a slight perf hit for logging query details, but more likely it just increases you log volume, as the perf isn’t very noticeable.&lt;/p&gt;

&lt;p&gt;Ways to enable logging:&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;# enable debug on production add to environments/production.rb
config.log_level = :debug

# debug just for ActiveRecord
ActiveRecord::Base.logger.level = :debug
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;activerecord-log-format&quot;&gt;ActiveRecord Log Format&lt;/h2&gt;

&lt;p&gt;The specifics of your log header, ender, single line logging, etc… The specifics shouldn’t matter. If you turn on default &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActiveRecord&lt;/code&gt; logs you should have something like below. If that is making it to your log querying tool you should be good to go.&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;TS=2017-06-20T14:46:34.881881 PID=00063 USER=000000 LEVEL=I |   [1m[35mSQL (1.6ms)[0m  [1m[32mINSERT INTO &quot;tracks&quot; (&quot;latitude&quot;, &quot;longitude&quot;, &quot;accuracy&quot;, &quot;action&quot;, &quot;action_id&quot;, &quot;timestamp&quot;, &quot;battery_percentage&quot;, &quot;used_memory&quot;, &quot;max_memory&quot;, &quot;created_at&quot;, &quot;updated_at&quot;, &quot;gps_enabled&quot;) VALUES ($1, $2, $3, $4, $5, $6, $7, ...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Verify you are seeing that in your log collector and then it is as simple as crafting a good query.&lt;/p&gt;

&lt;h2 id=&quot;db-field-log-search&quot;&gt;DB Field Log Search&lt;/h2&gt;

&lt;p&gt;This will be a bit different for any tool, but basically you will be looking to extract the table and all the fields into your tools parsed fields. At that point you can run a query over whatever time period you want to see the count or last timestamp of a given table / field usage.&lt;/p&gt;

&lt;p&gt;Find all table / fields updated along with the number of updates in the last search period.&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;_sourcename=*app.log *insert into |
 parse &quot;INSERT INTO \&quot;*\&quot; (*)&quot; as table, table_fields |
 count(table_fields) by table, table_fields&quot; |
 sort by _count
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Find all field usage where the field matches the world &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ability&lt;/code&gt;… Just a simple example of narrowing in on fields you think are likely dead.&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;_sourcename=*app.log *insert into |
 parse &quot;INSERT INTO \&quot;*\&quot; (*)&quot; as table, table_fields |
 count(table_fields) by table, table_fields |
 sort by _count |
 where table_fields matches &quot;*ability*&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;find-all-fields-with-0-results&quot;&gt;Find all fields with 0 results&lt;/h2&gt;

&lt;p&gt;This is an exercise for the reader. As it is simple to do human queries on fields you already suspect. If you want to detect anything unused without a good human guess you will need to automate your querying. Basically you will need to implement API access to your log query tool, then iterate through each table and field querying for the last usage, over whatever time period you care about I didn’t do this exercise as &lt;a href=&quot;https://www.mayerdan.com/programming/2017/05/22/unused-DB-columns&quot;&gt;using papertrail to detect unused DB columns&lt;/a&gt; worked well enough for me.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Paternity Leave</title>
   <link href="https://www.mayerdan.com/tips/2017/06/15/paternity-leave"/>
   <updated>2017-06-15T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/tips/2017/06/15/paternity-leave</id>
   <content type="html">
&lt;p&gt;I am on paternity leave, actually my second of 2 staggered Paternity leaves. I am about 1 week into my second leave, hanging out with little Theo. I have some time off to bond with my kid, but more importantly my wife has some time to go back to work and stay focused knowing, I’ve got this.&lt;/p&gt;

&lt;h1 id=&quot;mens-paternity-promotes-gender-equity&quot;&gt;Men’s Paternity Promotes Gender Equity&lt;/h1&gt;

&lt;p&gt;My wife started sharing a bunch of information about paternity leave when we started discussing having a child. The choices men make to be more involved with the kid can help women who choose to work to avoid some of the associated career setbacks that often come with becoming a mother. There is some interesting research going on here, it seems to impact both work and home life. Increasing men’s participation in chores and playing with children. I suspect that has something to do with the empathy gained, spending time at home alone with the kiddo.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/outside.jpg&quot; alt=&quot;image detect&quot; /&gt;
&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;Gotta make that dollar…&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;A mother’s future earnings increased 7% for every month that her partner took parental leave. &lt;br /&gt;
– &lt;a href=&quot;https://www.fastcompany.com/3042796/could-paternity-leave-polices-close-the-wage-gap&quot;&gt;Could Paternity Leave Policies Close The Wage Gap?&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;USA, get it together ;)&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;79: Number of countries that have laws requiring paternity leave&lt;br /&gt;
– &lt;a href=&quot;https://www.thebump.com/a/paternity-leave-for-men&quot;&gt;Paternity Leave By The Numbers&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href=&quot;http://money.cnn.com/2014/10/20/news/companies/change-org-maternity-paternity-leave/index.html&quot;&gt;Tech industry is leading the way (change.org most generous leave in tech)&lt;/a&gt;, keep pushing it forward.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;ul&gt;
    &lt;li&gt;this year alone, Amazon, Netflix, and Microsoft have all announced extended parental leave. This is of particular importance because for the first time, we’re seeing a move towards expanded leave for fathers as well as for mothers.&lt;/li&gt;
    &lt;li&gt;found that men increased their time spent on household chores by 23% after taking paternity leave.&lt;/li&gt;
    &lt;li&gt;Another study by the Columbia University School of Social Work found that men who took more than two weeks of leave were more likely to handle childcare tasks like diapering, feeding, and playing, and were more likely to contribute to household chores. &lt;br /&gt;
– &lt;a href=&quot;http://harvardkennedyschoolreview.com/women-benefit-most-when-men-take-paternity-leave/&quot;&gt;Women Benefit Most When Men Take Paternity Leave&lt;/a&gt;&lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;behavior-modification, how scientific ;)&lt;/p&gt;

&lt;blockquote&gt;
  &lt;ul&gt;
    &lt;li&gt;a behavior-modification tool that has been shown to boost male participation in the household, enhance female participation in the labor force, and promote gender equity in both domains.&lt;/li&gt;
    &lt;li&gt;Among labor economists, overly long maternity leaves are now recognized as creating a barrier to pay equity. At home, meanwhile, long leaves result in women doing most of the housework and child care. (referencing that it is better to have some leave for mom and dad opposed to long mom only leave)&lt;/li&gt;
    &lt;li&gt;policy makers decided to make men an offer they would feel ashamed to refuse&lt;/li&gt;
    &lt;li&gt;study suggests that paternity leave might give men a new mind-set, prompting them to trade more money for more time at home, more flexibility, or both.&lt;br /&gt;
– &lt;a href=&quot;https://www.theatlantic.com/magazine/archive/2014/01/the-daddy-track/355746/&quot;&gt;Daddy Track: The Case for Paternity Leave&lt;/a&gt;&lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;its-good-for-for-father--child-relationships&quot;&gt;It’s good for for Father / Child Relationships&lt;/h1&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/baby_tummy_time.jpg&quot; alt=&quot;image detect&quot; /&gt;&lt;/p&gt;

&lt;p&gt;It helps you bond with your baby, and it has been showm to increase the interest and time a father spends with his kids through out life. Ever hear about men jealous of the relationship between kids and their mothers? It is easy to see why kids could be closer to their moms, but taking paternity leave helps you to develop your own bond.&lt;/p&gt;

&lt;h1 id=&quot;daddy-paternity-leave-activities&quot;&gt;Daddy Paternity Leave Activities&lt;/h1&gt;

&lt;p&gt;What do we spend our days doing?&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Bi-lingual song &amp;amp; story time at the library&lt;/li&gt;
  &lt;li&gt;Visiting Daycares trying to get a spot locked down (the good times can’t go on forever).&lt;/li&gt;
  &lt;li&gt;Long walks to parks&lt;/li&gt;
  &lt;li&gt;Running errands, Theo enjoys looking around shops&lt;/li&gt;
  &lt;li&gt;Gardening, while I haven’t yet got him to pull weeds, he does seem to love watching me do it.&lt;/li&gt;
  &lt;li&gt;Baby Dance party&lt;/li&gt;
  &lt;li&gt;Crying and refusing to calm down (OK this part isn’t very fun)&lt;/li&gt;
  &lt;li&gt;Teaching the kid about Operation Ivy, and other Punk Rock.&lt;/li&gt;
  &lt;li&gt;Writing code and blog posts, the kid has to sleep sometime.&lt;/li&gt;
  &lt;li&gt;Visit a museum, kiddo loves being carried around to look at new things&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/dad_luggage.jpg&quot; alt=&quot;image detect&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;paternity-leave-stigma&quot;&gt;Paternity Leave Stigma&lt;/h1&gt;

&lt;blockquote&gt;
  &lt;p&gt;96 percent of men are back at work after two weeks, while 13 percent do not take a single day off.&lt;br /&gt;
–&lt;a href=&quot;http://www.chicagotribune.com/lifestyles/parenting/ct-stigma-of-paternity-leave-20170320-story.html&quot;&gt;Boston College Research&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;While there still is some stigma and even well intentioned folks unintentionally may throw a few jokes your way. It is getting better, and the best way to help keep the progress going is normalize dad’s taking of as much time as mom’s do.&lt;/p&gt;

&lt;h3 id=&quot;i-say-be-the-change-you-want-to-see&quot;&gt;I say, “Be the change, you want to see.”&lt;/h3&gt;

&lt;p&gt;I can tell you people are extremely nice to me, when I am out as a dad ‘alone’ with out baby. Random folks hold doors, elevators, make nice comments, etc. I am not even kidding that earlier today an old woman pulled her car over to the side of the road and shouted out the open window, “It is so nice to see dad’s taking their kids for a walk too.” So while it is shifting this way, it is still rare enough to see a man caring for a baby that folks are surprised to see it.&lt;/p&gt;

&lt;p&gt;So if you can again, take some time, Share the “burden.” Enjoy some time with your kid, while they like you.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.nytimes.com/2014/11/09/upshot/paternity-leave-the-rewards-and-the-remaining-stigma.html&quot;&gt;NYT’s Paternity Leave: The Rewards and the Remaining Stigma&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.law.com/sites/almstaff/2017/05/30/paternity-leave-stigma-at-law-firms-lifting-ever-so-slowly/?slreturn=20170514164651&quot;&gt;Paternity-Leave Stigma at Law Firms Lifting, Ever So Slowly&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.quora.com/Is-there-any-stigma-men-feel-taking-paternity-leave&quot;&gt;Quara: Is there any stigma men feel taking paternity leave?&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.chicagotribune.com/lifestyles/parenting/ct-stigma-of-paternity-leave-20170320-story.html&quot;&gt;http://www.chicagotribune.com/lifestyles/parenting/ct-stigma-of-paternity-leave-20170320-story.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;dude-check-your-privilege&quot;&gt;Dude, Check Your Privilege!&lt;/h1&gt;

&lt;p&gt;Yep, I hear that.&lt;/p&gt;

&lt;p&gt;Taking maternity or paternity leave in the US at all means you are pretty privileged. Only &lt;a href=&quot;http://www.techrepublic.com/article/10-things-you-need-to-know-about-maternity-leave-in-the-us/&quot;&gt;12% of Americans have access to paid parental leave&lt;/a&gt; and &lt;a href=&quot;http://www.techrepublic.com/article/10-things-you-need-to-know-about-maternity-leave-in-the-us/&quot;&gt;the US is one of only three countries left in the world that do not guarantee paid maternity leave&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As with most of my posts and life, I understand my ability to take leave, work from home, and live with reduced income from my wife and I taking leave is lucky. As is the flexibility of my career / current job to support staggered paternity leave (split between baby’s birth and Mom returning to work). As are many other things, the key point is if Paternity leave is available and viable to you as a mother or father, take it.&lt;/p&gt;

&lt;p&gt;Also, it is fine if a Mom chooses not go back to work, a father taking paternity leave is still a good thing that lets you bond with your child and promotes all people’s choices.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/baby_sit.jpg&quot; alt=&quot;image detect&quot; /&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Unused DB Columns</title>
   <link href="https://www.mayerdan.com/programming/2017/05/22/unused-DB-columns"/>
   <updated>2017-05-22T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/programming/2017/05/22/unused-DB-columns</id>
   <content type="html">
&lt;blockquote&gt;
  &lt;p&gt;a search for ‘unused database’ brings up this awesome &lt;a href=&quot;https://upload.wikimedia.org/wikipedia/commons/3/3c/Polar_bear_%28Ursus_maritimus%29_in_the_drift_ice_region_north_of_Svalbard.jpg&quot;&gt;wikimedia&lt;/a&gt; image for some reason.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;detecting-db-column-usage&quot;&gt;Detecting DB Column Usage&lt;/h1&gt;

&lt;p&gt;I am always interested in ways to automate and &lt;a href=&quot;https://github.com/danmayer/coverband&quot;&gt;cleanup old unused code&lt;/a&gt;. Removing the clutter helps focus in on the things that matter and make more clear patterns emerge. Continuing on some work to &lt;a href=&quot;https://www.mayerdan.com/programming/2017/02/08/cleanup-your-db&quot;&gt;cleanup unused tables in a database&lt;/a&gt;, I really wanted a way to find unused columns / fields. I came up with two easy ways to try to automate detecting this in a Rails app. The first is to query the DB’s &lt;a href=&quot;https://github.com/airblade/paper_trail&quot;&gt;paper_trail&lt;/a&gt; history, which is easy if you are already using paper_trail. The second I will cover in another post soon.&lt;/p&gt;

&lt;h2 id=&quot;column-usage-output&quot;&gt;Column Usage Output&lt;/h2&gt;

&lt;p&gt;What does column usage look like, well for each table it is a list of fields and the last date it was modified. The simple output should be pretty clear.
&lt;!--more--&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;____________________________________________________________
field usage for Customer
************************************************************
barcode: 2017-05-20
email: No updates in 3 months
fixed_location_id: No updates in 3 months
agent_id: No updates in 3 months
account_id: 2017-05-20
house_latitude: No updates in 3 months
house_longitude: No updates in 3 months
house_location_accuracy: No updates in 3 months
...
notes: 2017-05-20
last_paid_at: 2017-05-20
____________________________________________________________
field usage for Lead
************************************************************
fixed_location_id: No updates in 3 months
custom_location_name: No updates in 3 months
agent_id: 2017-05-20
captured_by_id: 2017-05-20
converted_at: 2017-05-20
service_level_id: No updates in 3 months
source: 2017-05-20
latitude: 2017-05-20
longitude: 2017-05-20
status: 2017-05-20
assigned_at: No updates in 3 months
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Obviously, a developer will need to interpret this, some tables aren’t often updated it doesn’t mean they have no purpose. For example we have some configuration tables that are only modified when we integrate new partners into our system. So they will show up in my list with “No updates in 3 months” but that can be quickly filtered out.&lt;/p&gt;

&lt;h2 id=&quot;detect-columns-via-paper_trail&quot;&gt;Detect Columns via Paper_Trail&lt;/h2&gt;

&lt;p&gt;If you use the gem &lt;a href=&quot;https://github.com/airblade/paper_trail&quot;&gt;paper_trail&lt;/a&gt; to track the history of changes on some of your models, you are already recording when any column was last changed. All one has to do is come up with the queries to pull the data out. Doing this by hand would be a huge pain, but luckily it is easy to hook into our code and automate the process. We just find all models that support paper_trail, then iterate through the fields.&lt;/p&gt;

&lt;h2 id=&quot;show-me-the-code&quot;&gt;Show Me the Code&lt;/h2&gt;

&lt;p&gt;The below functions and tasks can be added to any rake task file.&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;def find_ar_models
  Rails.application.eager_load!

  array = ActiveRecord::Base.descendants.collect { |x| x.to_s if x.table_exists? }.compact - [&apos;ActiveRecord::SchemaMigration&apos;]

  array.reject { |x| x.split(&apos;::&apos;).last.split(&apos;_&apos;).first == &quot;HABTM&quot; }
end

desc &apos;List when column was last updated&apos;
task :list_recent_columns =&amp;gt; :environment do
  ignore_models = %w(RailsSettings::Settings Devise::Oauth::Client)
  models = find_ar_models
  models = models.reject{|model| ignore_models.include?(model.to_s) }
  
  ignored_columns = %(id created_at updated_at)
  results = {}
  
  models.each do |model|
    model_results = {}
    model = Kernel.const_get model
    next unless model.paper_trail.enabled?
    puts &quot;_&quot;*60
    puts &quot;field usage for #{model}&quot;
    puts &quot;*&quot;*60
    model.attribute_names.each do |attr|
      next if ignored_columns.include?(attr)
      result = PaperTrail::Version.where(item_type: model.to_s)
                 .where(&quot;created_at &amp;gt;= ?&quot;,[3.months.ago])
                 .where(&quot;object_changes ILIKE ?&quot;,[&quot;%#{attr}%&quot;])
                 .order(&quot;created_at DESC&quot;)
                 .limit(1).first
      model_results[attr] = result&amp;amp;.created_at&amp;amp;.to_date || &apos;No updates in 3 months&apos;
      puts &quot;#{attr}: #{model_results[attr]}&quot;
    end
    results[model.to_s] = model_results
  end
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;quick-note-on-performance&quot;&gt;Quick Note on Performance&lt;/h3&gt;

&lt;p&gt;Make sure you have indexes on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;created_at&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;item_type&lt;/code&gt; or this will be very slow. If you don’t find another way to limit. For example we don’t have an index on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;created_at&lt;/code&gt; so I did a query to find the max_id of 3 months ago and use that as the limit opposed to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;created_at&lt;/code&gt; which makes the queries run without any perf issues.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Weekly Software Updates</title>
   <link href="https://www.mayerdan.com/management/2017/05/18/weekly-software-updates"/>
   <updated>2017-05-18T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/management/2017/05/18/weekly-software-updates</id>
   <content type="html">
&lt;h2 id=&quot;stay-in-sync&quot;&gt;Stay in Sync&lt;/h2&gt;

&lt;p&gt;When your team is distributed, it can be hard to keep in sync, feel a part of a team share knowledge, pass on learnings, and best practices. One thing I found helpful when I was part of a of partially co-located and part distributed. An async weekly software update, collecting announcements, important dates, and great links. My manager &lt;a href=&quot;https://twitter.com/dramsay&quot;&gt;Doug Ramsay&lt;/a&gt; and my next manager &lt;a href=&quot;https://twitter.com/mad_typist&quot;&gt;Jessie Link&lt;/a&gt; both followed this practice in some form or another… The basic format was some announcements, team updates, and interesting links. I honestly can’t recall if the weekly update had a name, but in my mine I know I always called Jessie’s updates, “Jessie Link’s Links”. I believe originally the updates were sent to the whole team via email, then email / campfire, and eventually Slack.&lt;/p&gt;

&lt;p&gt;This is different than team stand ups or more formal processes and can be looser sometimes sharing funny pics, posts, or birthdays. I always enjoyed reading Jessie &amp;amp; Doug’s links and it has been great to continue the practice.&lt;/p&gt;

&lt;h2 id=&quot;software-update-template&quot;&gt;Software Update Template&lt;/h2&gt;

&lt;p&gt;As I have been helping keep a fully distributed team working together and up to speed. I have found it helpful as well. I have slightly changed the format over time, but the basics are in the template &lt;!--more--&gt; below.&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;As another week starts, ...funny_or_motivating line...
	
:keyboard: Dev first-responder: &amp;lt;&amp;gt;
:todo: PM Release Coordinator: &amp;lt;&amp;gt;
:calendar: Holidays / Vacations
     * 
:speaker: Updates:
     * 
:newspaper: Articles:
     * 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;what-to-cover&quot;&gt;What to Cover&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Rotations&lt;/strong&gt;: If there is any sort of rotation like a &lt;a href=&quot;https://www.mayerdan.com/ruby/2012/11/18/working-with-teams-first-responder&quot;&gt;First Responder&lt;/a&gt; list who is on rotation that week. We have that concept both for the dev team and our PM team. So they are listed first.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Calendar&lt;/strong&gt;: While all the specifics on the calendar should be on some sort of team shared calendars it is good to call out and place reminders for the team in this section. People that might be out of office or specifically calling out holidays that impact parts of the company.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Updates&lt;/strong&gt;: In this section I note any big items occurring during the week, such as a large anticipated release. Internal or external talks by our team. It’s a great place to call out something awesome someone on the team recently did. New folks joining the team, hiring announcements, etc…&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Articles&lt;/strong&gt;: This is a collection of good and relevant links I have seen in the last week or so. Sometimes sent in by the team (share the link and credit who passed it along), sometimes if is one of teams blog posts, or something I picked up on twitter. This is a great way to help introduce new ideas that can be discussed by the team later.&lt;/p&gt;

&lt;h2 id=&quot;software-update-example&quot;&gt;Software Update Example&lt;/h2&gt;

&lt;p&gt;An example of a filled out example below, to help show what this translates into each week.&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;As another week starts, the sun still creates energy
    
:keyboard: Dev first-responder: John
:todo: PM Release Coordinator: Galt 
:calendar: Holidays / Vacations
     * Dan is working from Paris Monday / Tuesday
     * Annyce is at Google I/O on T - F
     * John is off on Friday
     * Tanzania folks are out of office for a Holiday Friday
:speaker: Updates:
     * Software Support is out this week so let’s try to pay extra care to #software-bugs
     * welcome Konrad, onboarding new dev on the 21st
     * We are looking for a good PM pass it around: https://medium.com/electric-africa/product-manager-service-logistics-41f5b404a8dd
:newspaper: Articles:
     * Sinatra hits 2.0, https://github.com/sinatra/sinatra/tree/v2.0.0
     * Best talks from Railsconf, http://confreaks.tv/events/railsconf2017
     * Switching to Kotlin, https://medium.com/@magnus.chatt/why-you-should-totally-switch-to-kotlin-c7bbde9e10d5
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Which when rendered becomes.&lt;/p&gt;

&lt;p&gt;As another week starts, the sun still creates energy&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;:keyboard: Dev first-responder: John&lt;/li&gt;
  &lt;li&gt;:todo: PM Release Coordinator: Galt&lt;/li&gt;
  &lt;li&gt;:calendar: Holidays / Vacations
    &lt;ul&gt;
      &lt;li&gt;Dan is working from Paris Monday / Tuesday&lt;/li&gt;
      &lt;li&gt;Annyce is at Google I/O on T - F&lt;/li&gt;
      &lt;li&gt;John is off on Friday&lt;/li&gt;
      &lt;li&gt;Tanzania folks are out of office for a Holiday Friday&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;:speaker: Updates:
    &lt;ul&gt;
      &lt;li&gt;Software Support is out this week so let’s try to pay extra care to #software-bugs&lt;/li&gt;
      &lt;li&gt;welcome Konrad, onboarding new dev on the 21st&lt;/li&gt;
      &lt;li&gt;We are looking for a &lt;a href=&quot;https://medium.com/electric-africa/product-manager-service-logistics-41f5b404a8dd&quot;&gt;good PM pass it around&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;:newspaper: Articles:
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;https://github.com/sinatra/sinatra/tree/v2.0.0&quot;&gt;Sinatra hits 2.0&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;http://confreaks.tv/events/railsconf2017&quot;&gt;Best talks from Railsconf&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://medium.com/@magnus.chatt/why-you-should-totally-switch-to-kotlin-c7bbde9e10d5&quot;&gt;Switching to Kotlin&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>Distributed Team Tips</title>
   <link href="https://www.mayerdan.com/management/2017/04/26/distributed-team-tips"/>
   <updated>2017-04-26T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/management/2017/04/26/distributed-team-tips</id>
   <content type="html">
&lt;h1 id=&quot;distributed-team-tips&quot;&gt;Distributed Team Tips&lt;/h1&gt;

&lt;blockquote&gt;
  &lt;p&gt;credit: &lt;a href=&quot;http://wikimedia.org&quot;&gt;wikimedia.org&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A awhile ago a developer friend &lt;a href=&quot;https://twitter.com/qrush&quot;&gt;@Qrush&lt;/a&gt; asked folks for thoughts on how distributed teams should work on twitter. He asked specific questions, I responded and thought it might be helpful to share the slightly cleaned up answers with &lt;!--more--&gt; others.&lt;/p&gt;

&lt;h2 id=&quot;how-distributed&quot;&gt;How Distributed?&lt;/h2&gt;

&lt;p&gt;A quick note to make the tips a bit more relevant. Our company is highly distributed across timezones, cultures, and languages. This means some things that work for people just dealing with East Coast and West Coast, don’t work so well when handling from Denver to Tanzania. Obviously, keep it simple and if you don’t need as many processes around managing timezones, don’t worry about it.&lt;/p&gt;

&lt;h3 id=&quot;whats-one-unique-thing-you-feel-your-team-does-differently-than-other-orgs-to-work-better-with-everyone-across-different-timezonesasynchronously&quot;&gt;What’s one unique thing you feel your team does differently than other orgs to work better with everyone across different timezones/asynchronously?&lt;/h3&gt;

&lt;p&gt;At &lt;a href=&quot;http://offgrid-electric.com/&quot;&gt;Offgrid Electric&lt;/a&gt; we have done a couple things, I don’t think there is any one magic bullet but it is a combination of efforts that helps to make a highly distributed team work.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Time Zone Management Guidelines&lt;/strong&gt;: I wish I could take credit for this, but it was started and created above my pay grade. The company had group discussions, surveys, and maintains a shared doc. The doc covers tips and examples to help folks with time zone etiquette and best practices.
    &lt;ul&gt;
      &lt;li&gt;It also manages a company calendar showing the ‘recommended’ hours for cross time zone meetings.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Maker Day Wednesday (No Meeting Wednesday)&lt;/strong&gt;: This is on the company calendar for all the groups that are software stakeholders (operations, finance, PM, etc).
    &lt;ul&gt;
      &lt;li&gt;This is frequently people’s favorite day of the week and often when they get the most done.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Encourage Async Communications&lt;/strong&gt;:
    &lt;ul&gt;
      &lt;li&gt;conversations should be in tickets (Asana/Jira), documents (lots of google docs attached to tickets with comments, screen shots, etc), or github (PRs)&lt;/li&gt;
      &lt;li&gt;slack is for more real time questions, not to record decisions, prioritize work, or cover details.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Meetings&lt;/strong&gt;:
    &lt;ul&gt;
      &lt;li&gt;Have a document outlining some meeting best practices the company encourages.&lt;/li&gt;
      &lt;li&gt;Our company doc pushes “core meeting hours”. Tuesday and Thursday have core hours that work for nearly all timezones, the bulk of meetings should be scheduled during these hours.&lt;/li&gt;
      &lt;li&gt;Record meetings, don’t expect everyone in every time zone will always be able to make meetings that are on weird hours, record the video and let them watch async&lt;/li&gt;
      &lt;li&gt;Add most people to calendar invites as optional unless they have an active role to play, otherwise they can catch up via the recording Async. This let’s people know the priority of making a meeting that might be at a difficult time.&lt;/li&gt;
      &lt;li&gt;In general the company outlines the types of meetings and suggests sending materials for meetings 24 hrs in advance. This is less for team standups and more for sharing with exec or wider audiences.&lt;/li&gt;
      &lt;li&gt;I also have a &lt;strong&gt;quarterly reminder&lt;/strong&gt; to go over all my meetings particularly recurring ones… Cut, reduce, shorten, whatever you can.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Respect Cultures Holidays&lt;/strong&gt;: We have all the countries holidays on our calendars, don’t schedule meetings important to someone during one of their countries holidays.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Shared Team Calendar&lt;/strong&gt;: The team should add anything on it that would impact their ability to work during normal hours…
    &lt;ul&gt;
      &lt;li&gt;It is really helpful to just know someone is out for an afternoon, if you had planned to sync up with them and miss because of timezones when they are out that is frustrating and can delay decisions by 24hours or more.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;In Person Time Matters&lt;/strong&gt;: Try to get together small groups a couple times a year and everyone at least once a year.
    &lt;ul&gt;
      &lt;li&gt;Remember when budgets get tight the first thing cut is travel. If you are highly distributed this is going to hurt more than you think. Protect it if you can, explain the importance to execs and anyone pushing back on budgets.&lt;/li&gt;
      &lt;li&gt;I know some folks that build travel cost into salary hidden away in the budget so it can’t really be taken away later.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Documentation&lt;/strong&gt;: Maintain lots of documentation…
    &lt;ul&gt;
      &lt;li&gt;Software Team Docs: We try to keep FAQs, expensing policies, travel policies, etc in Git… Want to change things, send a PR.&lt;/li&gt;
      &lt;li&gt;Cross Org Docs: Our team shares lots of documentation with other groups, these docs we keep in google docs. This is more accessible and easier to work with other groups.&lt;/li&gt;
      &lt;li&gt;Templates: Have a useful format for something? Make a template, then copy, rinse, and repeat.&lt;/li&gt;
      &lt;li&gt;Organize, Prune, Archive, Comment, Share, and do whatever to try to keep things up to date and useful.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Microhubs&lt;/strong&gt;: Try to get 2 people in cities were you hire… While we are all over we tend to have little clusters of a handful of people. It makes it nice to send that group out to dinner for the holidays, just have something in person.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Default Time Zone&lt;/strong&gt;: Pick the &lt;strong&gt;COMPANY&lt;/strong&gt; timezone. At our company all hours are given in EAT (East African Time), folks know to adjust from where they are to that time when there are any announcements.
    &lt;ul&gt;
      &lt;li&gt;Google Labs has an awesome calendar feature to have 2 timezones display on your calendar at all times.&lt;/li&gt;
      &lt;li&gt;We store all dates in UTC as most devs, but most of our tools (New Relic, Sumologic, etc) display data in EAT.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Distributed Bonding&lt;/strong&gt;: It is hard but do what you can to let distributed folks just get to know each other and stay engaged.
    &lt;ul&gt;
      &lt;li&gt;We do lots of brownbags, some more serious, some just kind of fun and interesting… Keep it light but encourages time to get folks chatting.&lt;/li&gt;
      &lt;li&gt;Slack / Team Jokes, I am sure folks love GIFs if they are distributed.&lt;/li&gt;
      &lt;li&gt;Emoji :)&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;whats-your-main-method-of-communication-chatemailsomething-else-if-you-could-change-anything-about-how-that-works-to-help-remote-folks-what-would-it-be&quot;&gt;Whats your main method of communication? Chat/email/something else? If you could change anything about how that works to help remote folks: what would it be?&lt;/h3&gt;

&lt;p&gt;Slack, it is our primary comms, and it is a bit noisy… I would encourage people to turn it off a bit more often for focus work.&lt;/p&gt;

&lt;p&gt;Make sure there is a agreed on video conferencing software for the company, that it is known to work well in the locations you operate. We have found that Zoom works very well for us and works even with low bandwidth connections.&lt;/p&gt;

&lt;h5 id=&quot;we-are-hiring&quot;&gt;We are hiring&lt;/h5&gt;

&lt;p&gt;Know any great project managers? Do you want to work with me on off-grid electricity in Africa?&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://medium.com/electric-africa/product-manager-service-logistics-41f5b404a8dd&quot;&gt;Product Manager, Logistics&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>Alexa Compliment My Wife</title>
   <link href="https://www.mayerdan.com/programming/2017/03/18/alexa-compliment-my-wife"/>
   <updated>2017-03-18T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/programming/2017/03/18/alexa-compliment-my-wife</id>
   <content type="html">
&lt;h1 id=&quot;alexa-tell-my-wife-shes-pretty&quot;&gt;Alexa, tell my wife she’s pretty&lt;/h1&gt;

&lt;blockquote&gt;
  &lt;p&gt;Erin holding baby Theo&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I have a hobby of playing around with new technology while ignoring the super bowl on TV. This year I decided to learn some of the Alexa API. I had received an &lt;a href=&quot;https://www.amazon.com/b/?ie=UTF8&amp;amp;node=9818047011&amp;amp;ref_=fs_ods_fs_aucc_cp&quot;&gt;Echo&lt;/a&gt; for Christmas and thought it would be fun to build a really simple app. I decided to make a little app to help me compliment my wife. We try to &lt;a href=&quot;https://www.gottman.com/blog/the-positive-perspective-dr-gottmans-magic-ratio/&quot;&gt;compliment each other daily&lt;/a&gt;, something we learned in our marriage class ;)&lt;/p&gt;

&lt;h1 id=&quot;alexa-skill-my-wife&quot;&gt;Alexa Skill: “My Wife”&lt;/h1&gt;

&lt;p&gt;I had the basic functions working by the end of the Super Bowl. After tweaking it a bit more and adding a name variable I decided it was good enough to publish. It is a silly skill, but it was something fun to learn and do in a couple of hours. It has also inspired me to start working on some other Alexa skills. Without further ado, feel free to install the &lt;a href=&quot;https://www.amazon.com/DanMayer-MyWife/dp/B06XDX5QPM/ref=sr_1_2?s=digital-skills&amp;amp;ie=UTF8&amp;amp;qid=1489859248&amp;amp;sr=1-2&amp;amp;keywords=my+wife&quot;&gt;MyWife Alexa skill&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;User: “Alexa, tell my wife she is pretty.”&lt;br /&gt;
Alexa: “Hey Wife, Wow, that confidence looks sexy on you.”&lt;br /&gt;
User: “Alexa, tell my wife set name to Erin”&lt;br /&gt;
Alexa: “Wife’s name is now set to Erin”&lt;br /&gt;
User: “Alexa, tell my wife Erin she is sexy” &lt;br /&gt;
Alexa: “Dang Girl is your name Wifi ? Because I’m feeling a connection!” &lt;br /&gt;
User: “Alexa, tell my wife I love her”    &lt;br /&gt;
Alexa: “I want to grow old with you.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I plan to add some more fun features eventually, but this was amusing enough for me to start with.&lt;/p&gt;

&lt;h1 id=&quot;alexa-links&quot;&gt;Alexa Links&lt;/h1&gt;

&lt;p&gt;I have been having fun with our Echo’s, you can see how I have been using &lt;a href=&quot;https://www.mayerdan.com/2017/03/05/Alexa-for-newborns&quot;&gt;Alexa for newborns&lt;/a&gt; to help us adjust to our baby. I plan to try to do even more with Alexa in the future.&lt;/p&gt;

&lt;p&gt;If you want to get started with Alexa programming. Amazon made it super simple, start with the Node.js Alexa SDK Lambda skill, and work from there. See the simple example tutorials, I liked above with my code. I think there are some really cool possibilities opened up by nearly always on voice UI.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;sign up as an &lt;a href=&quot;https://developer.amazon.com/alexa&quot;&gt;Amazon Alexa developer&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;read about the &lt;a href=&quot;https://github.com/alexa/alexa-skills-kit-sdk-for-nodejs&quot;&gt;Alexa skills kit for Node.js&lt;/a&gt; and &lt;a href=&quot;https://github.com/amzn/alexa-skills-kit-js&quot;&gt;here&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;getting started with &lt;a href=&quot;https://developer.amazon.com/alexa-skills-kit#Ready%20to%20start%3F&quot;&gt;skills kit&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;a good &lt;a href=&quot;https://developer.amazon.com/blogs/post/TxDJWS16KUPVKO/new-alexa-skills-kit-template-build-a-trivia-skill-in-under-an-hour&quot;&gt;trivia game tutorial&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;a nice &lt;a href=&quot;https://developer.amazon.com/blogs/post/Tx3DVGG0K0TPUGQ/updated-alexa-skills-kit-fact-template-step-by-step-guide-to-build-a-fact-skill&quot;&gt;Fact skills step by step guide&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;code&quot;&gt;Code&lt;/h1&gt;

&lt;p&gt;This is the full &lt;a href=&quot;https://gist.github.com/danmayer/34c645ef1780fed7510cf904e04dcc7b&quot;&gt;source code&lt;/a&gt;, feel free to use it to learn some Alexa basics. It basically extends some concepts from the &lt;a href=&quot;https://github.com/alexa/skill-sample-nodejs-fact&quot;&gt;sample space facts&lt;/a&gt; and &lt;a href=&quot;https://github.com/alexa/skill-sample-nodejs-highlowgame/blob/master/src/index.js&quot;&gt;high low game&lt;/a&gt; examples.&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/danmayer/34c645ef1780fed7510cf904e04dcc7b.js&quot;&gt;&lt;/script&gt;

</content>
 </entry>
 
 <entry>
   <title>Alexa For Newborns</title>
   <link href="https://www.mayerdan.com/2017/03/05/Alexa-for-newborns"/>
   <updated>2017-03-05T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2017/03/05/Alexa-for-newborns</id>
   <content type="html">
&lt;h1 id=&quot;a-newborn--alexa&quot;&gt;A Newborn &amp;amp; Alexa&lt;/h1&gt;

&lt;blockquote&gt;
  &lt;p&gt;Theo doing his best Spock hand&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Erin and I recently welcomed, &lt;a href=&quot;https://millermayersadventures.com/2017/02/23/theodore-harley-mayer/&quot;&gt;Theo&lt;/a&gt;, our first baby into the world. We had to learn lots of new things to take care of Theo and ourselves. Particularly, we had to learn how to do many things with a single hand or even better hands free. As I struggled to do things while feeding a baby and trying to keep him quiet so Erin could sleep, I started to get a bit more creative and utilize &lt;a href=&quot;https://www.amazon.com/echo&quot;&gt;Amazon’s Alexa&lt;/a&gt; more to ease some tasks.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/TheosCrib.gif&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;The crib light to check on baby: red light, 10% and 90% brightness, can change colors.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;introduced-to-alexa&quot;&gt;Introduced to Alexa&lt;/h2&gt;

&lt;p&gt;I had received an Echo Dot for Xmas, we set it up for some music. I got two Alexa plugs and a lightbulb. While kind of neat the home automation wasn’t that helpful. We mostly used it for news flash, &lt;a href=&quot;https://www.pandora.com/&quot;&gt;Pandora&lt;/a&gt;, and some silly fun. I programmed an Alexa app to compliment my wife for me, “Alexa, tell my wife she is pretty.” This makes Alexa spew random funny compliments much to our amusement, but that is the subject of upcoming post. My wife enjoyed following pregnancy tips using the &lt;a href=&quot;https://www.amazon.com/Everyday-Health-Inc-What-Expect/dp/B01JK91WBW&quot;&gt;What To Expect&lt;/a&gt; skill during her pregnancy as well. We even played around with a fun &lt;!--more--&gt; baby name &lt;a href=&quot;https://www.amazon.com/andrew-smith-Baby-Name/dp/B01N94A1A6&quot;&gt;Alexa skill&lt;/a&gt;. In general prior to our baby’s arrival Alexa was kind of neat, but I hadn’t really thought of it as something that would be useful or actually helpful for raising a newborn.&lt;/p&gt;

&lt;h2 id=&quot;the-lightbulb-moment&quot;&gt;The Lightbulb Moment&lt;/h2&gt;

&lt;p&gt;The lightbulb went off in my head about how useful Alexa could be when I was exhausted bottle feeding the baby in the middle of the very first night we had him home. The baby was crying after not getting quite enough milk from the last breast feeding, Erin really needed some sleep, and I was supplementing with donor milk in a bottle.&lt;/p&gt;

&lt;p&gt;As I sit in a room that is too dark, I am bored and sleepy as I forgot my phone and kindle. Theo is more playing than eating, so I just sit in the silence for over an hour trying to stay awake. I realize that Alexa could solve my problem. I could be entertained without my phone with music, audio books, and more. As for the lights being able to control and dim those by voice would do the trick. When I change the baby and try to get him into deep sleep before returning to our bedroom, I could also have Alexa sooth the baby with white noise back into a deep sleep. All of this sounded great, I wanted an Echo in our nursery right now, and if I had one, I could have used it to order one ;) The next morning in a sleepy haze I ordered another echo and some colored echo-enabled lights.&lt;/p&gt;

&lt;h2 id=&quot;lights-sounds--actions&quot;&gt;Lights, Sounds, &amp;amp; Actions&lt;/h2&gt;

&lt;p&gt;A brief list of our Alexa usage. See the section below for a far more detailed example of how we put Alexa to use.&lt;/p&gt;

&lt;h3 id=&quot;lights&quot;&gt;Lights&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/TheosRoom.gif&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;The nursery has two lights that can be controlled together or independently&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Crib Light:&lt;/strong&gt; We want to check on Theo occasionally through out the night or one of us needs to pick him up to soothe him out of the room. This light is our primary way to do that. Off course it is &lt;a href=&quot;http://www.health.com/sleep/red-spectrum-light-sleep&quot;&gt;Red Light&lt;/a&gt; to help us sleep.
&lt;strong&gt;Theo’s Room:&lt;/strong&gt; Generally when I walk into the Nursery my hands are full, baby, bottle, extra blankets, etc. Control either the full room or the bedside lamp.&lt;/p&gt;

&lt;h3 id=&quot;sounds&quot;&gt;Sounds&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Music&lt;/li&gt;
  &lt;li&gt;Books (Audible / Kindle)&lt;/li&gt;
  &lt;li&gt;White Noise machine&lt;/li&gt;
  &lt;li&gt;Podcasts&lt;/li&gt;
  &lt;li&gt;News Alerts&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;actions&quot;&gt;Actions&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.amazon.com/cWix-Baby-Stats/dp/B01F7RG94G/ref=sr_1_cc_1?s=aps&amp;amp;ie=UTF8&amp;amp;qid=1488651039&amp;amp;sr=1-1-catcorr&amp;amp;keywords=baby+stats&quot;&gt;Baby Stats&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Alarms&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.amazon.com/blandflakes-stopwatch/dp/B019DB6YNI&quot;&gt;Stopwatch&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Add to shopping / todo list when your hands are full&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;alexa-bedtime-routine&quot;&gt;Alexa Bedtime Routine&lt;/h2&gt;

&lt;p&gt;I continued to think of ideas and started to use the echo in other ways. The list below shows how we use Alexa as part of our nighttime baby routine. You can see how we use &lt;a href=&quot;http://www.health.com/sleep/red-spectrum-light-sleep&quot;&gt;red lights&lt;/a&gt; around all sleep area lighting to reduce light in the middle of the night impacting our sleep.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/Bedroom.gif&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;The two lights in our bedroom including the Crib light, which is the one we use the most.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
  &lt;li&gt;Due to Theo’s low birth weight and weight loss we had to track feedings, pees, &amp;amp; poops.
    &lt;ul&gt;
      &lt;li&gt;We did this on paper, but towards the end of our tracking discovered the Alexa skill &lt;a href=&quot;https://www.amazon.com/cWix-Baby-Stats/dp/B01F7RG94G/ref=sr_1_cc_1?s=aps&amp;amp;ie=UTF8&amp;amp;qid=1488651039&amp;amp;sr=1-1-catcorr&amp;amp;keywords=baby+stats&quot;&gt;Baby Stats&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;To encourage milk production and get Theo to gain weight we had to follow a “Triple feed diet”
    &lt;ul&gt;
      &lt;li&gt;Feed at least every three hours (unless baby requests food earlier)
        &lt;ul&gt;
          &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;Alexa, set alarm for 3 hours&quot;&lt;/code&gt;&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;Start feeding with breast feeding at least 10 minutes a side
        &lt;ul&gt;
          &lt;li&gt;install the &lt;a href=&quot;https://www.amazon.com/blandflakes-stopwatch/dp/B019DB6YNI&quot;&gt;Stopwatch&lt;/a&gt; skill.&lt;/li&gt;
          &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;Alexa, open stopwatch and start&quot;&lt;/code&gt;&lt;/li&gt;
          &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;Alexa, ask stopwatch for my status&quot;&lt;/code&gt;&lt;/li&gt;
          &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;Alexa, ask stopwatch to stop&quot;&lt;/code&gt;&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;Use the Breast pump for 15 minutes
        &lt;ul&gt;
          &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;Alexa, set alarm for 15 minutes&quot;&lt;/code&gt;&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;Bottle feed 1.5 oz
        &lt;ul&gt;
          &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;Alexa, ask baby stats to add feeding 1.5 ounces&quot;&lt;/code&gt;&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;During our feed cycle which takes about an hour, we can get some entertainment if we are away from our phones / partner.
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;Alexa, open Pandora&quot;&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;Alexa, open audible book&quot;&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;Alexa, open Kindle&quot;&lt;/code&gt; (This is a really cool feature where it will use the Alexa voice to read your kindle book to you.)&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;After a feeding (and a diaper change), time to calm the baby
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;Alexa, dim Theo&apos;s room to 5 percent&quot;&lt;/code&gt; (less light and red light)&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;Alexa, open ocean sounds&quot;&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;There are a number of white noise options this is my favorite.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As I finally leave our Nursery, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;Alexa, turn off Theo&apos;s room&quot;&lt;/code&gt;, and return to our bedroom, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;Alexa, turn on crib light&quot;&lt;/code&gt;, I set the baby back into his crib. I return to bed and watch Theo settle for awhile. Eventually, satisfied he is down for sleep, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;Alexa, turn off baby crib&quot;&lt;/code&gt; and the room goes dark and I return to sleep.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Cleanup your DB, find unused tables</title>
   <link href="https://www.mayerdan.com/programming/2017/02/08/cleanup-your-db"/>
   <updated>2017-02-08T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/programming/2017/02/08/cleanup-your-db</id>
   <content type="html">
&lt;p&gt;As systems grow sometimes tables and models come and go. Often things don’t get fully cleaned up or removed from the system. As time goes on it is often harder and harder to know what is safe to remove, as people involved with the code may have moved on. I have used lots of little tricks to clean up dead code. I even maintain &lt;a href=&quot;https://github.com/danmayer/coverband&quot;&gt;Coverband&lt;/a&gt; to find code usage in production. One trick &lt;a href=&quot;https://twitter.com/tcopeland&quot;&gt;Tom Copeland&lt;/a&gt; pointed out to me was checking the max created at on a given table to see when it was last used. It was so simple and helpful I ended up wanting to do that across all tables not just individual models.&lt;/p&gt;

&lt;h1 id=&quot;rake-db-cleanup&quot;&gt;Rake DB cleanup&lt;/h1&gt;

&lt;p&gt;I first tried to do this in pure SQL with a subquery, but when I was having trouble making it work, I found a post basically saying don’t do that. It’s point was if you are going to run a subquery over a collection of all your tables us code not SQL. In the end I made a simple Rake task to output the data I wanted.  You should be able to drop it into your own project and get a good idea of tables that might not be needed and get to drop the related code as well. In my case it brought about 14 tables to our attention to schedule for cleanup.&lt;/p&gt;

&lt;div class=&quot;language-ruby 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;n&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:database&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;List all tables in surge DB and last usage, help find tables to remove&apos;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:list_recent&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:environment&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;table_data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;skip_large_slow_tables&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;some_table&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;versions&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;sql&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;select * from information_schema.tables where table_schema=&apos;public&apos; and table_type=&apos;BASE TABLE&apos;;&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;tables_array&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;connection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sql&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;tables_array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;table_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;table_name&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;next&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;skip_large_slow_tables&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;include?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;running: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table_name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;begin&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;sql&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;select max(created_at) from &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table_name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;;&quot;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;max_result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;connection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sql&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;max&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;rescue&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;max_result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;N/A&apos;&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;table_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;max_result&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;table_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;sort_by&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_s&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pair&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;table&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pair&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;first&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;max&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pair&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;last&lt;/span&gt;
      &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>2016 in Review</title>
   <link href="https://www.mayerdan.com/management/2017/01/22/2016-in-review"/>
   <updated>2017-01-22T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/management/2017/01/22/2016-in-review</id>
   <content type="html">
&lt;h1 id=&quot;2016-in-review&quot;&gt;2016 in review&lt;/h1&gt;

&lt;p&gt;As 2017 gets going, it is nice to look back a bit. The year has certainly given much to think about.&lt;/p&gt;

&lt;h1 id=&quot;on-the-homefront&quot;&gt;On the Homefront&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;We have a &lt;a href=&quot;https://millermayersadventures.com/2016/11/21/our-next-adventure/&quot;&gt;baby on the way&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;Probably related we now have wills and life insurance in place ;)&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;We moved our household to 100% renewable electricity (still have natural gas as well)
    &lt;ul&gt;
      &lt;li&gt;reduced our energy usage about 30%&lt;/li&gt;
      &lt;li&gt;beat out our energy efficient neighbors: “You’re using 9% less energy than your efficient neighbors.”&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;We saw Hamilton in NYC, which we had been wanting to do since we first hear the NPR broadcast of the soundtrack. It was as inspiring as I had hoped.&lt;/li&gt;
  &lt;li&gt;I wrote a little bit:
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;https://www.mayerdan.com/archive&quot;&gt;5 dev blog posts&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;3 travel posts: &lt;a href=&quot;https://millermayersadventures.com/2016/11/24/life-stages-as-told-through-burritos/&quot;&gt;life stages through burritos&lt;/a&gt;, &lt;a href=&quot;https://millermayersadventures.com/2016/01/16/aruba-advice/&quot;&gt;Aruba Advice&lt;/a&gt;, &amp;amp; &lt;a href=&quot;https://millermayersadventures.com/2016/01/10/ya-gotta-have-a-hobby-mines-sewer-covers/&quot;&gt;Sewer Cover Photos&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;I worked on some open source:
    &lt;ul&gt;
      &lt;li&gt;Getting back into &lt;a href=&quot;https://github.com/danmayer/coverband&quot;&gt;Coverband&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;releasing my first &lt;a href=&quot;https://github.com/danmayer/s3_image_converter&quot;&gt;Clojure project&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;working on learning &lt;a href=&quot;https://github.com/danmayer/hundred-pushups&quot;&gt;React Native and Clojure script&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;books&quot;&gt;Books&lt;/h1&gt;

&lt;p&gt;I &lt;a href=&quot;https://www.goodreads.com/user/year_in_books/2016/33190534&quot;&gt;Read 26 books&lt;/a&gt;, over my goal of 18!&lt;/p&gt;

&lt;p&gt;I also finished converting my &lt;a href=&quot;https://github.com/danmayer/Eagle-Eye-Descending&quot;&gt;Grandfather’s book&lt;/a&gt; from the last known paper copy to an e-book. I will clean this up a bit more and likely try to release it in 2017.&lt;/p&gt;

&lt;h2 id=&quot;best-books&quot;&gt;Best books:&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Best book: &lt;a href=&quot;https://www.goodreads.com/book/show/11447065-the-quest&quot;&gt;The Quest: Energy, Security, and the Remaking of the Modern World&lt;/a&gt;, this was a long read, but I really enjoyed the history of and energy perspective.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.amazon.com/dp/B000SEKFZI/ref=dp-kindle-redirect?_encoding=UTF8&amp;amp;btkr=1&quot;&gt;Be Prepared: A Practical Handbook for New Dads&lt;/a&gt;: by far the most fun new father book I read.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.amazon.com/Elon-Musk-SpaceX-Fantastic-Future/dp/0062301233/ref=sr_1_1?ie=UTF8&amp;amp;qid=1483199773&amp;amp;sr=8-1&amp;amp;keywords=Elon+Musk%3A+Tesla%2C+SpaceX%2C+and+the+Quest+for+a+Fantastic+Future&quot;&gt;Elon Musk: Tesla, SpaceX, and the Quest for a Fantastic Future&lt;/a&gt;: while everyone has their flaws this was inspiring and makes me want to be more driven with purpose.&lt;/li&gt;
  &lt;li&gt;Saddest Book, but good to know: &lt;a href=&quot;https://www.amazon.com/Wish-Inform-Tomorrow-Killed-Families/dp/0312243359/ref=sr_1_1?ie=UTF8&amp;amp;qid=1483219127&amp;amp;sr=8-1&amp;amp;keywords=We+Wish+to+Inform+You+That+Tomorrow+We+Will+Be+Killed&quot;&gt;We Wish to Inform You That Tomorrow We Will be Killed With Our Families: Stories from Rwanda&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;book-quotes&quot;&gt;Book Quotes&lt;/h2&gt;

&lt;p&gt;Some quotes I highlighted that caught my eye.&lt;/p&gt;

&lt;h3 id=&quot;the-architecture-of-open-source-applications-volume-ii-structure-scale-and-a-few-more-fearless-hacks&quot;&gt;&lt;a href=&quot;https://www.amazon.com/Architecture-Open-Source-Applications-Structure-ebook/dp/B008940UYK/ref=sr_1_1?ie=UTF8&amp;amp;qid=1485135437&amp;amp;sr=8-1&amp;amp;keywords=The+Architecture+of+Open+Source+Applications%2C+Volume+II%3A+Structure%2C+Scale%2C+and+a+Few+More+Fearless+Hacks&quot;&gt;The Architecture of Open Source Applications, Volume II: Structure, Scale, and a Few More Fearless Hacks&lt;/a&gt;&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;When we started this project, we were losing team members too often. In itself, this is bad. However, the lack of accurate up-to-date documentation meant that most of the technical understanding of the release process was documented by folklore and oral histories, which we lost whenever a person left. We needed to turn this situation around, urgently.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Relates to how teams need to bust open silos and make sure multiple people understand systems.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;We did this by making sure that we set aside time to fix at least one thing—anything!—after each release. We implemented this by negotiating for a day or two of “do not disturb” time immediately after we shipped a release.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;How to get a problem area out of a tar pit…&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;These improvements snowballed until we found ourselves past the tipping point, and able to make time to work on significant major improvements. At that point, the gains from release optimizations really kicked in.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;and how you can tell you have finally overcome that weakness.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;One of the most important duties of a project’s lead architect is to defend the architecture against expedient “fixes” that would break it and cause functional problems or severe maintenance headaches down the road. Arguments over this can get quite heated, especially when defending architecture conflicts against something that a developer or user considers a must-have feature. But these arguments are necessary, because the easiest choice is often the wrong one for the longer term.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Team leadership, in architectural integrity is important.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The general rule of thumb is that rather than hard-coding constants, developers use run-time-settable MCA parameters, thereby allowing power users to tweak run-time behavior.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Mirroring some thoughts on permissions.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;There is no place for the “not invented here” religion when trying to solve highly complex engineering problems; it only makes good logistical sense to re-use external code whenever possible. Such re-use frees developers to focus on the problems unique to Open MPI; there is no sense re-solving a problem that someone else has solved already.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A good reminder to build onto of the shoulders of those before you.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Differences are good. Developer disagreements are good. Embrace challenges to the status quo; do not get complacent. A plucky grad student saying “Hey, check this out…” can lead to the basis of a whole new feature or a major evolution of the product.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Listen to others, and value the people you work with opinions&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The lesson learned from the TAP fiasco was that to have reasonable maintainability, persistent data needs an explicit schema. More generally, it was a lesson about adding complexity to a project: when considering introducing a novel system for solving a problem, make sure the complexity of that solution is well understood and tested and that the benefits are clearly worth the added complexity before committing the project to it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A reminder that flexible data storage is nice at a feature start, but can add endless complexity to data analysis later on.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The user interface is the most important part of any product. It’s the only part of your program visible to the outside world and if you get it wrong the world will hate you.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This one goes out to &lt;a href=&quot;https://twitter.com/NicholasStevens&quot;&gt;Nick S.&lt;/a&gt; and is a good reminder that in the end it is about the users. It isn’t done until they are using it.&lt;/p&gt;

&lt;h3 id=&quot;be-prepared-a-practical-handbook-for-new-dads&quot;&gt;&lt;a href=&quot;https://www.amazon.com/Be-Prepared-Practical-Handbook-Dads-ebook/dp/B000SEKFZI/ref=sr_1_1?ie=UTF8&amp;amp;qid=1485135486&amp;amp;sr=8-1&amp;amp;keywords=Be+Prepared%3A+A+Practical+Handbook+for+New+Dads&quot;&gt;Be Prepared: A Practical Handbook for New Dads&lt;/a&gt;&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;Don’t let the dog lick your baby’s face for the first few months. The baby’s immune system is still immature, and your dog’s tongue has been to places you’d rather not think about.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ha amazing, also lots of other great advice on prepping your dog for the baby.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Experts agree that reading to even very young babies is a good idea. They say that hearing words read aloud helps to “map” a baby’s brain to focus on, and eventually recognize, certain sound patterns, the building blocks of language.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We read Harry Potter to the baby belly&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;You may think this will never happen to you, and it probably won’t, but stick the key in your wallet anyway. The difficulty of pacifying a six-month-old through a car window is only matched by the humiliation of explaining to the 911 operator the nature of your emergency.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;On keeping an extra set of keys for when you eventually lock the baby in the car!&lt;/p&gt;

&lt;h3 id=&quot;elon-musk-tesla-spacex-and-the-quest-for-a-fantastic-future&quot;&gt;&lt;a href=&quot;https://www.amazon.com/Elon-Musk-SpaceX-Fantastic-Future-ebook/dp/B00KVI76ZS/ref=sr_1_1?ie=UTF8&amp;amp;qid=1485135521&amp;amp;sr=8-1&amp;amp;keywords=Elon+Musk%3A+Tesla%2C+SpaceX%2C+and+the+Quest+for+a+Fantastic+Future&quot;&gt;Elon Musk: Tesla, SpaceX, and the Quest for a Fantastic Future&lt;/a&gt;&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;“It’s like anything else where you find out that the last ten percent is where all the integration happens and things don’t play together,”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Agreed, frequently even with a problem solved, rolling out a integrated and useful solution takes so much effort in that last 10%.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;In one case someone spotted an error in a software file in the hours right before a launch. SpaceX’s engineers changed the file, checked how it affected the test hardware, and, when no problems were detected, sent the file to the Falcon 9, waiting on the launchpad, all in less than thirty minutes.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As organizations grow speed and quick responses get overlooked… Being able to adapt quickly is incredibly important.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;People who await guidance or detailed instructions languish.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Being able to self direct and provide value is incredibly important.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;If the rules are such that you can’t make progress, then you have to fight the rules.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Don’t let process be the excuse for a bad decision or bad execution.&lt;/p&gt;

&lt;h3 id=&quot;we-wish-to-inform-you-that-tomorrow-we-will-be-killed-with-our-families-stories-from-rwanda&quot;&gt;&lt;a href=&quot;https://www.amazon.com/Wish-Inform-Tomorrow-Killed-Families-ebook/dp/B000OI0FI0/ref=sr_1_1?ie=UTF8&amp;amp;qid=1485135945&amp;amp;sr=8-1&amp;amp;keywords=We+Wish+to+Inform+You+That+Tomorrow+We+Will+Be+Killed+with+Our+Families%3A+Stories+From+Rwanda&quot;&gt;We Wish to Inform You That Tomorrow We Will Be Killed with Our Families: Stories From Rwanda&lt;/a&gt;&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;Plenty of more moderate views could be heard, but who listens to moderates in times of revolution?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Rings true in modern times&lt;/p&gt;

&lt;h3 id=&quot;are-your-lights-on&quot;&gt;&lt;a href=&quot;https://www.amazon.com/Are-Your-Lights-Gerald-Weinberg-ebook/dp/B004WOXYV2/ref=sr_1_2?ie=UTF8&amp;amp;qid=1485136038&amp;amp;sr=8-2&amp;amp;keywords=Are+Your+Lights+On%3F&quot;&gt;Are Your Lights On?&lt;/a&gt;&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;EACH SOLUTION IS THE SOURCE OF THE NEXT PROBLEM&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So true when you solve any performance bottleneck you are just uncovering the next one.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;We never get rid of problems. Problems, solutions, and new problems weave an endless chain. The best we can hope for is that the problems we substitute are less troublesome than the ones we “solve.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Another way of saying the above.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;TRY BLAMING YOURSELF FOR A CHANGE—EVEN FOR A MOMENT.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Too often from the start we look to deflect blame.&lt;/p&gt;

&lt;h3 id=&quot;moonwalking-with-einstein-the-art-and-science-of-remembering-everything&quot;&gt;&lt;a href=&quot;https://www.amazon.com/Moonwalking-Einstein-Science-Remembering-Everything-ebook/dp/B004H4XI5O/ref=sr_1_1?ie=UTF8&amp;amp;qid=1485136288&amp;amp;sr=8-1&amp;amp;keywords=moonwalking+with+einstein&quot;&gt;Moonwalking with Einstein: The Art and Science of Remembering Everything&lt;/a&gt;&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;You can exercise daily and eat healthily and live a long life, while experiencing a short one. If you spend your life sitting in a cubicle and passing papers, one day is bound to blend unmemorably into the next—and disappear. That’s why it’s important to change routines regularly, and take vacations to exotic locales, and have as many new experiences as possible that can serve to anchor our memories. Creating new memories stretches out psychological time, and lengthens our perception of our lives.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A good life tip in a memory book.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;What separates experts from the rest of us is that they tend to engage in a very directed, highly focused routine, which Ericsson has labeled “deliberate practice.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I think I do need to do more focused and directed practice. My learning is often to scattered.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;we’re too busy thinking about what we’re going to say next, instead of paying attention&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I am guilty of this far to often.&lt;/p&gt;

&lt;h3 id=&quot;the-quest-energy-security-and-the-remaking-of-the-modern-world&quot;&gt;&lt;a href=&quot;https://www.amazon.com/Quest-Energy-Security-Remaking-Modern-ebook/dp/B005JE2LN6/ref=sr_1_1?ie=UTF8&amp;amp;qid=1485136625&amp;amp;sr=8-1&amp;amp;keywords=the+quest+energy+security+and+the+remaking+of+the+modern+world&quot;&gt;The Quest: Energy, Security, and the Remaking of the Modern World&lt;/a&gt;&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;To create an entire system required considerable funding. Although not called such at the time, one of the other inventions that could be credited to Edison and his investors was venture capital.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Interesting.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The challenge for developing countries is to increase reliability, ensure that power supplies keep up with economic growth, and avoid shortfalls that constrain growth. It is also to deliver electricity to the 1.6 billion people who have no access at all to electricity but instead burn kerosene or scrounge for wood or collect dung.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is in part why I do what I do.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;That is why, despite what is often said, increased renewable or nuclear power would have very little impact on oil use unless accompanied by very widespread adoption of electric cars that plug into the electric grid.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A good point about&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;That states can serve as the laboratories of democracy.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Do we really allow states to veer that far from one another?&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“There’s great momentum in China,” Shi said. “We used to pursue the American dream. Now everybody is pursuing the Chinese dream.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Another reason why we need to do better as a country.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Public Service Company of Colorado, a subsidiary of Xcel Energy, currently has the largest share of its total electricity coming from wind of any utility in the nation, almost 15 percent.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Woohoo, go Colorado. This program is actually how we got our home to 100% renewable energy, by participating in their wind program.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;We need to create a business model in which reducing megawatts is treated the same way from an investment point of view as producing megawatts.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Yes this is a very interesting idea, but capital doesn’t reward reduction at the moment, how would one go about this?&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;For its first 40 years, the oil industry had been a lighting business. Its main product was kerosene, poured into lamps and used around the world for illumination. John D. Rockefeller became the richest man in the world as an illumination merchant.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Interesting and makes obvious why light is the very first thing one gets into with solar energy in emerging markets.&lt;/p&gt;

&lt;h3 id=&quot;get-lucky-how-to-put-planned-serendipity-to-work-for-you-and-your-business&quot;&gt;&lt;a href=&quot;https://www.amazon.com/Get-Lucky-Planned-Serendipity-Business-ebook/dp/B007AKBIGI/ref=sr_1_1?ie=UTF8&amp;amp;qid=1485144074&amp;amp;sr=8-1&amp;amp;keywords=Get+Lucky%3A+How+to+Put+Planned+Serendipity+to+Work+for+You+and+Your+Business&quot;&gt;Get Lucky: How to Put Planned Serendipity to Work for You and Your Business&lt;/a&gt;&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;Attending events and seminars put on by different departments in your organization, joining extracurricular activities where other employees are also participating, even sitting with new people in the cafeteria at lunch are all ways [to increase serendipity]&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It is good to remember to spend time with other people in the company than just your group.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Conferences in your area of expertise, collaborative meet-ups, seminars, networking events, or other smaller get-togethers with like-minded individuals are all terrific ways to put yourself in an unfamiliar situation within a familiar environment.
…
The more you can open up space in your schedule to attend events as frequently as time allows, and to attend as wide a range as possible as opposed to just the same old conference circuit, the more likely you are to have exactly that serendipitous encounter you didn’t even know you needed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Make time to spend time with folks working on similar problems as yourself. You never know what comes out of it.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Pixar University’s roster of classes includes improvisational theater, drawing, and screenwriting as well as classes on every aspect of computerized filmmaking, and classes are open to all employees at every level.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is awesome, clearly I should step outside my box more often.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“The real problems are the ‘m &amp;amp; ms’—the managers and the meetings. Managers’ real jobs are to interrupt people … and managers most of all call meetings, and meetings are just toxic; they’re just terrible poisonous things during the day at work.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Our teams favorite day is no meeting Wednesday.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Even those of us who have lifelong obsessions find it easy to forget them amidst the immediacy of these trifles. But that just means it’s all the more critical to make time and space in your mind for the things that matter to you.
…
commitment balances the short-term view with long-term aspirations.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;More on needing focused, dedicated learning.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Too often, however, when it comes to our work environments we focus overwhelmingly on managing by objectives. We talk of hard work, discipline, and financial incentives.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We need to stay flexible&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;he learned that experiments done for the sake of curiosity often yield the most practical results.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Try new things!&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;We need to make something with it, and then put it out into the world with all the courage and determination we can muster.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Do something with your passions.&lt;/p&gt;

&lt;h3 id=&quot;slack-getting-past-burnout-busywork-and-the-myth-of-total-efficiency&quot;&gt;&lt;a href=&quot;https://www.amazon.com/Slack-Getting-Burnout-Busywork-Efficiency-ebook/dp/B004SOVC2Y/ref=sr_1_1?ie=UTF8&amp;amp;qid=1485140349&amp;amp;sr=8-1&amp;amp;keywords=Slack%3A+Getting+Past+Burnout%2C+Busywork%2C+and+the+Myth+of+Total+Efficiency&quot;&gt;Slack: Getting Past Burnout, Busywork, and the Myth of Total Efficiency&lt;/a&gt;&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;The shortcut that is most frequently employed is to assume that individual workers are entirely fungible.
…
The problem is that human workers are not entirely fungible. Even the most dedicated advocate of matrix management will understand that there are limits to how many ways an individual can be divided.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is still a problem I see far to often where management doesn’t understand the true cost of task shifting.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Knowledge workers aren’t fungible. Treating them as if they were will increase busyness but make it harder for them to get useful work done.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Try to get a team that can focus on the things they are good at.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;We collected information about the environment and recorded each and every task switch. To no one’s surprise, people who were interrupted less often performed better.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;How do we avoid this issue with slack and other tools constantly vying for attention.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Fragmented knowledge workers may look busy, but a lot of their busyness is just thrashing, switching continually from one activity to another.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Agreed when large portions of time by a whole team is just trying to keep up with company communications, announcements, and sync with related teams… It significantly handicaps the ability to get real work done.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The poor worker who is there at midnight has probably stayed at his or her desk after regular closing just in the hope of having some peace and quiet for a little real work. What a shame that the organization can’t give such workers a decently interrupt-and noise-free environment during standard hours.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When the best work is being done outside of the office and outside of office hours something is wrong.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;This can lead to a positive obsession with process. Process obsession is the problem.
…
Process obsession is as common in knowledge-worker organizations today as the common cold … and, in my opinion, about as desirable.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When something slips through the cracks rarely is more process the solution.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;employee evaluation standards that don’t help you build meaningful longterm relationships&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is frequently not kept in mind when developing review processes.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Real quality has little to do with defects, but our so-called quality programs have everything to do with defects.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is extremely true, this is an issue with how we structure the whole concept of QA teams.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;it would suggest that a real Quality Program should spend one-ninth or less of its resources on defect prevention and removal, and the rest on assuring product uniqueness, usefulness, market impact, change of customer work modes, etc&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is a interesting point, but basically now gets into what product managers actually, meaning that it would just be an issue with terminology for many groups (and perhaps the sizing of various teams).&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Defect prevention and removal efforts may add sufficient overhead to the overall process so that it is too slow and unresponsive to market needs.
…
The strongest argument for new product integrity and usefulness has been effectively silenced.
…
Cutting out the inconsequential would be, for most of them, the most important act of quality improvement.
…
The prescription “Make less and choose much more carefully what you make ” is a tough one to swallow. The problem is that the “quality” that quantity has is power.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Some more on what actually makes quality points again to product managers.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;You can’t grow if you can’t change at all. This may not sound like a revelation to you, but I frequently come across organizations that frankly admit they can’t change, that they are stuck in stasis, but nonetheless consider growth a normal expectation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A good reason to always be pushing for change, not more of the same.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;I have identified as barriers to change, such as reduced slack, standard process, poured-in-concrete processes and voluminous documentation of everything that will hold still for it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Another hit against overly process driven&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;It’s nontrivial for a company and everyone in it to know “who we are.” A little bit easier, however, is to know “who we aren’t.” When even that knowledge is missing—when there is no basis in the company to say about a given cockamamy scheme “it just isn’t us”—the company clearly lacks vision.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Making sure to spend time and effort to figure out a real vision is important.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Leadership is the ability to enroll other people in your agenda. Meaningful acts of leadership usually cause people to accept some short-term pain (extra cost or effort, delayed gratification) in order to increase the long-term benefit.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I liked this quote on leadership&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;In order to enable change, companies have to learn that keeping managers busy is a blunder.
…
Making it okay to take risks and succeed in risky endeavors requires that you also make it okay to fail.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;change takes time ;)&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Staying Organized with Markdown</title>
   <link href="https://www.mayerdan.com/programming/2016/11/26/standup-markdown-history"/>
   <updated>2016-11-26T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/programming/2016/11/26/standup-markdown-history</id>
   <content type="html">
&lt;p&gt;It is good to be able to keep track of the daily work you do and to keep a focus on important larger initiatives. While working as a team having software that supports robust communication and integrations is necessary, I find it overkill for personal daily use, quick tracking, and reflection over time.&lt;/p&gt;

&lt;p&gt;I have instead followed a different practice for years, that I have shown a few people who have also found it helpful. I keep it as simple as possible with a few Markdown files, which I check into a private git repo.&lt;/p&gt;

&lt;p&gt;Markdown notes work well because it is quick and free form. I still occasionally take notes on paper, but I transfer things into my Markdown notes at the end of each day. I have different sections and templates that I can use for weekly, quarterly, and yearly tasks. Nothing is faster than a few bullet lists of todos for day to day work, which keeps me focused on what I need to get done each day. It also lets me quickly throw in anything that came up suddenly which took my attention, letting me see why any goals for today slipped to tomorrow.&lt;/p&gt;

&lt;p&gt;These notes eventually turn into a story and keep my history, when I reflect back on OKRs each quarter, I go through my daily standups to pull out the bigger picture &lt;!--more--&gt; of what I accomplished for the quarter. My history of work I have captured for the last 3 or 4 years this way and can extract patterns and themes, and if I want to accomplish something over the long term build it into the short term goals to see improvements over the years.&lt;/p&gt;

&lt;p&gt;I have refined this system for a few years, and it has been the best thing at keeping me on track and focused for a long while now. I hope you can find parts of it helpful as well.&lt;/p&gt;

&lt;h3 id=&quot;tools-for-markdown-standups&quot;&gt;Tools for Markdown Standups&lt;/h3&gt;

&lt;p&gt;The tools to support this kind of process is super light weight, which is one of the advantages. I can even edit these through the GitHub editor on my phone if I am away from my computer.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Text Editor (&lt;a href=&quot;http://macdown.uranusjr.com/&quot;&gt;MacDown&lt;/a&gt;)&lt;/li&gt;
  &lt;li&gt;Git (I check in files when I make significant changes or at least weekly)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;project-structure&quot;&gt;Project Structure&lt;/h3&gt;

&lt;p&gt;Having a bit of structure with multiple files and sections of your daily file can be helpful. I keep all of these notes in a single folder, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;notes&lt;/code&gt;, which I then have several documents that are active.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;notes&lt;/code&gt; (directory)
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;archive&lt;/code&gt; (directory)&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;standup.md&lt;/code&gt; (the primary file I work on daily)&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;scratch.md&lt;/code&gt; (a staging area for notes on anything that often gets moved to official documentation, project ReadMes, emails, google docs, code, etc. at a later time)&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;okr_q1.md&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;okr_q2.md&lt;/code&gt;, etc. (my quarterly goals and notes reflecting on them at the end of the quarter)&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;random.md&lt;/code&gt; various documents in progress, individual documents I use when staging something larger to be shared with the team when more complete.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;notes-templates&quot;&gt;Notes Templates&lt;/h3&gt;

&lt;p&gt;I keep various levels of notes in my standup file… Here is a blank template that gives the high-level structure, below I will explain a few of the sections in more detail.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-Markdown&quot;&gt;# Links

* various links
* sometimes sub link sections

# Goals

### Daily

* daily reminders
* check calendar
* share standup
* etc

### Weekly

* review Asana
* review Jira
* weekly reflection
* etc

### Monthly

* branch cleanup
* AWS usage monitoring
* etc

# Weekly Monday template
see template below, but a template to fill out each Monday

# Current

* current high level
* team goals

# Symbol Key
see template below, a key for notes

# Standup

### Monday, Oct 11th

### Tuesday, Oct 12th

### Wednesday, Oct 13th

### Thursday, Oct 14th

### Friday, Oct 15th

# History
All the previous week&apos;s standups get moved here. Then I start a clean standup.md file each year archiving the previous one. 
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;goals-section&quot;&gt;Goals Section&lt;/h3&gt;

&lt;p&gt;This section lets me keep a short list of daily, weekly, monthly todos/goals. It is a little reminder for overall pace, focus, and schedule. Often things that are recurring end up with a calendar reminder as well, but this is a quick place to check to make sure I am staying on top of things.&lt;/p&gt;

&lt;h3 id=&quot;weekly-monday-template&quot;&gt;Weekly Monday Template&lt;/h3&gt;

&lt;p&gt;At the start of each week, I share a bit of a week’s summary with the team. It is a great way to make sure people are on the same page and remind of anything out of the ordinary like Holidays in one of the countries where we operate. I fill this out each Monday and share it in our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#software-team&lt;/code&gt; slack channel.&lt;/p&gt;

&lt;p&gt;It is also a good place to call out birthdays, kudos for something well done, link to a recent post-mortem or anything else that might be worth bringing a little extra attention.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-Markdown&quot;&gt;### Weekly Monday Template

Notes for Software Team about the coming week
* Dev first-responder: &amp;lt;name&amp;gt;
* PM Release Coordinator: &amp;lt;name&amp;gt;
* Out of office / Holidays / Vacation
   * TZ, US, Russia
   * Dev vacays etc...
* Updates:
   * info about a big release or test planned for the week
   * dates and times for brown bags, etc
* Articles:
   * some good links
   * from recent articles
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;current-section&quot;&gt;Current Section&lt;/h3&gt;

&lt;p&gt;The current section lets me add some higher level goals and keep them just above my weekly standup notes, so I keep them in mind. Current helps make sure my day to day work is building towards some of the larger themes and that daily tasks don’t always push out larger goals.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-Markdown&quot;&gt;### Current

* Follow up on Allware teams gathering
* Reschedule web and Android quarterly walkthroughs
* Update system architecture diagrams
* Improve ETL pipeline process
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;symbol-key&quot;&gt;Symbol Key&lt;/h3&gt;

&lt;p&gt;I like to be able to check things off my list so to speak, these symbols I try to apply to my daily standup todos so I can see the status. Often if something doesn’t get a checkmark, it gets copied into the next day, so I can continue to make progress against it.&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;### Symbol Key

✓ : done  
☯ : progress  
♺ : continuation from previous day  
⚠ : notice, missed, didn&apos;t get to it, problems
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;daily-standup-section&quot;&gt;Daily Standup Section&lt;/h3&gt;

&lt;p&gt;Daily Standup, is the workhorse of the system. The section where I put in what I plan to do for a day, but also adjust it to what I am doing during the day. At the start of the week, I write down a few of the things I know for the full week. As the week progresses, there is more and more detail and often my days get fully booked up with work scheduled.&lt;/p&gt;

&lt;p&gt;We share our text standups in a Slack channel, so I try to share mine at the end of most days or at the end of the week. I used to share at start of day, but to often unexpected items end up changing my plans.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-Markdown&quot;&gt;# Standup

### Monday, Oct 11th

* meetings: Stakeholders, PM Weekly, Squad IPM, Squad Retro, 1:1/Name
* post weekly software template
* tickets
   * links to Asana tickets
   * links to Jira tickets
* permissions fix (this is likely something that came up mid-day)
* resolve Solr issues (this is likely something that came up mid-day)
* research X
* documentation for Y (link to doc)  

### Tuesday, Oct 12th

* meetings: Call Center, Another Squad IPM, New Country Launch, Data Team Standup
* tickets
   * links to Asana tickets
   * links to Jira tickets
* recruiting followups
* schedule interviews
* review large PR (this is likely something that came up mid-day)

### Wednesday, Oct 13th

* etc
* ...
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;final-thoughts&quot;&gt;Final Thoughts&lt;/h3&gt;

&lt;p&gt;The markdown system is fast and flexible. Whenever I need a new section, to quickly organize thoughts, I just create a header and start writing, when the section is no longer useful in the day to day I pull it out.&lt;/p&gt;

&lt;p&gt;Sometimes you have a full day planned out, and something blows up, and the entire standup gets updated, that is OK it is quick and easy to roll today’s tasks into tomorrow section. The ability to very quickly see an overview and modify it is really where this wins. I can’t move 6 tickets in Asana and Jira from one day to another without about 20 clicks, but I can do that in seconds in my Markdown file.&lt;/p&gt;

&lt;p&gt;Obviously, this will need to be adjusted to your needs. It has worked very well for me with few changes over the last few years. Previously, I even kept the whole file in an organization public git repo so anyone at the company could see what I was up to. The scratch file became useful to several folks, and I got PRs updating sections, which had become little references for the team. I believe the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git tips / cool Git one-liners&lt;/code&gt; sections was one that had some mileage. Also, the list of links I had built up to all sorts of resources became useful to others over time. Currently, I keep my notes in a private repo. Private notes due to me doing more management, and my notes often include thoughts and feelings of the team which I need to keep in mind, but also keep privately. A private repo lets me keep my system as useful as possible, keeping it useful to me without worrying about sharing thoughts given in confidence.&lt;/p&gt;

&lt;p&gt;If you have any suggestions or other organizational tips, feel free to share. I hope this will be helpful to a few other folks as it has been to me over the years.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Managing DB Schema & Data Changes</title>
   <link href="https://www.mayerdan.com/programming/2016/11/21/managing-rails-migrations"/>
   <updated>2016-11-21T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/programming/2016/11/21/managing-rails-migrations</id>
   <content type="html">
&lt;p&gt;As you work with a growing team and codebase, occasionally database changes will cause some surprises and issues during deploys. This seems to be especially true in the Rails world, but isn’t exclusively a Rails app problem. Rails has a simple way to handle schema changes, which works well for small additive changes, but struggles on larger changes to an existing DB.&lt;/p&gt;

&lt;p&gt;As I have worked with a few teams, it is worth calling out some issues and learnings about how to help avoid some issues.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;deleting tables&lt;/li&gt;
  &lt;li&gt;changing columns&lt;/li&gt;
  &lt;li&gt;slow and missing indexes&lt;/li&gt;
  &lt;li&gt;long running migrations&lt;/li&gt;
  &lt;li&gt;data migration vs schema changes&lt;/li&gt;
  &lt;li&gt;communication issues between data and software teams&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;team-policies&quot;&gt;Team Policies&lt;/h2&gt;

&lt;p&gt;At &lt;a href=&quot;http://offgrid-electric.com&quot;&gt;Offgrid Electric&lt;/a&gt;, we have shared github wikis with documentation, process, and policies. Over time the team has built up processes related to making database changes to our application. These policies help us avoid some off the pitfalls, while encouraging good communication. We adjust them over time, but it might &lt;!--more--&gt; be helpful for other organizations as they begin to run into any issues around database change process. Below is a partial scrubbed version of our documentation.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;schema-changes-process&quot;&gt;Schema Changes Process&lt;/h2&gt;

&lt;p&gt;Changes to the data model (adding/removing tables/columns) should involve active communication with the data team to ensure:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;New structures (tables/columns) include all of the information we might need for future analysis in a format that is conducive to performing that analysis&lt;/li&gt;
  &lt;li&gt;Structural changes/removals (tables/columns) do not break existing data pipeline processes or reports&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The following processes should help us ensure these points.&lt;/p&gt;

&lt;h3 id=&quot;process-for-adding-new-tables--columns&quot;&gt;Process for Adding New Tables / Columns&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;If a new process is being modeled in the database the Business Intelligence (BI) and Data Infrastructure (DI) teams should be actively involved in the data modeling process to ensure all of the information needed for analysis is being collected.&lt;/li&gt;
  &lt;li&gt;Migration PR’s should have a :thumbsup: from someone on the data-infrastructure team prior to merging.&lt;/li&gt;
  &lt;li&gt;The DB migration to should be merged and run on staging prior to production. If there is any possible impact to dataware house it should remain on staging for a day (so the nightly data tasks run against staging) prior to being merged and deployed to production.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;process-for-modification-or-removal-of-tables--columns&quot;&gt;Process for Modification or Removal of Tables / Columns&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;A ticket should be created in the Data Warehouse Jira project backlog. The ticket should list the proposed modifications and ask that someone confirm there are no ETL or BI processes that utilize the column/table that is being changed. Completion of this ticket should be a prerequisite for a PR.&lt;/li&gt;
  &lt;li&gt;Migration PR’s should have a :thumbsup: from someone on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;data-infrastructure&lt;/code&gt; team prior to merging.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;database-general-guidelines&quot;&gt;Database General Guidelines&lt;/h2&gt;

&lt;p&gt;Avoid adding columns with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;json&lt;/code&gt; data type.  Our data warehouse uses &lt;a href=&quot;https://aws.amazon.com/documentation/redshift/&quot;&gt;AWS Redshift&lt;/a&gt;, which does not have robust support for JSON column types.  Which means that any data that starts off as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;json&lt;/code&gt; in our DB becomes, incredibly hard to query. Since it’s hard to know what we will want to query in the data warehouse, it’s safest to assume “all of it.”  If that’s the case, then we should strongly avoid &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;json&lt;/code&gt; column types in Surge.&lt;/p&gt;

&lt;h2 id=&quot;adding-large-indexes&quot;&gt;Adding large indexes&lt;/h2&gt;

&lt;p&gt;Creating an index on a large table can be slow, if you follow the process recommended here, it won’t lock the tables during the indexing process.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;avoid locking&lt;/li&gt;
  &lt;li&gt;if you indexing will take more than 10 minutes do it outside of a deploy, following the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Modifying Large Tables&lt;/code&gt; process.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href=&quot;https://robots.thoughtbot.com/how-to-create-postgres-indexes-concurrently-in&quot;&gt;postgres concurrent indexing in Rails&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;modifying-large-tables&quot;&gt;Modifying Large Tables&lt;/h2&gt;

&lt;p&gt;Why we need a process to modify large tables. This isn’t handled well by automatically running Rails migrations on deploy.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Migration happen on deploy, any migration longer than 10 min will time out&lt;/li&gt;
  &lt;li&gt;If we take down old workers during the deploy we will have an outage while the migration finishes (this used to occur on old deploy process, but no longer should be a problem)&lt;/li&gt;
  &lt;li&gt;Large DB changes can be a bit risky and effect performance it is good to schedule them.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To avoid this we commit the migration but run it manually from a console.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Make a PR with the migration only, no code changes&lt;/li&gt;
  &lt;li&gt;Commit the migration with a leading underscore &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_&lt;/code&gt; to it won’t run automatically
    &lt;ul&gt;
      &lt;li&gt;for example &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;db/migrate/_20160817125700_add_transaction_date.rb&lt;/code&gt; vs &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;db/migrate/20160817125700_add_transaction_date.rb&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Have data team review and approve migration as recommended for all schema changes as mentioned above&lt;/li&gt;
  &lt;li&gt;After approved, schedule a time to run migration during low site traffic volume (after 10pm EAT)
    &lt;ul&gt;
      &lt;li&gt;after data team approval and schedule is set make sure to do the below process on staging at least a day before the scheduled production run&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;merge the PR to master&lt;/li&gt;
  &lt;li&gt;deploy to master, MIGRATION SHOULD NOT RUN ON DEPLOY&lt;/li&gt;
  &lt;li&gt;connect to production console, and manually execute the migration
    &lt;ul&gt;
      &lt;li&gt;rename migration and remove the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;in this example: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mv db/migrate/_20160817125700_add_transaction_date.rb db/migrate/20160817125700_add_transaction_date.rb&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;execute the migration: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bundle exec rake db:migrate&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Alternate Complex Solution:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Rarely there is a reason to bypass migrations entirely and make a change manually against the DB. In these special cases which normally means a DBA is going to perform some magic to migrate something without locking, the process is slightly different.&lt;/p&gt;

&lt;p&gt;Basically the same as above &lt;strong&gt;BUT&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;migration file should never run in production, we still want it for dev, testing, and likely staging though. In this case skip the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_&lt;/code&gt; naming part of the process mentioned above.&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-ruby 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;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;AddTransactionDate&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Migration&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;up&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;production?&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;add_column&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:account_transactions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:transaction_date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:datetime&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;execute&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;UPDATE account_transactions SET transaction_date = created_at&quot;&lt;/span&gt;
      &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;ol&gt;
  &lt;li&gt;In all non production environments, deploy and let the migration run&lt;/li&gt;
  &lt;li&gt;In production have the DBA do their magic and then insert the migration into the schema table the migration should never even execute on production
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActiveRecord::Base.connection.execute(&quot;INSERT INTO schema_migrations (version) VALUES(&apos;20130817125700&apos;)&quot;)&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;At this point merge to master and deploy. The migration should be skipped, if anything went wrong the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;unless Rails.env.production?&lt;/code&gt; should also protect against the migration accidentally running.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;data-migrations-vs-schema-migrations&quot;&gt;Data Migrations vs. Schema Migrations&lt;/h2&gt;

&lt;p&gt;Data migrations often can take far over 10 minutes, if you are iterating through and modifying a large table we have had some that take hours. This isn’t a good first for the standard Rails migration or standard deployment processes. So we looked for some other options&lt;/p&gt;

&lt;p&gt;We previously ran release tasks, kind of following the &lt;a href=&quot;https://robots.thoughtbot.com/data-migrations-in-rails&quot;&gt;thoughtbot process&lt;/a&gt;. These are basically managed rake tasks, but occasionally folks weren’t sure what had been run by whom and when. While this worked OK, we thought we could do better.&lt;/p&gt;

&lt;p&gt;Our team ended up creating &lt;a href=&quot;https://github.com/OffgridElectric/rails-data-migrations&quot;&gt;rails-data-migrations&lt;/a&gt; which is a lightweight wrapper around standard Rails migrations to create data migrations. These use the same mechanism to store the timestamp into the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;schema_migrations&lt;/code&gt; as a standard migration, but they don’t run on default &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;db:migrate&lt;/code&gt;. The data migrations are included in their own &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;db/data_migrations&lt;/code&gt; and include a generator to create new migrations (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rails generate data_migration migration_name&lt;/code&gt;).&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;data migrations don’t run by default on deploy&lt;/li&gt;
  &lt;li&gt;we can kick them off in console&lt;/li&gt;
  &lt;li&gt;they will only run once and it is easy to check if anyone has run them&lt;/li&gt;
  &lt;li&gt;you can run all missing data migrations against a environment with a single command.&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>Impacting Work</title>
   <link href="https://www.mayerdan.com/programming/2016/10/16/impacting-work"/>
   <updated>2016-10-16T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/programming/2016/10/16/impacting-work</id>
   <content type="html">
&lt;h3 id=&quot;a-job-that-will-inspire-you-daily&quot;&gt;A job that will inspire you daily&lt;/h3&gt;

&lt;p&gt;The work we do at &lt;a href=&quot;https://medium.com/@Offgrid&quot;&gt;Off Grid Electric&lt;/a&gt;, has a clear impact. The mission is inspiring and the immediate benefits we deliver are obvious and extremely rewarding.&lt;/p&gt;

&lt;p&gt;Our team is amazing, and driven to rise to a challenge. We face unique challenges operating without physical addresses,  60% network coverage, and customer support for people without internet. We take distributed work both challenges and benefits seriously, building a team and process to enjoy it’s freedoms.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.mayerdan.com/assets/img/Offgrid-Tanzania/Dan-Roof-Install-2.JPG&quot;&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;installing our system on a roof in a remote village of Karatu, Tanzania&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;We are hiring&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Our Software team is hiring the positions below. There are other non software positions &lt;a href=&quot;https://medium.com/@Offgrid&quot;&gt;as well&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://medium.com/electric-africa/sr-data-engineer-bc6c3d5d476a#.u8yqw5qh7&quot;&gt;Sr. Data Engineer&lt;/a&gt;, big data, ETL, and data infrastructure.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://medium.com/@Offgrid/surge-platform-developer-274d3b9f2a69#.9ma62xwfr&quot;&gt;Full Stack Ruby&lt;/a&gt;, APIs, integrations, and IOT.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://medium.com/@Offgrid/sr-android-engineer-fff5ac2fccc0#.6jc3z3i2t&quot;&gt;Sr. Android Engineer&lt;/a&gt;, majority of our workforce is on motorcycles with an Android.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://medium.com/@Offgrid/sr-qa-engineer-335fdf0adea3#.v0iwehsxz&quot;&gt;QA Engineer&lt;/a&gt;, test automation, team management, web/mobile testing.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;dont-take-my-word-for-it&quot;&gt;Don’t take my word for it&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;“Across sub-Saharan Africa, fully 590 million people lack access to power. And it’s a life-or-death issue: Indoor air pollution from wood stoves now kills 3.5 million people per year, more than AIDS and malaria combined.” –&lt;a href=&quot;http://www.washingtonpost.com/blogs/wonkblog/wp/2013/07/02/a-closer-look-at-obamas-7-billion-plan-to-bring-electricity-to-africa/&quot;&gt;Brad Plumer: Washington Post&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Over the years, I have learned to respect &lt;a href=&quot;https://www.gatesnotes.com/&quot;&gt;Bill Gates&lt;/a&gt; for his passionate endeavors to change the world. His &lt;!--more--&gt; recent post highlighted the importance of the work we do at Offgrid. We deliver directly on #1 and provide free power to schools helping enable #4.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;There are four objectives I think we should prioritize:&lt;/p&gt;

  &lt;ol&gt;
    &lt;li&gt;Provide everyone on earth with affordable energy without contributing to climate change.&lt;/li&gt;
    &lt;li&gt;Develop a vaccine for HIV and a cure for neurodegenerative diseases.&lt;/li&gt;
    &lt;li&gt;Protect the world from future health epidemics, which might be more infectious than Ebola and more deadly than Zika.&lt;/li&gt;
    &lt;li&gt;Give every student and teacher new tools so all students get a world-class education.&lt;/li&gt;
  &lt;/ol&gt;

  &lt;p&gt;–&lt;a href=&quot;https://www.gatesnotes.com/About-Bill-Gates/Accelerating-Innovation&quot;&gt;Accelerating Innovation with Leadership&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Another, current inescapable name in the technology world these days is Elon Musk, who is know for his passion for Solar energy.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“this handy fusion reactor in the sky, where you don’t have to do anything—it works, it shows up every day, and it produces ridiculous amounts of power.” –&lt;a href=&quot;http://waitbutwhy.com/2015/06/the-deal-with-solar.html&quot;&gt;the deal with Solar&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;“This is because the overarching purpose of Tesla Motors (and the reason I am funding the company) is to help expedite the move from a mine-and-burn hydrocarbon economy towards a solar electric economy, which I believe to be the primary, but not exclusive, sustainable solution.”  –&lt;a href=&quot;https://www.tesla.com/blog/secret-tesla-motors-master-plan-just-between-you-and-me&quot;&gt;Original Tesla Master Plan&lt;/a&gt; (recently doubled down in &lt;a href=&quot;https://www.tesla.com/blog/master-plan-part-deux&quot;&gt;Master Plan, Part Duex&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Musk clearly believes in it because he is an investor in Offgrid Electric, through his involvement with &lt;a href=&quot;http://www.solarcity.com/&quot;&gt;SolarCity&lt;/a&gt;. He recently talked of the kind of impact we are working to deliver.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“This allows you to go completely off grid. It’s analogous to the way that mobile leapfrogged landlines.” –&lt;a href=&quot;http://www.bloomberg.com/news/articles/2015-05-01/musk-says-tesla-energy-will-help-world-s-poor-leapfrog-the-grid&quot;&gt;Tesla Energy Will Help World’s Poor Leapfrog the Grid&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;offrid-electric-in-tanzania&quot;&gt;Offrid Electric in Tanzania&lt;/h1&gt;

&lt;p&gt;Our global headquarters is in Arusha Tanzania. While we are launching in other countries, Tanzania is by far the most developed part of our business. We recently rebranded Offgrid Electric’s offerings from M-Power to Zola in Tanzania.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;a href=&quot;https://www.mayerdan.com/assets/img/zola_building.jpg&quot;&gt;&lt;img src=&quot;/assets/img/zola_building_sm.jpg&quot; alt=&quot;image&quot; /&gt;&lt;/a&gt;
&lt;a href=&quot;https://www.mayerdan.com/assets/img/zola_sales.jpg&quot;&gt;&lt;img src=&quot;/assets/img/zola_sales_sm.jpg&quot; alt=&quot;image&quot; /&gt;&lt;/a&gt;
&lt;a href=&quot;https://www.mayerdan.com/assets/img/Offgrid_cottage.jpg&quot;&gt;&lt;img src=&quot;/assets/img/Offgrid_cottage_sm.jpg&quot; alt=&quot;image&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

  &lt;p&gt;Office in Arusha, TZ, field visit with Sales team, hanging out employee cottage&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We have partnerships and sell at gas stations through out Tanzania.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;a href=&quot;http://www.mayerdan.com/assets/img/zola_ad.jpg&quot;&gt;&lt;img src=&quot;/assets/img/zola_ad_sm.jpg&quot; alt=&quot;image&quot; /&gt;&lt;/a&gt;
&lt;a href=&quot;http://www.mayerdan.com/assets/img/zola_demo.jpg&quot;&gt;&lt;img src=&quot;/assets/img/zola_demo_sm.jpg&quot; alt=&quot;image&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

  &lt;p&gt;Banner ad outside gas station, Demo unit inside Total gas station&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We sell most frequently to areas with limited or no access to the power grid. Often these are more modern villages just to remote for access, but we also sell to Maasai villages, which can spread over large distances into the mountains.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;a href=&quot;https://www.mayerdan.com/assets/img/maasai village.jpg&quot;&gt;&lt;img src=&quot;/assets/img/maasai village.jpg&quot; alt=&quot;image&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

  &lt;p&gt;Panoramic view from hilltop down to base of Maasai village, we have a number of customers in the village&lt;/p&gt;
&lt;/blockquote&gt;
</content>
 </entry>
 
 <entry>
   <title>Feature Branch Staging Servers</title>
   <link href="https://www.mayerdan.com/programming/2016/07/31/feature-branch-staging-servers"/>
   <updated>2016-07-31T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/programming/2016/07/31/feature-branch-staging-servers</id>
   <content type="html">
&lt;h1 id=&quot;continuous-deployment-for-feature-branches&quot;&gt;Continuous Deployment for Feature Branches&lt;/h1&gt;

&lt;p&gt;The ideal web software deployment process lets you quickly deploy features as they are ready and tested. I previously wrote about &lt;a href=&quot;http://www.mayerdan.com/programming/2013/08/04/thoughts-on-continuous-deployment/&quot;&gt;continuous-deployment&lt;/a&gt; previously, and most of those thoughts remain. I do think that continuous deployment can have some other issues beyond automated testing that make it a challenge, in this post I want to focus more on the human feedback loop opposed to the automated one.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Human QA&lt;/li&gt;
  &lt;li&gt;Stakeholder feedback&lt;/li&gt;
  &lt;li&gt;Long running branches (large groups of related features, 3rd party integrations, etc)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We had each &lt;!--more--&gt; of the above cases, at &lt;a href=&quot;http://offgrid-electric.com/&quot;&gt;Offgrid-Electric&lt;/a&gt;. All the cases point towards a staging environment. In normal feature development flow is a time where you want share your work, which should be deployed and available to some set of users. Let’s explore how a single staging doesn’t solve everything.&lt;/p&gt;

&lt;h1 id=&quot;staging-servers&quot;&gt;Staging Servers&lt;/h1&gt;

&lt;p&gt;The first thing people often do for QA and stakeholders is setup a staging server. When a feature is complete, you can merge it to staging and deploy for feedback. While this flow works fine for a small number of features and developers, it runs into issues as you scale the team.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Staging has a mix of features that are in various points of QA or getting feedback from stakeholders.&lt;/li&gt;
  &lt;li&gt;The code changes start to pile on top of each other, and can’t be cleanly applied to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;master&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Entangled features and iterations of fixes now can’t be released until all that has been merged into the staging branch are stakeholder &amp;amp; QA approved.&lt;/li&gt;
  &lt;li&gt;Long running branches that won’t be ready for weeks, can’t be merged in at all, where do you get feedback for those?&lt;/li&gt;
  &lt;li&gt;When QA is working on testing one feature they are often running into bugs for another feature complicating testing and debugging.&lt;/li&gt;
  &lt;li&gt;Training? We have a lot of internal tools that can’t be released until users are informed and trained on the new system. The feedback loop on training, can block features for weeks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At this point sometimes folks setup multiple staging servers or use tools like &lt;a href=&quot;http://xip.io/&quot;&gt;xip.io/&lt;/a&gt; to share developers boxes. These solutions are bandaids for and only solve small pieces of the problem. In the end, you want to be able to take any branch and dynamically create a deployed environment and keep it around as needed. This gets beyond the simple continuous deployment of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dev&lt;/code&gt; branch to the staging server and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;master&lt;/code&gt; to production. How do you scale continuous deployment?&lt;/p&gt;

&lt;h1 id=&quot;feature-branch-staging&quot;&gt;Feature Branch Staging&lt;/h1&gt;

&lt;p&gt;With the cloud and related tools, it isn’t much harder to go from continuous deployment of staging and production to dynamic branch based staging servers. We do this with Docker and AWS tools, but there are many ways to automate configuring an entire environment and deployment. I will cover some of the challenges a bit later, as there are a number of “gotchas”.&lt;/p&gt;

&lt;h3 id=&quot;what-does-it-look-like&quot;&gt;What does it look like?&lt;/h3&gt;

&lt;p&gt;A developer starts a feature branch from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;master&lt;/code&gt;. They work on the feature say &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;feature/new_cool_page&lt;/code&gt;, and check it into git. The team CI (Continuous Integration) server is already running the tests on each commit. At some point, the developers are ready to share and get external feedback from stakeholders, PM team, and QA. The developers can run some command &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;create_branch_staging&lt;/code&gt;. Which will stand up a new server deployment using their branch and return a URL based on the branch name &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;new_cool_page.staging-domian.com&lt;/code&gt;. From then on future git pushes can deploy to the feature branch if CI finds a matching staging environment, or you can continue to deploy to the feature staging server manually.&lt;/p&gt;

&lt;p&gt;Once you have a feature on a viewable staging branch, there can be a lot of feedback and discussion among the team. Often PRs with comments from devs will link to issues on the feature staging server. When everything is finally ready to go, you merge the branch to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;master&lt;/code&gt; and deploy to production. Destroy the feature staging environment &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;destroy_branch_staging&lt;/code&gt; and celebrate another victory!&lt;/p&gt;

&lt;p&gt;We still manage a “shared” staging server as well, this is for any features that might need to sit and “bake” with additional usage for awhile (think major library updates or infrastructure changes), alternatively in that case the feature branch is just collapsed to our old &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dev&lt;/code&gt; branch and deployed to staging.&lt;/p&gt;

&lt;p&gt;The process enabled by being able to create multiple environments dynamically solves most of the issues with a shared staging server. It significantly increased our velocity. Increased the confidence of our smaller deploys, and made it simpler to prototype and throw away ideas. Beyond the developer process. It additionally has other nice benefits.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Forcing function to fully automate your infrastructure (encouraging DevOps), basically &lt;a href=&quot;https://blog.codeship.com/immutable-infrastructure/&quot;&gt;immutable infrastructure&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;Easy to test infrastructure changes independantly from code changes (ha-proxy, Nginx config, different memory CPU and instances, etc.)&lt;/li&gt;
  &lt;li&gt;Depending on how you solve some of the data replication challenges (mentioned shortly), it is built in testing for backup and restore&lt;/li&gt;
  &lt;li&gt;Discover bottlenecks in your infrastructure earlier before production&lt;/li&gt;
  &lt;li&gt;Helps force environment simplification and understanding.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;challenges&quot;&gt;Challenges&lt;/h1&gt;

&lt;p&gt;While this sounds great, it does bring up some additional challenges from having a more traditional CI deployments to staging and production environments. It isn’t quite as simple as the prior case of deploying to single environment that was often configured by hand and had it’s data smoothed over the ages. Think about all the dependencies that go into running your app and how they relate to one another, such as how you seed your staging environment data. Here are some of the challenges we faced.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Seed Data:&lt;/strong&gt; our staging uses an old snapshot of production with additional QA data layered on top
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;our solution:&lt;/strong&gt; daily snapshots of staging. Each feature branch starts with the most recent RDS (AWS Postgres) snapshot to dynamically create a new DB. Then any migrations since the snapshot are applied on top. (We can easily pull our production snapshots as well if we need to debug production data issues)&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Multiple Data Sources:&lt;/strong&gt; Our app requires Postgres, Redis, and Solr and each needs data seeded per environment
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;our solution:&lt;/strong&gt; each data store has its individual requirements.
        &lt;ul&gt;
          &lt;li&gt;&lt;strong&gt;Postgres&lt;/strong&gt;: RDS snapshots as mentioned above&lt;/li&gt;
          &lt;li&gt;&lt;strong&gt;Solr&lt;/strong&gt;: we trigger a snapshot of staging Solr, and script creating a new Solr namespace from the snapshot for each server.&lt;/li&gt;
          &lt;li&gt;&lt;strong&gt;Redis&lt;/strong&gt;: we treat Redis as a rebuildable store, it can start empty for any given instance. We spin up a new Redis for each server, but you could leverage namespaces for this.&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Multiple Services:&lt;/strong&gt; Where do you point to your other services? By  make sure it is configurable
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;our solution:&lt;/strong&gt; By default, all feature-stagings point to our other staging services. Where they point is controlled by environment variables (&lt;a href=&quot;http://12factor.net/&quot;&gt;12-factor app&lt;/a&gt;) for any staging deployment you can alter the ENV to point to a specifically deployed alternative.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Additional Cost&lt;/strong&gt;: You still want to be careful about the number of staging, but also how you decide to share resources.
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;our solution:&lt;/strong&gt; We approach this a few ways
        &lt;ul&gt;
          &lt;li&gt;&lt;strong&gt;shared resources&lt;/strong&gt;: you can deploy feature-staging-lite (webapp only deploy (no data sources, crons, or background workers). Instead it uses &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;shared-staging&lt;/code&gt; data sources. This is ideal for UI only changes and requires far fewer resources.&lt;/li&gt;
          &lt;li&gt;&lt;strong&gt;shared servers&lt;/strong&gt;: Beyond just sharing actual data sources. We sometimes spin up full new servers like RDS, but for Solr, we just utilize namespaces. Allowing a single Solr server to host &lt;strong&gt;ALL&lt;/strong&gt; the feature branches search indexes.&lt;/li&gt;
          &lt;li&gt;&lt;strong&gt;reduced hardware&lt;/strong&gt;: While we run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;shared-staging&lt;/code&gt; very close to production. We run far fewer web-workers, with less CPU and memory for most of our dev stacks. We can adjust if needed. Along with Docker we run multiple deployments on the same servers.&lt;/li&gt;
          &lt;li&gt;&lt;strong&gt;staging sweeper&lt;/strong&gt;: Cleaning up after staging environments, you don’t want an infinitely growing list of servers and databases sitting around. We monitor staging servers and they self-destroy after 3 days of inactivity.&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Additional Monitoring:&lt;/strong&gt; you will need to monitor some of your staging tools. For example, it’s important that things like staging sweeper keep running.
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;our solution:&lt;/strong&gt; Most of our monitoring and metrics are build with server environment names in mind.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Exception Overload:&lt;/strong&gt; Managing multiple exception streams, can be confusing.
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;our solution:&lt;/strong&gt; We keep exceptions in very clear &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;production&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;shared-staging&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;feature-staging&lt;/code&gt; streams at the very least, often including &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BRANCH_NAME&lt;/code&gt; for any metrics / alerts&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Scaling Staging:&lt;/strong&gt; At first people might be cautious using feature staging, but it becomes so useful next thing you realize you could be maxing out your staging infrastructure.
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;our solution:&lt;/strong&gt; We had to build in auto-scaling up and down based on the demand for the number of branch-staging servers. If someone deploys and there aren’t enough servers in the cluster, it adds more. It will shrink back down to size as staging servers are destroyed.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Custom Tooling:&lt;/strong&gt;  When you have a single staging server, folks just connect to it and do what they need. Now it can be hard to find where your service is running and what is going on.
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;our solution:&lt;/strong&gt; where you can avoid tooling, using things like log aggregation. You don’t need to connect to staging to view logs. If you need a console we have a script &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker:console&lt;/code&gt; which will create a docker task and connect it to your environment and SSH you over. Also, keep it simple we rely on a lot of battle tested AWS tooling.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;our-approach&quot;&gt;Our Approach&lt;/h1&gt;

&lt;p&gt;We worked with the very talented &lt;a href=&quot;https://www.trek10.com/&quot;&gt;Trek10&lt;/a&gt;, to develop our production and staging infrastructure as well as our deployment process. We leveraged a few technologies, some open source, some standard AWS tooling, and some custom scripts or services we or Trek10 developed.&lt;/p&gt;

&lt;p&gt;The basic tech stack:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;CloudFormation:&lt;/strong&gt; This lets us manage all the related resources and services together as one.
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;ECS&lt;/strong&gt;: Easy way to have multiple services and tasks in our environment.
        &lt;ul&gt;
          &lt;li&gt;&lt;strong&gt;Docker&lt;/strong&gt;: This makes it easy to package up our app and dependencies… Easy to CI as well as scale out as multiple services and tasks.&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;RDS&lt;/strong&gt;: We can create copies from snapshots of any given database for other branches&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Elastic-Cache:&lt;/strong&gt; Similar to RDS the tools around using Redis from elastic cache makes it super easy to manage&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;AWS-lambda:&lt;/strong&gt; If we need to do anything fancy (say scale our production DB size to something more cost effective for staging). We can script that in AWS Lambda and CloudFormation will trigger our events and wait for their results. Anything CloudFormation can’t handle out of the box, we can handle this way.&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Route-53:&lt;/strong&gt; You will need easy to integrate dynamic DNS to go this route. We are always adding and tearing down DNS names.&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;ELB:&lt;/strong&gt; Less required for feature staging but a piece we use for production high availability.&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Ruby:&lt;/strong&gt; We have some simple Ruby glue scripts we kick off to interact with all of this stuff ;)&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As for the detailed specifics, we will save that for another post.  Hopefully, something our partners &lt;a href=&quot;https://www.trek10.com/&quot;&gt;Trek10&lt;/a&gt; will cover in much more detail soon.&lt;/p&gt;

&lt;h3 id=&quot;the-perfect-staging&quot;&gt;The Perfect Staging&lt;/h3&gt;

&lt;p&gt;In a perfect world, you would never have to think about this stuff and environments would just appear when and how you need them. With no concern for the cost and complexity of getting there. That isn’t the case but depending on your project and how early you start to automate away manual tasks, it is easier than ever to get pretty close to the ideal. You need to look at your project to see if it makes sense, how often are you having a real issue with features getting in each other’s way, does your organization have significant stakeholder feedback or Human QA against features? If you haven’t shipped anything and aren’t worried about zero downtime deployment, again not worth your time. I think the cost of getting this kind of staging and development process was too high a few years ago, but it can be built out for many projects in a cost effective manner with enormous benefits. Is it the holy grail, no, but it is something I would have a hard time living without anymore.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Research, Proof of Concept, Pilot template</title>
   <link href="https://www.mayerdan.com/management/2016/01/17/research-project-template"/>
   <updated>2016-01-17T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/management/2016/01/17/research-project-template</id>
   <content type="html">
&lt;h1 id=&quot;learning-project-template&quot;&gt;Learning Project Template&lt;/h1&gt;

&lt;p&gt;When we start a small project to learn and answer questions for the organization. It is good if we can be clear about the type of project and the specific learning goals of the project. This template aims to help create a path towards focused learning.&lt;/p&gt;

&lt;h1 id=&quot;project-name&quot;&gt;Project Name&lt;/h1&gt;

&lt;p&gt;Start with a small paragraph describing the project. List the specific limitations, simplifications, and assumptions in the project. What is roll out / testers / pilot usage if planned, or approach to research if no initial rollout planned. State explicitly if this is a research, proof of concept, or pilot project.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;timeline for research / POC / Pilot
    &lt;ul&gt;
      &lt;li&gt;timebox: before check-in / extension&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;involved contributors&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Research&lt;/strong&gt;: This often is documenting, searching, having discussions, proposing plans and alternative approaches. Research may include small code samples that explore or verify if something works as expected or is a viable option. Most often is used to drive the discussion further.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;POC (Proof of concept)&lt;/strong&gt;: This would be a step deeper than pure research. It tries to proof out an idea in a way that answers goals of the project under more real-world conditions. POCs aren’t necessarily for end users, often just for the developers. POCs, unlike pure research should be trying to work with real world business data/conditions opposed to lab conditions of research only.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pilot&lt;/strong&gt;: Pilots are often intended for release to a small group of initial users. While pilots are tests of the underlying code and architecture, it is also often testing the business value, UX, performance and usability in real-world conditions. A pilot can be ongoing for a time to allow close contact with a small group of users to iterate towards a solution.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Current Status Quo&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We are probably doing this project because we have no solution, or the existing solution has known flaws. Let’s give a brief statement on the current state, which could be as simple as “does not exist.”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Goals&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A list of defined things we are trying to learn from the project. Below are just some examples that might be relevant to different levels of learning projects.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Research
    &lt;ul&gt;
      &lt;li&gt;What questions are we trying to answer?&lt;/li&gt;
      &lt;li&gt;What decisions do we want to be able to discuss or make as a result of this research?&lt;/li&gt;
      &lt;li&gt;Can we identify and reduce the scope of unknowns with this research?&lt;/li&gt;
      &lt;li&gt;Is it faster, cheaper, better to do any of the proposed solutions?&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;POC
    &lt;ul&gt;
      &lt;li&gt;What are we proving out? (or what existing challenge does this solve)&lt;/li&gt;
      &lt;li&gt;What alternatives are we skipping to focus on this POC?&lt;/li&gt;
      &lt;li&gt;What does this replace if there is an existing solution?&lt;/li&gt;
      &lt;li&gt;Level of effort:
        &lt;ul&gt;
          &lt;li&gt;Did the POC level of effort match expectations?&lt;/li&gt;
          &lt;li&gt;Cost more / less?&lt;/li&gt;
          &lt;li&gt;expose flaws in our assumptions?&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;Should we continues to pursue this approach?&lt;/li&gt;
      &lt;li&gt;We halted the POC early due to learning X, causing us to abandon the approach. Often this result would still answer many of the questions above.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Pilot
    &lt;ul&gt;
      &lt;li&gt;We are working to prove this works for our users?&lt;/li&gt;
      &lt;li&gt;The new way is better by X% than the existing way.&lt;/li&gt;
      &lt;li&gt;Does this work in a real world scenario?&lt;/li&gt;
      &lt;li&gt;Are there flaws in our assumptions before we fully build this out for all users?&lt;/li&gt;
      &lt;li&gt;Can we support this at scale?&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>Configuring a new OS X Yosemite development machine</title>
   <link href="https://www.mayerdan.com/programming/2015/12/10/configuring-osx-yosemite"/>
   <updated>2015-12-10T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/programming/2015/12/10/configuring-osx-yosemite</id>
   <content type="html">
&lt;p&gt;This is a update from how I setup &lt;a href=&quot;http://www.mayerdan.com/programming/2013/12/01/configuring-osx-mavericks-dev-machine/&quot;&gt;OS X Mavericks&lt;/a&gt; years ago. Basically, it is more for my own reference, but that post seemed to be helpful to some others.&lt;/p&gt;

&lt;p&gt;These are the steps I took to setup a new OS X 10.10.3 (Yosemite) machine for development. I have heard on twitter that each time you setup a new machine you get 1% better and at this rate you can become good at it in a few hundred years! Ha while that seems to be true I am getting happier with the process and I am now at least trying to document it. This post won’t serve as a rich tutorial on the configuration process, but should give some ideas on good ways to setup your own machine or ideas on how to fix some common gotchas I ran into with my setup.&lt;/p&gt;

&lt;p&gt;I know this is a very opinionated approach without much explanation, so if you are new to development I recommend finding richer tutorial as a this is largely intended for my own documentation.&lt;/p&gt;

&lt;h1 id=&quot;new-instructions&quot;&gt;new instructions&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;same xcode-select –install&lt;/li&gt;
  &lt;li&gt;install brew manually from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http://brew.sh/&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;brew install gpg&lt;/li&gt;
  &lt;li&gt;keys slightly different &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gpg -o key.tar.gz --decrypt latest_keys.tar.gz.gpg&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brew install zsh&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;same verify &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;zsh&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;got your keys working clone your dotfiles so your terminal is better: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git clone git@github.com:danmayer/dotfiles.git&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;install rbenv &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git clone https://github.com/sstephenson/rbenv.git ~/.rbenv&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;install postgres http://www.postgresql.org/download/macosx/ or http://postgresapp.com/&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;initial-setup-steps&quot;&gt;Initial setup steps&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;verify full disk encryption&lt;/li&gt;
  &lt;li&gt;update password on account / machine (if one was previously configured)&lt;/li&gt;
  &lt;li&gt;install all OS X updates&lt;/li&gt;
  &lt;li&gt;install X code (this and the updates can be slow get this started as soon as you can)
    &lt;ul&gt;
      &lt;li&gt;cmd line &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xcode-select --install&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;while-xcode-installs&quot;&gt;While Xcode installs&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;install chrome&lt;/li&gt;
  &lt;li&gt;setup chome sync so it will pull your plugins like &lt;a href=&quot;https://lastpass.com/&quot;&gt;lastpass&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;while X code installs install &lt;a href=&quot;http://www.irradiatedsoftware.com/downloads/?file=SizeUp.zip&quot;&gt;size up&lt;/a&gt;, or your prefered window manager&lt;/li&gt;
  &lt;li&gt;install dropbox, get sync started&lt;/li&gt;
  &lt;li&gt;get your old ssh keys (or you can make new ones and upload them to services like github)
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tar czf key.tar.gz .ssh/&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gpg -c key.tar.gz&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;scp it over from other machines or encypted file sync via Dropbox&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gpg -o key.tar.gz --decrypt latest_keys.tar.gz.gpg&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tar xvzf key.tar.gz&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;download and install &lt;a href=&quot;https://www.virtualbox.org/wiki/Downloads&quot;&gt;virtualbox&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;download and install &lt;a href=&quot;http://aquamacs.org/&quot;&gt;aquamacs&lt;/a&gt; (or your favorite editor)&lt;/li&gt;
  &lt;li&gt;download and install &lt;a href=&quot;https://justgetflux.com/&quot;&gt;flux&lt;/a&gt; for light filtering&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;after-xcode-install-brew&quot;&gt;After Xcode, install brew&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;fix various SSL errors like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Faraday::SSLError: SSL_connect returned=1 errno=0 state=unknown state: certificate verify failed&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;you need to run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brew install openssl&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brew link openssl --force&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;re-install your rubies! Don’t forget to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rbenv rehash&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;install &lt;a href=&quot;http://www.iterm2.com/#/section/home&quot;&gt;iterm2&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;install zsh brew install zsh
    &lt;ul&gt;
      &lt;li&gt;change default shell to zsh &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chsh -s /bin/zsh&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;verify your running zsh after opening new terminal &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ps -o comm $$&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;install Pygments &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo easy_install Pygments&lt;/code&gt; #my dotfiles use Pygments&lt;/li&gt;
  &lt;li&gt;git clone &lt;a href=&quot;https://github.com/danmayer/dotfiles&quot;&gt;dotfiles&lt;/a&gt;, or however you sync your environment
    &lt;ul&gt;
      &lt;li&gt;sync your non git dot files encypted via scp or pgp dropbox sync (I keep .env_extras that contains some sensative keys so it is not stored on github)&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;configure VPN (if needed)&lt;/li&gt;
  &lt;li&gt;install &lt;a href=&quot;http://mouapp.com/&quot;&gt;Mou&lt;/a&gt; (markdown editor)&lt;/li&gt;
  &lt;li&gt;install campfire client (&lt;a href=&quot;http://propaneapp.com/&quot;&gt;Propane&lt;/a&gt;)&lt;/li&gt;
  &lt;li&gt;install IM client, &lt;a href=&quot;https://adium.im/&quot;&gt;Adium&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;configure &lt;a href=&quot;https://support.google.com/a/answer/48758?hl=en&quot;&gt;Adium for gchat&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brew install qt&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;at the time required HEAD &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brew update; brew install qt --HEAD&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gem install thrift -v &apos;0.9.0&apos; -- --with-cppflags=&apos;-D_FORTIFY_SOURCE=0&apos;&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brew install Mysql55&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;
        &lt;p&gt;initialize mysql:&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;mysql_install_db --verbose --user=`whoami` --basedir=&quot;/opt/boxen/homebrew/Cellar/mysql55/5.5.30/&quot; --datadir=/opt/boxen/homebrew/var/mysql55 --tmpdir=/tmp
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gem install mysql2 -v &apos;0.2.18&apos; -- --with-mysql-dir=/opt/boxen/homebrew/opt/mysql55/&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brew install imagemagick&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;get-your-code&quot;&gt;Get your code&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;begin to git clone all the things you love&lt;/li&gt;
  &lt;li&gt;start to rbenv install all the Ruby versions you need&lt;/li&gt;
  &lt;li&gt;bundle install all the things&lt;/li&gt;
  &lt;li&gt;verify tests are passing or apps start&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;updates-since-installed&quot;&gt;Updates Since installed&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://twitter.com/obfuscurity/status/403776103929888768&quot;&gt;rbenv to get latest ruby 1.9.3-p484 thanks @obfuscurity&lt;/a&gt;&lt;/p&gt;

  	cd /opt/boxen/rbenv/plugins/ruby-build
    &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;  git pull; cd
  rbenv install 1.9.3-p484
  rbenv global 1.9.3-p484
  gem install bundler
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;configure default global node.js &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nodenv global v0.10.21&lt;/code&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;possible-improvements&quot;&gt;Possible Improvements&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Should I install emacs starter kit or start over? I need to fix my emacs setup&lt;/li&gt;
  &lt;li&gt;Automated way to reset all the OS X default apps to open file types?&lt;/li&gt;
  &lt;li&gt;Automate some of the app downloads and installs&lt;/li&gt;
  &lt;li&gt;Automate some of the common ruby version installs&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>Developer Team Onboarding Playbook</title>
   <link href="https://www.mayerdan.com/management/2015/08/25/dev-team-onboarding-playbook"/>
   <updated>2015-08-25T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/management/2015/08/25/dev-team-onboarding-playbook</id>
   <content type="html">
&lt;blockquote&gt;

  &lt;p&gt;photo credit: &lt;a href=&quot;https://www.flickr.com/photos/opensourceway/5537336155&quot;&gt;opensourceway&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;developer-team-onboarding-playbook&quot;&gt;Developer Team Onboarding Playbook&lt;/h1&gt;

&lt;p&gt;I’m working on building out a plan to improve our engineering onboarding at &lt;a href=&quot;http://offgrid-electric.com/&quot;&gt;Offgrid Electric&lt;/a&gt;. As we start to add engineers to the team at a faster pace, the cost of not getting onboarding right increases. I’m sure the playbook is slightly different from team to team, and I think it is important to note that I am developing this playbook with a distributed team in mind.&lt;/p&gt;

&lt;p&gt;I reached out to some mentors, past &amp;amp; current teammates, &amp;amp; twitter to develop a plan. I would love further suggestions, if you feel I am missing something or breaking &lt;a href=&quot;http://chadfowler.com/blog/2014/01/19/rule-one-of-management-first-do-no-harm/&quot;&gt;Rule One of Management: First, Do No Harm&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This is my current draft on building out an onboarding playbook:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Have a home for onboarding plans, checklists, &amp;amp; materials
    &lt;ul&gt;
      &lt;li&gt;different pieces in git, wiki, or project management software&lt;/li&gt;
      &lt;li&gt;execute / improve against playbook with each hire&lt;/li&gt;
      &lt;li&gt;plans are available to everyone including the new hires to help improve&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Checklists
    &lt;ul&gt;
      &lt;li&gt;accounts to be setup (and who is responsible for setting them up)&lt;/li&gt;
      &lt;li&gt;email lists, groups, permissions to be granted (and who is responsible for setting them up)&lt;/li&gt;
      &lt;li&gt;things that need to happen in the first day, first week, first month&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Send welcome / Team Introduction email&lt;/li&gt;
  &lt;li&gt;Assign a team buddy
    &lt;ul&gt;
      &lt;li&gt;helps answer questions on company, process, code, &amp;amp; culture&lt;/li&gt;
      &lt;li&gt;partner for first staging deployment&lt;/li&gt;
      &lt;li&gt;partner for first production deployment&lt;/li&gt;
      &lt;li&gt;many teams make use of a buddy, the concept is covered well in, &lt;a href=&quot;https://zapier.com/learn/the-ultimate-guide-to-remote-working/remote-employee-evaluation/&quot;&gt;Make New Teammates Feel Welcome from the Start&lt;/a&gt;, of Zapier’s guide to remote work.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Day one plan
    &lt;ul&gt;
      &lt;li&gt;1-1 with hiring manager, verify basic accounts setup together&lt;/li&gt;
      &lt;li&gt;get on team communication tools (Slack, Asana, etc)&lt;/li&gt;
      &lt;li&gt;git commit to documentation&lt;/li&gt;
      &lt;li&gt;time to install dev tools&lt;/li&gt;
      &lt;li&gt;1-1 with buddy&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Initial getting started on the project
    &lt;ul&gt;
      &lt;li&gt;Follow project setup instructions, sending PRs for any missing, dated, or confusing information along the way&lt;/li&gt;
      &lt;li&gt;tests passing&lt;/li&gt;
      &lt;li&gt;development server success&lt;/li&gt;
      &lt;li&gt;mobile app builds (mobile devs)&lt;/li&gt;
      &lt;li&gt;mobile app can connect to dev server (mobile devs)&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Initial smaller quick win tickets for the first week(s)&lt;/li&gt;
  &lt;li&gt;Intro Getting Started Pairing Schedule
    &lt;ul&gt;
      &lt;li&gt;a small number of schedule pairing events in the first few weeks&lt;/li&gt;
      &lt;li&gt;scheduled pairing time with buddy&lt;/li&gt;
      &lt;li&gt;scheduled pairing time with team mates, help with knowledge sharing and getting to know folks&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Schedule initial check-ins
    &lt;ul&gt;
      &lt;li&gt;with team lead 1 week, 2 weeks, 1 month, 3 months&lt;/li&gt;
      &lt;li&gt;with buddy&lt;/li&gt;
      &lt;li&gt;with business partners (if sensible)&lt;/li&gt;
      &lt;li&gt;with other key players (QA, etc)&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Scheduled items should be added to new dev’s calendar&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thanks &lt;a href=&quot;https://twitter.com/mariagutierrez&quot;&gt;@mariagutierrez&lt;/a&gt;, &lt;a href=&quot;https://twitter.com/glv&quot;&gt;@glv&lt;/a&gt;, &lt;a href=&quot;https://twitter.com/tempo&quot;&gt;@tempo&lt;/a&gt;, &lt;a href=&quot;https://twitter.com/sonia_pdx&quot;&gt;@sonia_pdx&lt;/a&gt;, &amp;amp; &lt;a href=&quot;https://twitter.com/davetron5000&quot;&gt;@davetron5000&lt;/a&gt; for ideas and inspiration.&lt;/p&gt;

&lt;p&gt;If you have ideas, please share in the comments or hit me up on twitter &lt;a href=&quot;https://twitter.com/danmayer&quot;&gt;@danmayer&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Storing and deploying graphite configs</title>
   <link href="https://www.mayerdan.com/programming/2015/08/23/storing-and-deploying-graphite-configs"/>
   <updated>2015-08-23T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/programming/2015/08/23/storing-and-deploying-graphite-configs</id>
   <content type="html">
&lt;h1 id=&quot;storing--deploying-graphite-configs&quot;&gt;Storing &amp;amp; deploying graphite configs&lt;/h1&gt;

&lt;p&gt;When I worked &lt;a href=&quot;https://twitter.com/livingsocialeng&quot;&gt;@livingsocialeng&lt;/a&gt; a engineer on the team &lt;a href=&quot;https://github.com/crankharder&quot;&gt;Josh Sharpe&lt;/a&gt; improved how we dealt with graphite dashboards. He wrote Rake tasks to push and pull our graphite dashboards into each project’s git repo. This made it easier to collaborate on dashboards and see the history of a dashboard. It is really handy to keep your dashboard under version control. If you don’t know why you want graphite dashboards or why it would be good to keep them in version control, checkout &lt;a href=&quot;http://matt.aimonetti.net/posts/2013/06/26/practical-guide-to-graphite-monitoring/&quot;&gt;a practical guid to graphite monitoring&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;At the time, I thought awesome, used the tasks and went back to work. As I have moved on to a new job, and am working on graphite dashboards. I realized I really missed the tasks. I checked around and didn’t find a gem to handle storing graphite dashboards with a project. So I looked into how to build the tasks myself, there likely isn’t a gem because it is really pretty simple.&lt;/p&gt;

&lt;h1 id=&quot;download-existing-dashboards&quot;&gt;Download existing dashboards&lt;/h1&gt;

&lt;p&gt;The format is a bit verbose, so it is best to download an existing dashboard to edit. I added a Rake task, which just pulls a dashboard so it can be edited as a template to build a new dashboard.&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;task :pull_dashboard_skeleton do
  curl -u user:pass https://graphite.host.com/dashboard/load/DashName.json &amp;gt; temp.json
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Curling an existing graph will pull down JSON &lt;!--more--&gt; that looks something 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;{
  &quot;state&quot;: {
    &quot;name&quot;: &quot;My Dash&quot;,
    &quot;defaultGraphParams&quot;: {
        &quot;width&quot;: 1024,
        &quot;from&quot;: &quot;-24hours&quot;,
        &quot;until&quot;: &quot;now&quot;,
        &quot;height&quot;: 450
    },
    &quot;graphSize&quot;: {
        &quot;width&quot;: 1024,
        &quot;height&quot;: 400
    },
    &quot;refreshConfig&quot;: {
        &quot;interval&quot;: 60000,
        &quot;enabled&quot;: false
    },
    &quot;timeConfig&quot;: {
        &quot;relativeStartUnits&quot;: &quot;hours&quot;,
        &quot;relativeStartQuantity&quot;: &quot;2&quot;,
        &quot;relativeUntilQuantity&quot;: &quot;&quot;,
        &quot;startTime&quot;: &quot;9:00 AM&quot;,
        &quot;endTime&quot;: &quot;5:00 PM&quot;,
        &quot;type&quot;: &quot;relative&quot;,
        &quot;relativeUntilUnits&quot;: &quot;now&quot;
    },
  &quot;graphs&quot;: [
        [
            [
            ],
            {
              &quot;target&quot;: [
                &quot;alias(drawAsInfinite(stats_counts.namespace.deploys), \&quot;deploys\&quot;)&quot;,
                &quot;alias(summarize(sumSeries(stats_counts.namespace.env.key.*.*),\&quot;1min\&quot;), \&quot;current something\&quot;)&quot;,
                &quot;alias(timeShift(summarize(sumSeries(stats_counts.namespace.env.key.*.*),\&quot;1min\&quot;),\&quot;1d\&quot;), \&quot;previous 24h something\&quot;)&quot;
              ],
                &quot;hideLegend&quot;: &quot;false&quot;,
                &quot;title&quot;: &quot;\&quot;something 24 hour Comparison\&quot;&quot;,
                &quot;height&quot;: &quot;420&quot;,
                &quot;width&quot;: &quot;1024&quot;,
                &quot;_salt&quot;: &quot;1433982521.214&quot;
            },
            &quot;&quot;
        ],
        [
            [
            ],
            {
              &quot;target&quot;: [
                &quot;alias(sumSeries(stats_counts.namespace.env.other.*.*), \&quot;total other\&quot;)&quot;
              ],
                &quot;hideLegend&quot;: &quot;false&quot;,
                &quot;title&quot;: &quot;\&quot;other graph\&quot;&quot;,
                &quot;height&quot;: &quot;420&quot;,
                &quot;width&quot;: &quot;1024&quot;,
                &quot;_salt&quot;: &quot;1433982521.214&quot;
            },
            &quot;&quot;
        ]
    ]
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;uploading-a-static-dashboard&quot;&gt;Uploading a static dashboard&lt;/h1&gt;

&lt;p&gt;For a static dashboard, you can make a really simple Rake task. I ended up putting it in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lib/tasks/graphite.rake&lt;/code&gt;. In the task point at where in your project you want to store the dashboard file, in this example &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config/graphite_dashboards/DashboardName.json&lt;/code&gt;. Then the task can just use curl to post that data up to your graphite server.&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;task :update_graphite_dash do
  data = File.read(&apos;config/graphite_dashboards/DashboardName.json&apos;)
  data = CGI.escape(JSON.parse(data)[&apos;state&apos;].to_json)
  state = &quot;state=#{data}&quot;
  `curl -d &apos;#{state}&apos; -u user:pass -X POST https://graphite.host.com/dashboard/save/DashboardName`
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This lets you keep any dashboards the team build in git. Allowing you to edit the JSON in your own editor which is more nicer than using the Graphite online editor. Once you have this it is easy to extend the concept even further to use data to generate dashboards.&lt;/p&gt;

&lt;h1 id=&quot;generating-dynamic-dashboards&quot;&gt;Generating dynamic dashboards&lt;/h1&gt;

&lt;p&gt;While, previously I had only pushed and pulled dashboards. I ran into cases where I really needed the same dashboard, but slightly scoped by a particular set of data. I realized now that I had the dashboard posting code inside Ruby, it would be easy to dynamically generate dashboards based on data. Again, adding another Rake task, I was able to iterate through our regions and generate region specific dashboards. The trick here, is to use ERB to allow you to embed ruby into the dashboard template. A simple trick, but very effective at letting me zoom in on any dashboard to a specific scope.&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;task update_dynamic_dashboard: :environment do
  Regions.each do |region|
    data = ERB.new(File.read(&apos;config/graphite_dashboards/DynamicRegionDash.json&apos;)).result(binding)
    data = CGI.escape(JSON.parse(data)[&apos;state&apos;].to_json)
    state = &quot;state=#{data}&quot;
    `curl -d &apos;#{state}&apos; -u user:pass -X POST http://graphite.host.com/dashboard/save/Dynamic-#{region.name}-Dashboard`
  end
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The actual JSON for the dashboard is pretty much the same, but with the region embed a few places 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; {
   &quot;state&quot;: {
      &quot;name&quot;: &quot;Dynamic-&amp;lt;%= region.name %&amp;gt;-Dashboard&quot;,
      ...
       &quot;alias(summarize(sumSeries(stats_counts.namespace.env.key.&amp;lt;%= region.name %&amp;gt;.*),\&quot;1min\&quot;), \&quot;current something by region\&quot;)&quot;,
       ...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That is all there is to it. Hopefully, this will be helpful to someone else to get their graphite dashboards under version control. It didn’t take long to google around to figure it out, but this should save time from finding the exact formats and urls needed to make things work.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Working at Off Grid Electric</title>
   <link href="https://www.mayerdan.com/programming/2015/08/10/working-at-off-grid-electric"/>
   <updated>2015-08-10T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/programming/2015/08/10/working-at-off-grid-electric</id>
   <content type="html">
&lt;p&gt;&lt;a href=&quot;https://www.mayerdan.com/assets/img/Offgrid-Tanzania/Dan-Roof-Install-2.JPG&quot;&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Some folks have asked about how I ended up at a job like &lt;a href=&quot;https://medium.com/@Offgrid&quot;&gt;Off Grid Electric&lt;/a&gt;, so I thought I would share a bit of that. I recently took a sabbatical to travel for about 5 months with my wife. While I was traveling, I was thinking about my career, the future, and what is important to me. I felt like I hadn’t been pushing myself enough lately, and I had lost some of the passion I always liked to bring into my work.&lt;/p&gt;

&lt;h3 id=&quot;tldr&quot;&gt;TL;DR&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;“Across sub-Saharan Africa, fully 590 million people lack access to power. And it’s a life-or-death issue: Indoor air pollution from wood stoves now kills 3.5 million people per year, more than AIDS and malaria combined.” –&lt;a href=&quot;http://www.washingtonpost.com/blogs/wonkblog/wp/2013/07/02/a-closer-look-at-obamas-7-billion-plan-to-bring-electricity-to-africa/&quot;&gt;Brad Plumer: Washington Post&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Stories illustrating the importance of our mission, and meeting our customers in Tanzania, helped restore a passion to my work.&lt;/p&gt;

&lt;h1 id=&quot;travel-shifts-perspectives&quot;&gt;Travel Shifts Perspectives&lt;/h1&gt;

&lt;p&gt;There is a great post by &lt;a href=&quot;http://adventure.howstuffworks.com/travel-change-you.htm&quot;&gt;how stuff works called can travel change you?&lt;/a&gt; I would say it can change you if your open to it. I was thinking about my position in the world and looking to change things up. So I started getting interested in international positions, that could improve some of the major challenges in the world.&lt;/p&gt;

&lt;p&gt;Seeing many different countries in my world definitely changed &lt;!--more--&gt; my thoughts on what were major challenges in the world:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;clean water&lt;/li&gt;
  &lt;li&gt;sufficient sewage systems&lt;/li&gt;
  &lt;li&gt;access to power&lt;/li&gt;
  &lt;li&gt;clean energy&lt;/li&gt;
  &lt;li&gt;functional mail systems&lt;/li&gt;
  &lt;li&gt;trusted government&lt;/li&gt;
  &lt;li&gt;free press&lt;/li&gt;
  &lt;li&gt;access to education&lt;/li&gt;
  &lt;li&gt;income inequality&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;a href=&quot;https://www.mayerdan.com/assets/img/Offgrid-Tanzania/Dan-Climb-1.JPG&quot;&gt;&lt;img src=&quot;/assets/img/Offgrid-Tanzania/Dan-Climb-1-sm.JPG&quot; alt=&quot;image&quot; /&gt;&lt;/a&gt;
&lt;a href=&quot;https://www.mayerdan.com/assets/img/Offgrid-Tanzania/Dan-Climb-2.JPG&quot;&gt;&lt;img src=&quot;/assets/img/Offgrid-Tanzania/Dan-Climb-2-sm.JPG&quot; alt=&quot;image&quot; /&gt;&lt;/a&gt;
&lt;a href=&quot;https://www.mayerdan.com/assets/img/Offgrid-Tanzania/Dan-Climb-3.JPG&quot;&gt;&lt;img src=&quot;/assets/img/Offgrid-Tanzania/Dan-Climb-3-sm.JPG&quot; alt=&quot;image&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

  &lt;p&gt;Climbing onto a roof to install solar panels&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For awhile, I had been a bit stuck in an echo chamber of dev tools, consumer startups, and incremental improvements on social communications. While there is cool working going on there, I was a bit burnt out on it. Looking from outside my normal communities, I realized all the issues I found most important in the world to be outside of my current focuses. I wanted to help solve big problems, I knew I was passionate about. I wanted work on something outside the frequent tech joke that resonates a bit too loudly on occasion.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“The best minds of my generation are thinking about how to make people click ads. That sucks.” –&lt;a href=&quot;http://www.fastcompany.com/3008436/takeaway/why-data-god-jeffrey-hammerbacher-left-facebook-found-cloudera&quot;&gt;Former Facebook dev &amp;amp; Cloudera co-founder Jeff Hammerbacher&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Although, there are some good &lt;a href=&quot;http://readwrite.com/2011/04/21/what-will-this-bubbles-legacy&quot;&gt;counterpoints showing the legacy of amazing tools the industry is building&lt;/a&gt;. Hilariously, I saw another version of this just today.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“Meanwhile the “best minds” of our generation work on drone kale smoothie delivery.” –&lt;a href=&quot;http://twitter.com/thomasfuchs&quot;&gt;@thomasfuchs&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;investing-your-time&quot;&gt;Investing Your Time&lt;/h1&gt;

&lt;p&gt;There are great ways to support problems you care about, but in the end support often comes down to your time or your money. While I don’t have lots of money to invest in these important issues, like &lt;a href=&quot;http://www.gatesnotes.com/Energy/Energy-Innovation&quot;&gt;Bill Gates dedicating to invest $1 billion clean energy&lt;/a&gt;, I can invest my time. I can make working on these hard problems a part of my career path. A long career dedicated to working on big problems to help focus my direction for the future.&lt;/p&gt;

&lt;p&gt;I started to think that many of the best engineers I know work at a cross section of engineering and knowing another domain extremely well. I decided I could look for work where I could leverage my software skills while learning more about a big issue I am passionate about improving. These thoughts were still forming in my head as we travel around.&lt;/p&gt;

&lt;p&gt;Around that time, I started to come across a post on my social media. I must have seen it three or four times before I decided it was something I needed to follow up on. I do know that &lt;a href=&quot;http://learntoduck.net/&quot;&gt;Micah&lt;/a&gt;, whom I had know through &lt;a href=&quot;http://www.techstars.com/&quot;&gt;@techstars&lt;/a&gt; had posted the job to Facebook. His post was the final push to motivate me to finally click through and contact &lt;a href=&quot;https://twitter.com/offgride&quot;&gt;@Off Grid Electric&lt;/a&gt;. The job post really seemed to be a great fit for what I was looking for, &lt;a href=&quot;https://medium.com/electric-africa/a-dream-job-for-someone-who-loves-adventure-d256b8d21a97&quot;&gt;a dream job for someone who loves adventure&lt;/a&gt;.&lt;/p&gt;

&lt;h1 id=&quot;know-what-you-want&quot;&gt;Know What You Want&lt;/h1&gt;

&lt;p&gt;After a bit of time talking with &lt;a href=&quot;https://twitter.com/tempo&quot;&gt;Thor Muller&lt;/a&gt;, it became clear that Off Grid Electric also satisfied other parts of a job I find important.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;We are a small, highly-distributed software team (US, Nigeria, Tanzania, Russia).&lt;/li&gt;
  &lt;li&gt;We allow remote workers&lt;/li&gt;
  &lt;li&gt;We are driven by a mission, which I find personally compelling&lt;/li&gt;
  &lt;li&gt;Flexible and travel-friendly work&lt;/li&gt;
  &lt;li&gt;I wanted to work on a project with international components&lt;/li&gt;
  &lt;li&gt;While technology is important, it is just one tool to solve problems&lt;/li&gt;
  &lt;li&gt;A long term position - I want to invest time in a company and be able to grow with it.&lt;/li&gt;
  &lt;li&gt;An opportunity to work closely with end users&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The list goes on, there was a lot to make everything seem right to me. It is good to take some time and reflect deeply on what you want, and what you want to be doing X years from now. Then find a path that can take you in that direction. I feel fortunate to have found that role.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;a href=&quot;https://www.mayerdan.com/assets/img/Offgrid-Tanzania/Mpower-Regional-Office-2.jpg&quot;&gt;&lt;img src=&quot;/assets/img/Offgrid-Tanzania/Mpower-Regional-Office-2-sm.jpg&quot; alt=&quot;image&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

  &lt;p&gt;Debugging in the Karatu regional office with Dmitry&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Look cool? More photos from my recent &lt;a href=&quot;https://goo.gl/photos/4vqLQMisFRjPJ1jQA&quot;&gt;work trip to Tanzania&lt;/a&gt;.&lt;/p&gt;

&lt;h1 id=&quot;singing-your-tune-were-hiring&quot;&gt;Singing your tune? We’re Hiring&lt;/h1&gt;

&lt;p&gt;I know other folks that are looking to make changes. If this is the kind of work that speaks to you, ping me I would love to chat. Feel free to reach out over email (danmayer&lt;at&gt;gmail) or twitter [@danmayer](http://twitter.com/danmayer).&lt;/at&gt;&lt;/p&gt;

&lt;h3 id=&quot;open-development-positions-offgrid&quot;&gt;Open Development Positions &lt;a href=&quot;https://medium.com/@Offgrid&quot;&gt;@OffGrid&lt;/a&gt;&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://medium.com/electric-africa/lead-android-engineer-d35e149745e3&quot;&gt;Lead Android Engineer&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://medium.com/electric-africa/surge-ui-engineer-c0d116f06d71&quot;&gt;User Experience Lead&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://medium.com/electric-africa/surge-platform-developer-274d3b9f2a69&quot;&gt;Ruby Generalist&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We also have &lt;a href=&quot;http://offgrid-electric.com/jobs/&quot;&gt;non-software jobs&lt;/a&gt;, if you aren’t about writing code all day ;)&lt;/p&gt;

&lt;h3 id=&quot;not-your-thing&quot;&gt;Not your thing?&lt;/h3&gt;

&lt;p&gt;Obviously, the gig won’t be for everyone. If you’re looking for a different kind of change, I should also give a shout out to &lt;a href=&quot;https://twitter.com/livingsocialeng&quot;&gt;@livingsocialeng&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Like a bigger team, more interested in consumer products at scale, &lt;a href=&quot;http://jobs.livingsocial.com/careers/departments/engineering-software-development/&quot;&gt;LivingSocial is Hiring&lt;/a&gt;, and is has many brilliant devs focused on big engineering challenges. I am not kidding, it is frequently described by developers past and present as the best job they have ever had.&lt;/p&gt;

&lt;p&gt;If neither interests you, but you’re looking for something new, feel free to get in touch. I know various teams trying to pick up Ruby, Clojure, and Mobile devs.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.mayerdan.com/assets/img/Offgrid-Tanzania/Arusha-Park-Dan-Thor-Waterfall.jpg&quot;&gt;&lt;img src=&quot;/assets/img/Offgrid-Tanzania/Arusha-Park-Dan-Thor-Waterfall-sm.jpg&quot; alt=&quot;image&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>pair programming readme toc</title>
   <link href="https://www.mayerdan.com/programming/2015/06/02/pair-programming-readme-toc"/>
   <updated>2015-06-02T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/programming/2015/06/02/pair-programming-readme-toc</id>
   <content type="html">
&lt;h1 id=&quot;pairing-around-the-globe&quot;&gt;Pairing Around the Globe&lt;/h1&gt;

&lt;p&gt;I met a number of &lt;a href=&quot;http://www.mayerdan.com/programming/2015/01/21/ruby-in-south-east-asia/&quot;&gt;Ruby devs when traveling&lt;/a&gt;. One developer I met &lt;a href=&quot;http://samnang.me/&quot;&gt;Samnang Chhun&lt;/a&gt; have been keeping in touch and wanting to pair on something. We hooked up online for a bit, while chatting we thought it would be nice if github readme’s had a nice table of contents. So we decided to hack something up real quick.&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/samnang/cb9515ffcf5b0ab03f8f.js&quot;&gt;&lt;/script&gt;

&lt;p&gt;It is kind of amazing that we can meet people and setup connections online, that much later are still growing and teaching us new things. The Ruby community has always excelled at this. Samnang posted about &lt;a href=&quot;http://samnang.me/2015/pair-with-dan-mayer/&quot;&gt;pairing on this as well&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Considering we only had a little bit of time, the hack was obviously something simple. It was fun to hack on something while catching up, and to take something from ID to usable code while on a single call. You can drag the bookmarklet into you nav bar to quickly add a TOC onto any github page.&lt;/p&gt;

&lt;p&gt;(&lt;a href=&quot;javascript:(function(){function%20tocLink%28a%2Ce%2Cn%29%7Bvar%20s%3D%27%3Cli%20class%3D%22tooltipped%20tooltipped-w%22%20aria-label%3D%22%27+e+%27%22%3E%3Ca%20href%3D%22%27+n+%27%22%20aria-label%3D%22%27+e+%27%22class%3D%22js-selected-navigation-item%20sunken-menu-item%22%3E%3Cspan%20class%3D%22full-word%22%3E%27+e+%22%3C/span%3E%3C/a%3E%3C/li%3E%22%3Breturn%20s%7Dvar%20header%3D%22%3Cli%20class%3D%27tooltipped%20tooltipped-w%27%3E%3Cstrong%3E%26nbsp%3BTable%20of%20Contents%3C/strong%3E%3C/li%3E%22%2Crows%3D%5B%5D%3B%24%28%22a%5Bclass%3Danchor%22%29.each%28function%28a%2Ce%29%7B%24anchor%3D%24%28e%29%2C%24heading%3D%24anchor.parent%28%29%2Crows.push%28tocLink%28%24heading.prop%28%22tagName%22%29.toLowerCase%28%29%2C%24heading.text%28%29%2C%24anchor.attr%28%22href%22%29%29%29%7D%29%3Bvar%20template%3D%22%3Cdiv%20class%3D%27repository-sidebar%27%3E%3Cnav%20class%3D%27sunken-menu%20repo-nav%27%20role%3D%27navigation%27%3E%3Cdiv%20class%3D%27sunken-menu-separator%27%3E%3C/div%3E%3Cul%20class%3D%27sunken-menu-group%27%3E%22+header+rows.join%28%22%22%29+%22%3C/ul%3E%3C/nav%3E%3C/div%3E%22%3B%24%28%24%28%22.repository-with-sidebar.with-full-navigation%20.repository-sidebar%22%29%5B0%5D%29.append%28template%29%3B}());&quot;&gt;Readme TOC&lt;/a&gt;)&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;a href=&quot;http://samnang.me/&quot;&gt;Samnang&lt;/a&gt; is a Rails developer currently looking for remote contracting or full time Ruby gig’s. If you are looking for someone get in touch with him.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Making A Programming Presentation</title>
   <link href="https://www.mayerdan.com/programming/2015/03/25/making-a-programming-presentation"/>
   <updated>2015-03-25T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/programming/2015/03/25/making-a-programming-presentation</id>
   <content type="html">
&lt;p&gt;I wrote this shortly after I gave my talk, but never cleaned up the draft. I cleaned it up a bit this morning and thought I should just publish it as &lt;!--more--&gt; is.&lt;/p&gt;

&lt;p&gt;I don’t think there are many posts talking about how to get into presenting at programming conferences. I doubt I’m even particularly good at it, but I still felt I might have some information to share. If you are interested in speaking at programming conferences and think you have knowledge to share, hopefully this post can help you on your path.&lt;/p&gt;

&lt;h2 id=&quot;the-git-log-of-my-rubynation-talk&quot;&gt;The “git log” of my RubyNation talk&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;I wrote a post about &lt;a href=&quot;https://techblog.livingsocial.com/blog/2013/12/17/coverband-production-ruby-code-coverage/&quot;&gt;Coverband code coverage&lt;/a&gt; on the &lt;a href=&quot;https://techblog.livingsocial.com&quot;&gt;Livingsocial techblog&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;I gave a 5 min. lightning talk at &lt;a href=&quot;http://mtnwestrubyconf.org/&quot;&gt;MountainWest RubyConf&lt;/a&gt;. (&lt;a href=&quot;http://www.mayerdan.com/coverband-lightning/&quot;&gt;slides: Coverband lightning talk&lt;/a&gt;)&lt;/li&gt;
  &lt;li&gt;I decided I wanted to expand on this subject and started seeking some help putting together my first talk proposal. Asking co-workers and online friends for help &lt;a href=&quot;http://www.mayerdan.com/programming/2014/03/25/production-code-analysis-talk-proposal/&quot;&gt;improving my proposal&lt;/a&gt;. I got lots of great feedback.&lt;/li&gt;
  &lt;li&gt;I submitted my proposal to a couple conferences I was interested in speaking at.
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;http://goruco.com/&quot;&gt;GoRuCo Conf&lt;/a&gt;, not accepted&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;http://www.rubynation.org/&quot;&gt;RubyNation&lt;/a&gt;, accepted&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;I expanded the talk to give a longer (10 min.) lightning talk at Railsconf, as part of the &lt;a href=&quot;http://www.confreaks.com/videos/3400-railsconf-living-social-lightning-talks&quot;&gt;LivingSocial Lightning Talks (video)&lt;/a&gt; sessions. (&lt;a href=&quot;http://www.mayerdan.com/coverband-railsconf/&quot;&gt;slides: Coverband Railsconf talk&lt;/a&gt;)&lt;/li&gt;
  &lt;li&gt;I expanded the talk out and gave the presentation internally as part of our LivingSocial speaker series to our developers. Asking for feedback from our team which helped me to improve the slides and talk.&lt;/li&gt;
  &lt;li&gt;I worked feedback into my presentation and gave dry runs whiled hooked up to a projector. Making sure I was comfortable with my slides and talk.&lt;/li&gt;
  &lt;li&gt;I gave my presentation at &lt;a href=&quot;http://www.rubynation.org/&quot;&gt;RubyNation&lt;/a&gt; and tried to remember what &lt;a href=&quot;http://russolsen.com/&quot;&gt;Russ Olsen&lt;/a&gt; told me, “everyone watching really wants to see you succeed.” I am sure I learned a lot in the process even if it didn’t go quite as well as I hoped.
    &lt;ul&gt;
      &lt;li&gt;final slides: &lt;a href=&quot;http://www.mayerdan.com/rubynation-production-code-analysis/&quot;&gt;Production Code Analysis: Tales of deleting 200K+ LOC&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;video of my talk (link not yet available, should be soon)&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;developing-an-idea&quot;&gt;Developing an Idea&lt;/h2&gt;

&lt;p&gt;I recommend starting with a small idea. Giving smaller lightning talks or short talks at local Ruby meetups to judge if there is enough material to go deeper and enough interest to justify more effort. Watch some presentations and see what kind of style you meshes well with you. Same goes for looking at various slides, find a style and format that you like. Write a short blog post, or even a outline for yourself to see if you have enough material to build a talk.&lt;/p&gt;

&lt;p&gt;Seek out people to help give you feedback, in all stages of the process. On the initial idea, while working on the proposal, to listen to shorter “beta” versions of your talk, and as you develop it into a full talk.&lt;/p&gt;

&lt;h2 id=&quot;problems-i-had&quot;&gt;Problems I had&lt;/h2&gt;

&lt;p&gt;Since I gave this as a few lightning talks and internal talks. I ran into various issues, I tried to learn from each of them to improve my final result.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Projectors, keep text off the edges of slides. Conference projectors could be set up poorly or blur on edges.&lt;/li&gt;
  &lt;li&gt;Bring water!&lt;/li&gt;
  &lt;li&gt;Try to present on your own laptop, you will be comfortable making changes or fixing issues.&lt;/li&gt;
  &lt;li&gt;White and grey backgrounds work better than very dark or black backgrounds on projectors that don’t have great contrast.&lt;/li&gt;
  &lt;li&gt;Try not to read from your slide notes. Speak more from your thoughts evoked from your slides (I didn’t do great at this).&lt;/li&gt;
  &lt;li&gt;Any live demos practice configuring the 2 screen setup a few times (I had an issue where the demo showed on my laptop screen but didn’t go to the projector screens).&lt;/li&gt;
  &lt;li&gt;Get good at setting up two monitors, and swapping them. Know the key commands. The screen set to be the dominate screen swapped right before my talk, and I had to sort it out&lt;/li&gt;
  &lt;li&gt;Smaller code snippets (really the audience can’t read large blocks of code)&lt;/li&gt;
  &lt;li&gt;Animated gifs! While a easy way to get a laugh. It’s still are appreciated by the audience and keeps people engaged, go for it.&lt;/li&gt;
  &lt;li&gt;Talks convey concepts and practices better than the nitty gritty of code and specifics. Details might do better in a blog post.&lt;/li&gt;
  &lt;li&gt;Get good with whatever slide presentation software you end up using. I used &lt;a href=&quot;https://github.com/imakewebthings/deck.js&quot;&gt;deck.js&lt;/a&gt; which is pretty cool, but still not as powerful as things like keynote.&lt;/li&gt;
  &lt;li&gt;Nervousness, while I learned a lot. I think I learned public speaking isn’t really my thing. I am far more comfortable writing a post or working out ideas ahead of time. I might continue to work on speaking, but in the end I doubt it is my best format for me to share ideas.&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>Cache me if you can</title>
   <link href="https://www.mayerdan.com/programming/2015/02/21/cache-me-if-you-can"/>
   <updated>2015-02-21T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/programming/2015/02/21/cache-me-if-you-can</id>
   <content type="html">
&lt;p&gt;Recently, I was looking at a few endpoints to speed up. We were expecting to send large volumes of traffic to a few URLs and wanted to make sure they were fast. While digging in, I found about 10,000 unexected requests per minute (RPM), going between one of our back end services and another. Sometimes the same internal service request was occurring multiple times in a single front end request. I thought this can’t be right, what is going on? Let’s take a look.&lt;/p&gt;

&lt;p&gt;I tracked the code issue down to something 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;def get_object_data(object_id)
  @object_data = Rails.cache.fetch(&quot;cache-key-name-#{object_id}&quot;, expires_in: 1.minutes) {
      SomeInternalApi.getCoolObject(object_id)
  }
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;At first glance it seems fine, it looks like this is a normal cache call and should never make the same external request twice during the same request, let alone the same minute. Do you see the issue yet?&lt;/p&gt;

&lt;h2 id=&quot;remember-to-take-special-care-if-you-need-to-cache-nil&quot;&gt;Remember to take special care if you need to cache nil&lt;/h2&gt;

&lt;p&gt;Looking back at the logs, I noticed this was only occurring for objects that were no longer available. A minority of requests for sure, but they would happen over and over… Because the API client was turning the 404 response into nil. Unlike the case with an object &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rails.cache.fetch&lt;/code&gt; won’t cache &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nil&lt;/code&gt;. To read a bit more about features of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rails.cache.fetch&lt;/code&gt; or how it might not match expectations read the &lt;a href=&quot;http://api.rubyonrails.org/classes/ActiveSupport/Cache/Store.html#method-i-fetch&quot;&gt;cache.fetch documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There are two places I see this error pop up:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;forgetting the nil case in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rails.cache.fetch&lt;/code&gt;, as was the case I just found&lt;/li&gt;
  &lt;li&gt;forgetting the nil case in a method with Memoization (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@var ||= getVar(id)&lt;/code&gt;)
remember that rails.cache.fetch&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;that-makes-sense-how-do-i-fix-it&quot;&gt;That makes sense, how do I fix it?&lt;/h2&gt;

&lt;p&gt;There are many ways to deal with this problem. I don’t know if I think one is better than another necessarily. Honestly, long as the caching method takes nil into account, I am happy. All of the solutions I have seen are pretty obvious when reading the code.&lt;/p&gt;

&lt;h5 id=&quot;nullobject-solution&quot;&gt;NullObject Solution&lt;/h5&gt;

&lt;p&gt;One nice solution, I’ve seen was done by &lt;a href=&quot;https://twitter.com/gfmurphy&quot;&gt;@gfmurphy&lt;/a&gt; (according to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git blame&lt;/code&gt;). It basically creates a Null Object, which can be cached. The Null Object solution looks something 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;  class DataGetter
    NullData = Struct.new()
 
    def get_slow_data_rails_cache
      @slow_data_or_nil = Rails.cache.fetch(&quot;cache_key_#{params[:id]}&quot;, :expires_in =&amp;gt; 30.minutes) {
        SomeService.slow_data(params) || NullData.new()
      }
      @slow_data_or_nil.is_a?(NullData) ? nil : @slow_data_or_nil
    end
 
    def get_slow_data_memoize
      @slow_data_or_nil ||= SomeService.slow_data(params) || NullData.new()
      @slow_data_or_nil.is_a?(NullData) ? nil : @slow_data_or_nil
    end
  end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h5 id=&quot;existence-check&quot;&gt;Existence Check&lt;/h5&gt;
&lt;p&gt;Another solution I have seen a number of times, so I don’t know who to give original credit to, I will call the existence check.&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;class DataGetter
  # this solution only works for memoization
 
  # This will not cache nils
  def get_slow_data__bad
    @slow_data_or_nil ||= SomeService.slow_data(params)
  end
 
  # This will cache nils, hooray!
  def get_slow_data__good
    return @slow_data_or_nil if defined?(@slow_data_or_nil)
    @slow_data_or_nil = SomeService.slow_data(params)
  end
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I am sure there are many other ways to handle caching for nils. I know I have seen others, but if you have a favorite way to solve this issue send it my way. If not just wanted to share that it is something to keep in mind. I have seen this issue a lot, and I am sure I have made this mistake my fair share of times. It is an easy thing to overlook, but can have fairly large consequences if nil can be an expected response.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Safer Refactoring on Legacy Systems</title>
   <link href="https://www.mayerdan.com/programming/2015/02/18/safer-refactoring-on-legacy-systems"/>
   <updated>2015-02-18T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/programming/2015/02/18/safer-refactoring-on-legacy-systems</id>
   <content type="html">
&lt;p&gt;A small refactoring project I took on turned into a crazy journey. Filled with frustration, production bugs, and quick fixes…&lt;/p&gt;

&lt;blockquote class=&quot;twitter-tweet&quot; lang=&quot;en&quot;&gt;&lt;p&gt;started one of those, &amp;quot;it will be an easy little refactoring&amp;quot;… Now I am lost in a sea of yak hair on a ship crewed by yaks… What happened?&lt;/p&gt;&amp;mdash; Dan Mayer (@danmayer) &lt;a href=&quot;https://twitter.com/danmayer/status/564940251911557120&quot;&gt;February 10, 2015&lt;/a&gt;&lt;/blockquote&gt;

&lt;p&gt;As you can see from the time between these two tweets it also, was burning away on my back burner annoying me for 8 days. I worked on other things for most of that time while it went through some PR review and QA, but it was still a task to check in on each day.&lt;/p&gt;

&lt;blockquote class=&quot;twitter-tweet&quot; lang=&quot;en&quot;&gt;&lt;p&gt;Hooray! Started as an estimated 2 hr refactoring -&amp;gt; 9 hours of coding, 1 1/2 week of QA / PR review -&amp;gt; 138 prod errors -&amp;gt; fix -&amp;gt; it&amp;#39;s live!&lt;/p&gt;&amp;mdash; Dan Mayer (@danmayer) &lt;a href=&quot;https://twitter.com/danmayer/status/568114276344336384&quot;&gt;February 18, 2015&lt;/a&gt;&lt;/blockquote&gt;

&lt;h1 id=&quot;there-has-got-to-be-a-better-way&quot;&gt;There has got to be a better way&lt;/h1&gt;

&lt;p&gt;TLDR; I did a refactoring, which I believe should have been done. After I completed the release, I realized how I could have done it safer and with less stress all around. Read the breakdown and two solutions to see a better way to handle situations like this.&lt;/p&gt;

&lt;p&gt;First let me start by saying I am very aware of &lt;a href=&quot;http://naildrivin5.com/blog/2013/08/08/responsible-refactoring.html&quot;&gt;responsible refactoring&lt;/a&gt;, which is a great &lt;!--more--&gt; thing to keep in mind. I knew when I first started this refactoring it would be a little dangerous, but decided to take it on for a few reasons.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;we had extracted the functionally of the app into a gem&lt;/li&gt;
  &lt;li&gt;the gem had made it into usage in ALL other consumer facing applications&lt;/li&gt;
  &lt;li&gt;the legacy app code was basically a duplicate&lt;/li&gt;
  &lt;li&gt;the legacy app code had fallen out date&lt;/li&gt;
  &lt;li&gt;the legacy app code was missing some features&lt;/li&gt;
  &lt;li&gt;the refactoring has been suggested since the initial creation of the gem and beyond being out of date the code methods and results were beginning to diverge.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;OK, this made me think it was worth the risk and there were risks.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;the legacy app is poorly tested&lt;/li&gt;
  &lt;li&gt;the legacy app was notoriously hard to manually test&lt;/li&gt;
  &lt;li&gt;the code wasn’t designed into the app well in the first place, it was all over the place, my final change illustrates that well &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git diff =&amp;gt; Showing  43 changed files  with 130 additions and 492 deletions.&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;while the original code was out of date and didn’t always match the results of the newer gem, we might have started to “code 
around” the issues, you know “it’s not a bug, it’s a feature.”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As &lt;a href=&quot;http://twitter.com/tcopeland&quot;&gt;@tcopeland&lt;/a&gt; said, “ha whew. Replacing lots of global-ish methods is tough”&lt;/p&gt;

&lt;h1 id=&quot;quick-breakdown-of-the-problem&quot;&gt;Quick breakdown of the problem&lt;/h1&gt;

&lt;p&gt;Since every situation is a bit different. I want to try to give a bit better idea of the problem I faced.&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;#app/controllers/application_controller.rb
include HelpfulModule

# Hey cool, it is mixed in all our controllers, lets really make this global
# we can just include a bunch of helper methods,
# and sprinkle usage of this all over the view layer as well!
helper_method :helpful_check?
helper_method :give_me_some_type_name
...# a surprisingly large set of these
helper_method :is_some_special_thing?

#app/controllers/api/various_base_controller(s).rb
include HelpfulModule

#app/controllers/modules/helpful_module.rb
module HelpfulModule
  protected

	def helpful_check?
	 !!(rand &amp;gt; 5)
	end
	
	def give_me_some_type_name
	  case
   	    when (helpful_check?) then true
        when is_some_special_thing? then false
        else true
      end
	end

	... #many more methods
	
	def is_some_special_thing?
	  return true
	end

end

#many controller and module call sites
app/controllers/modules/auth_support.rb
app/controllers/home_controller.rb
...

#many view layer call sites
app/views/cool/index.hml.erb
app/views/something/_partial.hml.erb
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;the-way-i-did-it&quot;&gt;The way I did it&lt;/h1&gt;

&lt;p&gt;I didn’t realize the code had spread so far and wide through out the app. I could thought I’d simply create the object one place in the request and then switch all the calls to it. Basically the change was something like below.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;app/controllers/application_controller.rb&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;I removed the module: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git diff =&amp;gt; -  include HelpfulModule.new&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;I removed all the various helper methods &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git diff =&amp;gt;&lt;/code&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;- helper_method :helpful_check?
- helper_method :give_me_some_type_name
...# a surprisingly large set of these
- helper_method :is_some_special_thing?
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;I added an accessor&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; def helpful_thing
   @helpful_thing ||= Gem::HelpfulModule.new(request, &quot;stuff&quot;)
 end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;I removed the module &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rm app/controllers/modules/helpful_module.rb&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I then updated all those many many call sites &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git diff =&amp;gt;&lt;/code&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;  #app/controllers/home_controller.rb
  - is_some_special_thing?
  + helpful_thing.is_some_special_thing?
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After thinking, I had made all the changes. I ran the test suite. I had a bunch of errors and failures. I missed various calls. Some places didn’t have access to the initialized object. I fixed both good and bad tests (tests which basically had hard coded expectations), and got everything passing. I looked at my diff and realized I had been all over the code, changing far more files than expected and having to fix more unexpected failures than imagined. I knew this had become riskier than initially imagined. Also the time it took me was far longer to get to reach a completed state than I thought it would. I knew that the tests in the legacy app weren’t covering the change well enough and went though manual testing myself along with related fixes. I then passed it off to the mobile team to help QA since it would effect some mobile APIs. We finally deployed it, and boom exceptions. A quick rollback and fix, deploy… All seems good a few hours later some error reports come in, with another minor issue which didn’t raise any exceptions… Another fix released. Finally, the long 8 day journey of the minor refactoring is over.&lt;/p&gt;

&lt;h1 id=&quot;the-way-i-should-have-made-the-change&quot;&gt;The way I should have made the change&lt;/h1&gt;

&lt;p&gt;I should have broken this into two steps. It would have made the initial estimate far more accurate at a few hours. It would have significantly reduced the risk. It also would have reduced the scope of where I needed to focus testing, both manual and automated to catch any unexpected changes.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Replace global methods implementation in module wrapper to simply make calls to the new gem. Initially leaving all callers as they are&lt;/li&gt;
  &lt;li&gt;After that has been successfully deployed, slowly move callers to direct calls, where it makes sense. Otherwise leave the level of abstraction as a single container for all interactions with the gem.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That change looks something 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;#app/controllers/modules/helpful_module.rb
module HelpfulModule
  protected

	def helpful_check?
	 helpful_thing.helpful_check?
	end
	
	def give_me_some_type_name
	  helpful_thing.give_me_some_type_name
	end

	... #many more methods
	
	def is_some_special_thing?
	  helpful_thing.is_some_special_thing?
	end

   private
   
   def helpful_thing
     @helpful_thing ||= Gem::HelpfulModule.new(request, &quot;stuff&quot;)
   end
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Which is obviously a much simpler and less invasive change. It is easier to reason about and test. It is something I could have completed much faster and released with more confidence.&lt;/p&gt;

&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h1&gt;

&lt;p&gt;Yes this is actually pretty common refactoring advice. It has been around nearly as long as the concept of refactoring. Make this type of refactoring in two or more steps opposed to doing it all at once. For me the point was that it is easy to forget the challenges of working with a large and legacy production system. The complexity demands additional attention both in terms of adding features and making “smaller” refactorings. A big part of any change to a large complex production system at the heart of a companies systems, should be a roll out plan. Always think of the safest way to try to move forward. Sometimes that is feature flags, A/B testing, limiting to employee users, and sometimes it is breaking up a refactoring into smaller easier and safer steps, like I should have.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Build robots with my nephew</title>
   <link href="https://www.mayerdan.com/education/2015/02/09/build-robots-with-nephew"/>
   <updated>2015-02-09T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/education/2015/02/09/build-robots-with-nephew</id>
   <content type="html">
&lt;p&gt;My nephew is interested in robots. I told him that we could build a robot together and he got really excited. Originally my plan was to get Lego mindstorms, but considering it is $350. I am hoping to find a cheaper way to test out the waters. Also, at his current age I assume he will want to keep what we build and may be a bit rough with it. I figure when he is a bit older Lego mindstorms is the way to go.&lt;/p&gt;

&lt;h2 id=&quot;anyone-know-of-simple-and-fun-robot-kits&quot;&gt;Anyone know of simple and fun robot kits?&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Nephew just turned 6&lt;/li&gt;
  &lt;li&gt;Looking for something we can put together&lt;/li&gt;
  &lt;li&gt;Simple action like pushing a button to cause the robot to drive forward for X seconds&lt;/li&gt;
  &lt;li&gt;Looking to build something my newphew could keep and play with.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If anyone has any ideas shoot me an email, comment, or tweet.&lt;/p&gt;

&lt;h2 id=&quot;thanks-current-ideas-shared-sent-and-found&quot;&gt;Thanks! Current ideas (shared, sent, and found)&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://makeymakey.com/&quot;&gt;makeymakey&lt;/a&gt; //thanks &lt;a href=&quot;http://twitter.com/marchdoe&quot;&gt;@Marchdoe&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.amazon.com/dp/B004FEXUP4/ref=wl_it_dp_o_pC_nS_ttl?_encoding=UTF8&amp;amp;colid=NT2OSIG2COH&amp;amp;coliid=I1G491QO401CBH&amp;amp;psc=1&quot;&gt;smallest solar power car kit&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.amazon.com/dp/B000WWV4MY/ref=wl_it_dp_o_pC_nS_ttl?_encoding=UTF8&amp;amp;colid=NT2OSIG2COH&amp;amp;coliid=I37DRKYOUINY3X&quot;&gt;solar race car kit&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.amazon.com/dp/B0014WO96Y/ref=wl_it_dp_o_pC_nS_ttl?_encoding=UTF8&amp;amp;colid=NT2OSIG2COH&amp;amp;coliid=I1Y9YWH8Q1VV9D&quot;&gt;tin can robot&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.amazon.com/dp/B00CAWP9YI/ref=wl_it_dp_o_pC_nS_ttl?_encoding=UTF8&amp;amp;colid=NT2OSIG2COH&amp;amp;coliid=I2CM7MFN20NNXY&quot;&gt;owi 14-in-1 robot kit&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;results&quot;&gt;Results&lt;/h2&gt;

&lt;p&gt;I ended up going with, the &lt;a href=&quot;http://www.amazon.com/dp/B0014WO96Y/ref=wl_it_dp_o_pC_nS_ttl?_encoding=UTF8&amp;amp;colid=NT2OSIG2COH&amp;amp;coliid=I1Y9YWH8Q1VV9D&quot;&gt;tin can robot&lt;/a&gt;. Which worked out great. Since he is 6 and the product is recommended for 8 and up, it was a lot more of me putting it together and him helping, but it did maintain his interest the entire time and we were able to finish the robot. I did have issues screwing nearly all of the joints together. I gave up on a number of them and ended up just taping a few pieces together with invisible tape. Overall, still worked out great, he learned some things it sparked his interest, and he asked great questions.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/soda_can_robot.jpg&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Best Tech Travel Articles</title>
   <link href="https://www.mayerdan.com/programming/2015/02/03/best-tech-travel-articles"/>
   <updated>2015-02-03T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/programming/2015/02/03/best-tech-travel-articles</id>
   <content type="html">
&lt;p&gt;I posted the &lt;a href=&quot;http://millermayersadventures.com/2015/01/24/best-articles-i-read-while-traveling/&quot;&gt;best articles I read while traveling&lt;/a&gt; and my &lt;a href=&quot;http://millermayersadventures.com/2015/01/18/reading-while-traveling/&quot;&gt;travel book list&lt;/a&gt; on our family blog. On my dev blog, I figured I could share my slightly nerdier posts.&lt;/p&gt;

&lt;p&gt;I thought it might be fun to share some of my favorite’s of the year. Many of these links come from &lt;a href=&quot;https://www.linkedin.com/in/dougramsay&quot;&gt;Doug Ramsay&lt;/a&gt;, who sends a weekly engineering update along with the best links he has seen. Besides Doug’s emails many links come from some of my awesome &lt;a href=&quot;https://techblog.livingsocial.com/&quot;&gt;LivingSocial colleagues&lt;/a&gt;. Enjoy, and remember to &lt;a href=&quot;http://www.amazon.com/gp/sendtokindle&quot;&gt;send to Kindle&lt;/a&gt; to save your eyes.&lt;/p&gt;

&lt;h5 id=&quot;programming&quot;&gt;Programming&lt;/h5&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://bost.ocks.org/mike/algorithms/&quot;&gt;Visualizing Algorithms&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.twitter.com/2015/introducing-practical-and-robust-anomaly-detection-in-a-time-series&quot;&gt;Introducing practical and robust anomaly detection in a time series&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://yannesposito.com/Scratch/en/blog/Higher-order-function-in-zsh/&quot;&gt;Higher order function in zsh&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://gist.github.com/jashkenas/cbd2b088e20279ae2c8e&quot;&gt;semantic versioning is broken&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://flowingdata.com/2014/10/15/linked-small-multiples/&quot;&gt;visually linked graphs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://the-paper-trail.org/blog/distributed-systems-theory-for-the-distributed-systems-engineer/&quot;&gt;being a distributed systems engineer&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://radar.oreilly.com/2014/07/questioning-the-lambda-architecture.html&quot;&gt;Questioning the Lambda Architecture&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.luster.io/blog/9-29-14-mobile-web-checklist.html&quot;&gt;Mobile App checklist&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.xaprb.com/blog/2014/12/08/eventual-consistency-simpler-than-mvcc/&quot;&gt;If Eventual Consistency Seems Hard, Wait Till You Try MVCC&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.codergears.com/Blog/?p=1103&quot;&gt;Doom3 is the proof that “keep it simple” works.&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://npf.io/2014/10/why-everyone-hates-go/&quot;&gt;Why Everyone Hates Go&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://zachholman.com/talk/move-fast-break-nothing/&quot;&gt;Move fast and break nothing&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.bennorthrop.com/Essays/2013/pair-programming-my-personal-nightmare.php&quot;&gt;Pair Programming - My Personal Nightmare&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://patshaughnessy.net/2014/2/10/use-an-ask-dont-tell-policy-with-ruby&quot;&gt;Use An Ask, Don’t Tell Policy With Ruby&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.wired.com/wiredenterprise/2013/04/developers-age/&quot;&gt;Like a Good Scotch, Developers Get Better With Age&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.engineyard.com/2014/gittip-open-source-grant&quot;&gt;An Open Source Grant with a Difference&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://taras.glek.net/blog/2014/02/06/cost-efficient-continious-integration/&quot;&gt;Cost Efficient Continious Integration&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://content.time.com/time/printout/0,8816,2166770,00.html&quot;&gt;Engineers on overtime fix healthcare.gov&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h5 id=&quot;career&quot;&gt;Career&lt;/h5&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://speaking.io/&quot;&gt;Tips for public speaking&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://jorendorff.github.io/hackday/2013/tutoring/&quot;&gt;What’s known about tutoring&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://dan.carley.co/blog/2014/05/21/working-late-responsibly/&quot;&gt;Working Late, Responsibly&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.theatlantic.com/business/archive/2014/10/why-germany-is-so-much-better-at-training-its-workers/381550/&quot;&gt;Why Germany Is So Much Better at Training Its Workers&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.paperplanes.de/2014/12/10/from-open-to-minimum-vacation-policy.html&quot;&gt;From Open (Unlimited) to Minimum Vacation Policy&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.threechords.org/blog/how-animated-gif-selfies-fixed-our-teams-morale/&quot;&gt;How animated gif selfies fixed our team’s morale&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.kitchensoap.com/2012/10/25/on-being-a-senior-engineer/&quot;&gt;On being a Senior Engineer&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://therealadam.com/2013/03/11/thoughts-on-being-a-senior-engineer/&quot;&gt;Adam Key’s follow up, thoughts on being a Senior Engineer&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h5 id=&quot;startups&quot;&gt;Startups&lt;/h5&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://little-startup-handbook.dojo4.com/&quot;&gt;little startup handbook&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://danwang.co/why-is-peter-thiel-pessimistic-about-technological-innovation/&quot;&gt;why peter thiel is pessimistic with tech&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://moz.com/rand/long-ugly-year-depression-thats-finally-fading/&quot;&gt;long ugly year or depression that is fading&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.ncsociology.org/sociationtoday/v21/merit.htm&quot;&gt;The Meritocracy Myth&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h5 id=&quot;interesting&quot;&gt;Interesting&lt;/h5&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.wired.com/2014/05/the-robot-car-of-tomorrow-might-just-be-programmed-to-hit-you/&quot;&gt;How the robot car of tomorrow might be programmed to hit you&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.nytimes.com/2015/01/16/opinion/the-cruel-waste-of-americas-tech-talent.html?_r=1&quot;&gt;The cruel waste of America’s tech talent&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://fivethirtyeight.com/features/lessons-from-brazils-war-on-poverty/&quot;&gt;lessons from brazil’s war on poverty&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.theatlantic.com/national/archive/2013/10/this-is-the-average-mans-body/280194/&quot;&gt;avg US mens bodies&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>Ruby in South East Asia</title>
   <link href="https://www.mayerdan.com/programming/2015/01/21/ruby-in-south-east-asia"/>
   <updated>2015-01-21T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/programming/2015/01/21/ruby-in-south-east-asia</id>
   <content type="html">
&lt;h3 id=&quot;rubyists-are-a-welcoming-community-everywhere&quot;&gt;Rubyists are a welcoming community everywhere&lt;/h3&gt;

&lt;p&gt;Some great things are going on in the Ruby communities in SE Asia. As &lt;a href=&quot;http://millermayersadventures.com/&quot;&gt;we were traveling&lt;/a&gt; I decided I would like to meet some Ruby communities along the way in other parts of the world.&lt;/p&gt;

&lt;p&gt;I reached out to &lt;a href=&quot;http://railsgirls.com/&quot;&gt;Rails Girls&lt;/a&gt;, and their organizers were amazing,
, specifically &lt;a href=&quot;http://lindaliukas.fi/&quot;&gt;Linda Liukas&lt;/a&gt;. Linda was extremely helpful putting me in touch with local organizers around the world. Rails Girls was by far the most helpful group to help get me in touch with a bunch of other great Rubyists. It was amazing to me that shooting a single email to &lt;a href=&quot;http://railsgirls.com&quot;&gt;Rails Girls&lt;/a&gt;, resulted in at least 5 organizers responding to me and putting me in touch with other contacts to help me meet up with Rubyists while traveling. Huge thanks to from Rails Girls that helped ping pong my email request around the world.&lt;/p&gt;

&lt;p&gt;I also met some organizers by searching for local Ruby user groups, and finding &lt;a href=&quot;https://groups.google.com/forum/#!forum/cape-town-ruby-brigade&quot;&gt;Cape Town Ruby Brigade&lt;/a&gt;. Specifically, wanted to thank Steve Barnett the organizer of &lt;a href=&quot;https://groups.google.com/forum/#!forum/cape-town-ruby-brigade&quot;&gt;Cape Town Ruby Brigade&lt;/a&gt;. Steve helped try to setup a meetup with me while I was in South Africa, even though it didn’t end up working out with my schedule.&lt;/p&gt;

&lt;p&gt;Everyone was extremely accommodating and happy to try to get together, when &lt;!--more--&gt; I sent out a few emails with my travel schedule. Our community is amazing that way. I talked with other developers and startups as well, but my initial connection was always through the Ruby community. It is amazing to show up in a city where you don’t know anyone, and have Rubyist welcome you to their work, apartments, cars, and favorite restaurants. &lt;a href=&quot;https://twitter.com/search?q=%23rubyfriends&amp;amp;src=typd&quot;&gt;#Rubyfriends&lt;/a&gt; who share a love for a programming language, and trust the community enough to know we are instantly with friends.&lt;/p&gt;

&lt;h3 id=&quot;my-interest-in-international-developer-communities&quot;&gt;My interest in international developer communities&lt;/h3&gt;

&lt;p&gt;As I told friends I met up with while traveling. I see developer teams shrinking while expanding their reach. I think in the future startups will increasingly want some international talent on their team. Making it easier to provide continuous support and improvement to their products across time-zones. Making it easier to build support for different languages, currencies, and communities which have different expectations in each country. As remote development gets easier and tools continue to improve, being able to launch sites and applications quickly across the globe will increasingly be a huge advantage. While this is something American developers aren’t as experienced with, my conversations with people and companies in other SE Asia show this global from start is already common in the way they work. For example the Malaysian startup, &lt;a href=&quot;http://watchovermeapp.com/&quot;&gt;Watch Over Me&lt;/a&gt;, co-founded by &lt;a href=&quot;https://twitter.com/search?q=Chin%20Xin-Ci%20&amp;amp;src=typd&amp;amp;mode=users&quot;&gt;Chin Xin-Ci&lt;/a&gt;, has their primary user base in the US, UK, and Australia (not in Malaysia). Startups that can work internationally and utilize the growing talent around the world will be ahead of the curve. I think it is how small teams will operate in the future to compete against ever growing multinational corporations, which can employ much larger teams across the globe. Seeing communities which are growing from a handful of developers to communities that are attracting &lt;a href=&quot;https://www.techinasia.com/500-durians/&quot;&gt;interest and investment from VC firms&lt;/a&gt;, is pretty cool. Developers can be a great global community which helps build a middle class across borders and unlock opportunities in countries which might not have as strong of an economy. I think that is one of the reasons software can be an amazing career.&lt;/p&gt;

&lt;h3 id=&quot;similarity-between-cambodia-and-malaysia-communities&quot;&gt;Similarity between Cambodia and Malaysia communities&lt;/h3&gt;

&lt;p&gt;While I want to talk a bit about each country specifically, it was interesting to see some of the similarities between the two communities. The size of the Ruby community is fairly small in both countries. The community is centralized around the major city of the country (Phnom Phen and Kaula Lumpur). Both communities are struggling with online payment as the government hasn’t fully supported online payment options in the country. While both countries official language isn’t English the majority of apps and sites are built in English. In both countries Android was more common, but mostly because of cheaper units, the developers I met seemed more into iPhones. In part I am guessing because the English language apps often weren’t targeting local populations (Android), instead international tech consumers (iPhone). In both countries, programming seemed to be introduced mostly at the college level, with no computer programming classes earlier in the educational system. The developers I met relied on a lot of self learning on the internet, and in the local community to continue to improve their skills. The local communities being fairly small seemed well connected both to the local community and to the international community at large. The groups I talked with all seemed much more comfortable with working across borders, languages, time zones, and currencies than most US devs. Which isn’t surprising, I just think this will give international developers a good edge in the future. Everyone I talked with seemed to love what they did and were really happy that developing software was such a international career.&lt;/p&gt;

&lt;h3 id=&quot;cambodia&quot;&gt;Cambodia&lt;/h3&gt;

&lt;p&gt;First I want to thank &lt;a href=&quot;https://c.kat.pe/&quot;&gt;Katherine Pe&lt;/a&gt;, who through &lt;a href=&quot;http://railsgirls.com&quot;&gt;Rails Girls&lt;/a&gt;, connected me with &lt;a href=&quot;http://twitter.com/samnangchhun&quot;&gt;Samnang Chhun&lt;/a&gt;. Samnang organizes the &lt;a href=&quot;http://pailin.github.io/&quot;&gt;Cambodia Ruby users group&lt;/a&gt; and is a core member of a web development meetup group called &lt;a href=&quot;http://www.sharevisionteam.org/&quot;&gt;Share Vision Team&lt;/a&gt;. Samnang organized a lunch, for me to meet some of the developers from the area. It was great to meet freelancers, organizers, and some developers from &lt;a href=&quot;http://instedd.org/ilabs/southeast-asia/&quot;&gt;InSTEDD iLabs SE Asia&lt;/a&gt;. Thanks for spending time chatting with me &lt;a href=&quot;https://twitter.com/ungsophy&quot;&gt;@ungsophy&lt;/a&gt;, &lt;a href=&quot;https://twitter.com/channaly&quot;&gt;@channaly&lt;/a&gt;, and &lt;a href=&quot;https://twitter.com/ksokmesa&quot;&gt;@ksokmesa&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/rubyists_cambodia_1_enhance.JPG&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;While out at lunch, I learned a lot about pieces of the current development community in Cambodia. I didn’t know much going in, so learning basic info like most of the community being in Phnom Penh, was interesting to me. Below is a short list of thoughts I had after visiting.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Khmer is the official language of Cambodia, &lt;a href=&quot;https://support.google.com/googleplay/android-developer/table/4419860?hl=en&quot;&gt;Google play doesn’t support Khmer language&lt;/a&gt;.
    &lt;ul&gt;
      &lt;li&gt;This means most sites and apps are in English&lt;/li&gt;
      &lt;li&gt;Hard to target older Khmer / French speaking locals&lt;/li&gt;
      &lt;li&gt;Focus is on younger people (who mostly know English) and other English speaking countries&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;small tight knit community, developers from various companies often get together&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://instedd.org/about-us/&quot;&gt;InSTEDD&lt;/a&gt; headquarters, employees many local devs. Seems to be a central role in the community.
    &lt;ul&gt;
      &lt;li&gt;InSTEDD iLab, is sponsored by &lt;a href=&quot;http://instedd.org/our-work/network/google-org/&quot;&gt;google.org&lt;/a&gt; and &lt;a href=&quot;http://instedd.org/our-work/network/the-rockefeller-foundation/&quot;&gt;Rockefeller Foundation&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;working on some awesome project’s like &lt;a href=&quot;http://instedd.org/blog/epihack-tanzania-2014/&quot;&gt;disease tracking in Africa&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;not just developers! A interdisciplinary team of public health experts, scientists, and software engineers&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Payment systems aren’t in place so many local online companies have to deal with cash (or target other countries)&lt;/li&gt;
  &lt;li&gt;a large of the community seems to be working for companies based in other countries, small number of local focused startups&lt;/li&gt;
  &lt;li&gt;There seemed to be a noticeable mobile focus in some of our conversations&lt;/li&gt;
  &lt;li&gt;Software development was regarded as a good and growing career&lt;/li&gt;
  &lt;li&gt;The developer community seems most active on twitter&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;malaysia&quot;&gt;Malaysia&lt;/h3&gt;

&lt;p&gt;The community in Malaysia was amazing, it was clearly growing fast and had great government support. I first want to thank &lt;a href=&quot;https://twitter.com/winstonyw&quot;&gt;Winston Teo&lt;/a&gt;, who organizes the &lt;a href=&quot;http://www.meetup.com/Singapore-Ruby-Group/&quot;&gt;Singapore Ruby User Group&lt;/a&gt; and &lt;a href=&quot;http://www.reddotrubyconf.com/&quot;&gt;Red Dot Ruby Conf&lt;/a&gt;. I was put in touch with Winston through Rails Girls, who put me in touch with &lt;a href=&quot;http://joshteng.com/&quot;&gt;Josh Teng&lt;/a&gt;, who became my guide into Malaysia’s developer community. Josh is co-founder of &lt;a href=&quot;http://www.televate.io/&quot;&gt;televate.io&lt;/a&gt; and teaches a beginner web development class using Ruby called &lt;a href=&quot;codedivision.my&quot;&gt;Code Division&lt;/a&gt;. The class is being run in collaboration with the &lt;a href=&quot;http://www.mymagic.my/&quot;&gt;Malaysian Global Innovation and Creativity Centre (MaGIC)&lt;/a&gt;. It was great to get to meet the Code Division class and many people involved in &lt;a href=&quot;https://www.facebook.com/magic.cyberjaya&quot;&gt;MaGIC&lt;/a&gt;. Magic seems to be the center of the Malaysian tech startup scene. I actually just missed the &lt;a href=&quot;http://academy.mymagic.my/magic-startup-career-fair/&quot;&gt;MaGIC startup career fair&lt;/a&gt;, which had 70+ startups involved and &lt;a href=&quot;http://www.jasonshen.com/2014/three-days-magic-malaysias-emerging-startup-community/&quot;&gt;sounds like it was a great event&lt;/a&gt;. I got to spend a little more time with the community in Kuala Lumpur (KL), so I learned a bit more about the community there.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/rubyists_malaysia_3.jpg&quot; alt=&quot;image&quot; /&gt;
&lt;img src=&quot;/assets/img/rubyists_malaysia_1.jpg&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;MaGIC operations and former board member, &lt;a href=&quot;https://www.linkedin.com/in/khailee&quot;&gt;Khailee Ng&lt;/a&gt;, is the 500 startups SE Asia managing partner
    &lt;ul&gt;
      &lt;li&gt;I know &lt;a href=&quot;https://twitter.com/davemcclure&quot;&gt;@davemcclure&lt;/a&gt; from his talks with &lt;a href=&quot;http://www.techstars.com/&quot;&gt;techstars&lt;/a&gt; back in the day. Also, from &lt;a href=&quot;500 startups&quot;&gt;http://500.co/&lt;/a&gt;, investing in a few friends startups.&lt;/li&gt;
      &lt;li&gt;I find the 500 startups interest in SE Asia especially encouraging for the startup and developer community there.&lt;/li&gt;
      &lt;li&gt;Prior to starting a fund specifically aimed at SE Asia, 500 startups had already invested in a number of COs from the area&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;It was great to meet some people working at MaGIC like Imran Sulaiman, Dr Lau Cher Han to help support the developer community.&lt;/li&gt;
  &lt;li&gt;Thanks for chatting with me &lt;a href=&quot;http://www.dhendyferdian.com/&quot;&gt;Dhendy Ferdian&lt;/a&gt;, who was working too much ;), on various projects&lt;/li&gt;
  &lt;li&gt;Amazing government support, much of which is funneled through MaGIC
    &lt;ul&gt;
      &lt;li&gt;Developers could get re-imbursed for iphones, github, &lt;a href=&quot;https://codeclimate.com/&quot;&gt;code climate&lt;/a&gt;, and other developer tools&lt;/li&gt;
      &lt;li&gt;Government has helped build many developer training programs through &lt;a href=&quot;http://www.mymagic.my/magic-academy/&quot;&gt;MaGIC Academy&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;Government is funding scholarships making MaGIC academy affordable to nearly everyone.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Small set of Ruby developers around 50 devs in KL
    &lt;ul&gt;
      &lt;li&gt;The &lt;a href=&quot;http://www.codedivision.my/&quot;&gt;Web Development Bootcamp&lt;/a&gt; is aiming to double the number of Ruby developers in the city&lt;/li&gt;
      &lt;li&gt;Malay and SE Asia startups have a big demand for more Ruby developers in this area&lt;/li&gt;
      &lt;li&gt;advanced schools aimed at leveling up junior developers and exposing them to more complicated systems.&lt;/li&gt;
      &lt;li&gt;programs to place Jr. developers in apprenticeships at companies with established developers&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;local population mostly have androids, but tech community is into iPhones&lt;/li&gt;
  &lt;li&gt;Google play does support the official language (Malay), but most sites and app still focus on English.&lt;/li&gt;
  &lt;li&gt;A growing startup scene, &lt;a href=&quot;http://grabtaxi.com/&quot;&gt;GrabTaxi&lt;/a&gt;, &lt;a href=&quot;http://shopprapp.com/&quot;&gt;ShopperApp&lt;/a&gt;, &lt;a href=&quot;https://www.imoney.my/&quot;&gt;iMoney&lt;/a&gt; with support from government, investors, and mentors.&lt;/li&gt;
  &lt;li&gt;small startups like &lt;a href=&quot;http://goget.my/&quot;&gt;GoGet&lt;/a&gt;, a task-rabbit like service. I met the CTO, Fun Wei Tan Tai, at MaGIC.&lt;/li&gt;
  &lt;li&gt;The government is into some protectionist policies seemingly &lt;a href=&quot;https://www.techinasia.com/malaysia-crackdown-on-uber/&quot;&gt;cracking down on Uber&lt;/a&gt;, while helping push the local GrabTaxi. It was fun to get to try the home grown, GrabTaxi service while around KL.&lt;/li&gt;
  &lt;li&gt;a fun loving community, into the techie lingo, memes, and seemingly SV culture.&lt;/li&gt;
  &lt;li&gt;Online payment systems aren’t fully supported. Many companies have to deal with cash or focus on countries with established online payments&lt;/li&gt;
  &lt;li&gt;many developers work for companies out of the country (often in Singapore)
    &lt;ul&gt;
      &lt;li&gt;although compared to Cambodia, a larger percentage seems to be working for home grown companies&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;The developer community seems most active on FB&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thanks one more time to &lt;a href=&quot;https://twitter.com/search?q=Chin%20Xin-Ci%20&amp;amp;src=typd&amp;amp;mode=users&quot;&gt;Chin Xin-Ci&lt;/a&gt; co-founder of &lt;a href=&quot;http://watchovermeapp.com/&quot;&gt;Watch Over Me&lt;/a&gt;, for taking the time to chat with me about her company’s growth and struggles as a startup. It was awesome to get such good insight into a Malaysian startup.&lt;/p&gt;

&lt;p&gt;I really enjoyed helping with Ruby code reviews for the &lt;a href=&quot;codedivision.my&quot;&gt;Code Division bootcamp class&lt;/a&gt;, as well as chatting with the teacher and students over dinner. A really big thank you goes out to &lt;a href=&quot;http://joshteng.com/&quot;&gt;Josh Teng&lt;/a&gt;, who showed me a great time around KL and introduced me to many amazing people. It was a blast to see where you work, your apartment, and get driven around town by a local Rubyist showing off an awesome startup city. Thanks!&lt;/p&gt;

&lt;h3 id=&quot;final-thoughts&quot;&gt;Final thoughts&lt;/h3&gt;

&lt;p&gt;I heard a lot about Singapore, when reaching out to the Ruby community in SE Asia and in person. I wish my travels had taken me through Singapore as it seems like the programming and startup hub of SE Asia from what I heard. Hopefully I can make a trip to visit the community in Singapore in the future.&lt;/p&gt;

&lt;p&gt;It was interesting to get to see some of the amazing growth happening in Cambodia and Malaysia. Overall I was impressed and the visit left me feeling energized and excited about the Ruby community. Happy with how close and supportive the global Ruby community can be. I am not sure if this post will be very useful to anyone else, but it was nice to reflect on my thoughts and remember some of the people and conversations. If nothing else, it is good to be able to once again thank everyone who helped connect me to various developers, organize meetups, and take the time to talk with me. It was really a pleasure to get to know the communities and people. I think it will be very exciting to watch as SE Asia continues to grow on the tech scene and to see partnerships between SE Asian and US companies. I hope I can help some of the folks I met along the way in the future, and I hope to get a chance to visit again. In fact, I hope I can work with some of the people I met sometime in the future.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How to improve profit from tourists for small international businesses</title>
   <link href="https://www.mayerdan.com/tips/2014/12/18/improving-tourist-profits"/>
   <updated>2014-12-18T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/tips/2014/12/18/improving-tourist-profits</id>
   <content type="html">
&lt;p&gt;As we have traveled for months we often have to rely on online reviews. We are forced to as we often show up in cities with no contacts on the ground and plans to see some attractions. This means when it comes to hotels, food, ATMs, taxis, bars, and other activities we turn to the net. It seems many smaller hotels and restaurants don’t know how to leverage the internet to increase their business. For instance we are in &lt;a href=&quot;http://www.tripadvisor.com.my/Restaurants-g303867-Elmina_Central_Region.html&quot;&gt;Elmina, Ghana and it only has 1 restaurant according to TripAdvisor&lt;/a&gt;, which I am here to tell you is not true.&lt;/p&gt;

&lt;p&gt;Some little B&amp;amp;B and restaurants do become active with basic online presence and often tell us about massive growth in their business. It becomes so important they protect there online reputations, diligently responding to any complaints and trying to improve on common customer issues. So how can you attract international travelers, with a low amount of effort? I thought I should share what I consider to be the basic steps for having an online presence that tourists can find.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Have at least a basic website&lt;/li&gt;
  &lt;li&gt;Ensure correct location information on your establishments on mapping sites&lt;/li&gt;
  &lt;li&gt;Ensure correct additional information and photos of your establishments pages&lt;/li&gt;
  &lt;li&gt;For lodging that includes &lt;!--more--&gt; a restaurant ensure both hotel and food listings&lt;/li&gt;
  &lt;li&gt;For lodging, include an option to pay with credit card&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let’s dive a bit more into the details of the list.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Have at least a basic website.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Your business has to have a website, even if very basic. A single page will do, you can consider managing a google places or facebook page as a website if you don’t know how to create your own. It must list contact information (phone, email, physical address), accurate map location (a link to the correct location on a online map), and links to any other relevant online presence you officially manage (facebook, TripAdvisor, etc).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ensure correct location information on your establishments on mapping sites&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I can’t count the number of times we WANTED to visit a restaurant and could not find it. TripAdvisor has many restaurants with inaccurate location information. Google maps can’t find the business at all. The official website doesn’t link to any map or even have an address that shows up when entered into online map services.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;add your location or fix the location of your establishment on google maps, bing maps, apple maps, mapquest&lt;/li&gt;
  &lt;li&gt;verify the location information is correct on popular review sits like TripAdvisor&lt;/li&gt;
  &lt;li&gt;if you are a hotel, B&amp;amp;B, check location information on hotels.com, agoda, priceline, or where ever else you have previously accepted reservations from&lt;/li&gt;
  &lt;li&gt;if you have a facebook page add or correct your location information.&lt;/li&gt;
  &lt;li&gt;make sure your website links to a valid address on a online map service like google maps, if your address does not yet work on the maps link to direct latitude and longitude on the map which can ensure the pinpoint is in the correct location.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Ensure correct additional information and photos of your establishments pages&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Maintain your online pages, adding photos, ensuring correct information (address, phone number, website, map locations). I would recommend maintaining at least these 3 pages:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.tripadvisor.com/GetListedNew&quot;&gt;TripAdvisor&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://support.google.com/business/answer/2911778?hl=en&quot;&gt;Google Local&lt;/a&gt; (google places, google plus, google maps all seem to fall under the name google local for business)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.facebook.com/help/104002523024878&quot;&gt;Facebook page&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;For lodging that includes a restaurant ensure both hotel and food listings&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If the establishment is both a hotel and has a restaurant open to anyone, you &lt;a href=&quot;http://www.tripadvisor.co.za/ShowTopic-g1-i12105-k7806366-Can_a_pub_be_listed_as_a_hotel_restaurant_and_attraction-TripAdvisor_Support.html&quot;&gt;need two listings on TripAdvisor&lt;/a&gt;. One for the hotel and one for the restaurant. Even if the restaurant doesn’t have a distinctly different name, it should be &lt;a href=&quot;https://www.tripadvisorsupport.com/hc/en-us/articles/200614647-Restaurant-listing-guidelines&quot;&gt;listed as HOTEL NAME Restaurant&lt;/a&gt; for the second food listing. This will let travelers find food in cities like Elmina where most of the dinning establishments are attached to lodging. So go submit a &lt;a href=&quot;http://www.tripadvisor.co.uk/GetListedRestaurant&quot;&gt;second restaurant listing now&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For lodging, include an option to pay with credit card&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the last item and it is really a bonus point, 1-4 are far more important than this last item. If the establishment offers overnight accommodations, either work with a service AirBNB, Agoda, or any others to have a way to accept online payments via credit cards. Travelers frequently end up in situations where getting enough cash can be difficult. Also, having online reservations that can accept credit card payments through your own website is acceptable, but be careful as I personally don’t always trust small business websites to handle credit card information and often prefer to book through a 3rd party for that reason. I have seen some hotels try to accept credit cards via email or over http non secure internet connections which isn’t OK. If you can’t work with any of the services at least make sure you can accept a credit card in person even if you need to charge an additional credit card fee.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It really is that easy&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That’s it, by having a website and managing a few brand pages, many lost tourists who are looking to buy things will come knocking at your door. Besides a bit of time and the cost of internet access, these resources are basically free. The best part is you will get more customers and they will be happier, and require less help. Being able to find information about location, hours, and prices online will reduce the time you spent answering the same questions from tourists calling it. There is a lot more you can do online and here might be ways to spend money on smart online advertising, but if your in a small tourist town you can probably get by, with the tips above. Happy customers will help do the rest for you.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Coding while traveling</title>
   <link href="https://www.mayerdan.com/programming/2014/11/15/coding-while-traveling"/>
   <updated>2014-11-15T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/programming/2014/11/15/coding-while-traveling</id>
   <content type="html">
&lt;p&gt;I set off to travel for about 5 months, during this time I wouldn’t be working. Of course since I actually really love to program, I planned on writing some code while traveling around. My old Mac book was dated, heavy, and starting to have some issues charging (as well as a 45 minute battery life). So I started to think about the machine I would bring with me on my travels. After a bit, of consideration the decision was between a Chromebook or Mac book Air.&lt;/p&gt;

&lt;p&gt;The Macbook has nearly all the advantages, but they are expensive and a theft target. A target to the point that some people I been traveling with hide the fact that they have macs (cases, stickers, etc). To cover the cost I looked into a older used Air, but I couldn’t find a good one at the time. Since we are spending a lot on the trip and spending time on my computer wasn’t the trip’s goal I was still interested in the Chromebook.&lt;/p&gt;

&lt;p&gt;Any Chromebook has the advantage of being extremely cheap and having amazing battery life. If it is broken, lost or stolen you can buy one or have one shipped to nearly any country for a reasonable price. In the end I went with a Samsung Chromebook. I bought it a few months before my trip to make sure &lt;!--more--&gt; I would be pretty happy using it.&lt;/p&gt;

&lt;p&gt;I knew developing on the Chromebook wouldn’t be at all like I was used to, but I thought it would still work out well for me to write some blog posts, edit some photos, learn some new libraries, and work on some of my Open Source Software projects.&lt;/p&gt;

&lt;p&gt;Previously, I had been teaching some people how to program. Since everyone had different machines I ended up teaching with each student using &lt;a href=&quot;https://www.nitrous.io/&quot;&gt;Nitrious.io&lt;/a&gt;. Since Nitrous has worked so well for me in the past, I planned to do much of my development using that while traveling. Nitrous has a Chromebook app, which is almost great it has a few bugs related to copy and paste that becoming ridiculously frustrating.&lt;/p&gt;

&lt;p&gt;Beyond Nitrious, I planned on also writing code using a variety of other methods.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Github online text editor&lt;/li&gt;
  &lt;li&gt;SSH to my EC2 machine&lt;/li&gt;
  &lt;li&gt;SSH to my Digital Ocean machine&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I figured I had a good set of options, and I am quite comfortable using Emacs over a screen session on a SSH connection. Before starting my trip at home my Chromebook development plan went great, I was very happy with the situation, while it wasn’t as good as a Mac, I figured it would good enough during my trip. If you look at my development methods above, you will easily see they all suffer from the same problem. They rely on having a working internet connection. Which wasn’t a problem at all before I traveled ;)&lt;/p&gt;

&lt;p&gt;No worries, I had heard how everywhere in Asia was leaps and bounds ahead of the US for internet ;) Also, rumors of cheap 4G data SIM cards where available in nearly all countries… The problem with that theory is that it doesn’t match the kind of travel I’ve been doing, namely travel on the cheap. We are also, often in remote areas far from major cities. While Asia often has great high speed internet it was never good at a $30 a night hostel;)&lt;/p&gt;

&lt;p&gt;While we do have international data with T-mobile, which has been amazing and made keeping in touch easy. The coverage is spotty and often we are limited to edge speeds. If you are out of the cities you have no access. The same issue applies to buying a SIM card, which is only helpful when you are near cities. I am currently writing this post in a tiny town in Uganda with a view of a rainy Nile river, which has 30 people sharing a single 4G connection.&lt;/p&gt;

&lt;p&gt;I didn’t think having good access to the internet would be such a problem, but it has been continually difficult to find a decent connection. My backup plan for this was local text editors and recent Chromebook apps like Chome Development Kit. While interesting the backup plan hasn’t been very useful.&lt;/p&gt;

&lt;p&gt;First, I end up wanting the net to pull various JS libraries, CSS, or look up solutions. The other issue is that I don’t want to built a pure JS application. I want to build an app that interacts with 3rd party APIs, or a simple server back end. Both of which quickly put me back in the situation where I need net to build anything interesting. While it has been fun to play with a few D3 javascript visualization scripts locally, it hasn’t really let me work on the kind of projects I was hoping to play with while traveling.&lt;/p&gt;

&lt;p&gt;I assumed between good internet and occasionally spending some time on local development I would be pretty satisfied. In the end, that hasn’t been the case. I do regret going with the Chromebook and if I could do it all again I would have gotten a Mac book Air. I am sure, I would have spent a bit too much time on the computer, but I would have been able to keep up with my skills better. Since working on development projects hasn’t worked out. I have had time to read a large amount, as well as spend a good amount of time writing. The Chromebook as a distraction free text editor, is hard to beat. Without net or the ability to do much else, I have been able to write a surprising amount.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Final thoughts on the Chromebook&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you do get a Chromebook I can say the older version didn’t have the horsepower to be that great at photo editing. Any large number of DSLR images slow it to a crawl. My wife has the newer Samsung Chromebook 2, while it doesn’t have a built in SD card reader, adding a USB reader will give you a much more usable travel laptop. If it wasn’t for wanting to develop offline a bit, I actually think the Chromebook is a pretty ideal long term travel machine. With all the wireless features turned off. I can get a solid 7 hours out of the machine, which gives me a number of usages between charges. It is cheap, fast to boot, and can jump online faster than my Mac can search for WiFi networks in reach.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>C Extension Notes</title>
   <link href="https://www.mayerdan.com/ruby/2014/07/24/c-extension-notes"/>
   <updated>2014-07-24T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/ruby/2014/07/24/c-extension-notes</id>
   <content type="html">&lt;p&gt;These are unorganized notes from building a c extension, I found it in the drafts folder and figured it might as well be set free.&lt;/p&gt;

&lt;h1 id=&quot;building-a-ruby-gem-with-a-c-extension&quot;&gt;Building a Ruby Gem with a C extension&lt;/h1&gt;

&lt;h3 id=&quot;notes&quot;&gt;Notes&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://qiita.com/yui-knk/items/92b0537fa1d5dd5ebc83&quot;&gt;C level events, including RUBY_EVENT_COVERAGE&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://tenderlovemaking.com/2009/12/18/writing-ruby-c-extensions-part-1.html&quot;&gt;writing C extensions: part 1&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://tenderlovemaking.com/2010/12/11/writing-ruby-c-extensions-part-2.html&quot;&gt;writing C extensions: part 2&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://blog.firmhouse.com/wrapping-up-a-c-library-for-ruby-it-s-actually-pretty-easy&quot;&gt;wrapping a c-library for ruby is easy&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.ruby-doc.org/stdlib-1.9.3/libdoc/coverage/rdoc/Coverage.html&quot;&gt;Coverage Documentation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://blog.x-aeon.com/2012/12/13/the-ruby-c-api-basics/#TRCAB_Calling&quot;&gt;Ruby C extensions: basics&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://java.ociweb.com/mark/NFJS/RubyCExtensions.pdf&quot;&gt;Ruby C extensions: Slides&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://clalance.blogspot.com/2011/01/writing-ruby-extensions-in-c-part-11.html&quot;&gt;Ruby C extensions: blocks&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.jcoglan.com/2012/07/29/your-first-ruby-native-extension-c/&quot;&gt;Your first Ruby native extension: C&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://clalance.blogspot.com/2011/01/writing-ruby-extensions-in-c-part-8.html&quot;&gt;Working with Ruby strings in C&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://yard.ruby-doc.org/stdlib-2.1.0/Num2int.html&quot;&gt;Working with Ruby numbers in C&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://stackoverflow.com/questions/10210624/how-to-convert-a-string-taken-out-from-a-ruby-array-into-a-c-c-string&quot;&gt;Converting Ruby Strings to C Strings&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;examples&quot;&gt;Examples&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/tmm1/stackprof/blob/master/ext/stackprof.c&quot;&gt;Stackprof.c&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://raw.githubusercontent.com/SamSaffron/fast_stack/060f15dc68d2102f73f3302833b37fde357d3cde/ext/fast_stack/fast_stack.c&quot;&gt;fast_stack.c&lt;/a&gt; (older simpler version)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/SamSaffron/fast_stack/blob/060f15dc68d2102f73f3302833b37fde357d3cde/lib/fast_stack.rb&quot;&gt;FastStack.rb&lt;/a&gt; (older simpler version)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;gotchas&quot;&gt;Gotchas&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;If you are compiling locally over and over, it appears that the .o files from install gems will stick around. You need to gem uninstall the native gem and reinstall it to pick up your changes.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;related-examples&quot;&gt;Related Examples&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/SamSaffron/fast_stack&quot;&gt;C ext fast stacks, includes line numbers&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;https://github.com/SamSaffron/fast_stack/blob/master/lib/fast_stack.rb&quot;&gt;examples parsing frames&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/tmm1/stackprof&quot;&gt;tmm1’s stackprofiler&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/tmm1/rblineprof&quot;&gt;tmm1’s lineprof, line number profiler&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;https://github.com/tmm1/rblineprof/blob/master/ext/rblineprof.c&quot;&gt;this without timing looks like what I need&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;perf-progress&quot;&gt;Perf Progress&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ab -n 100 -c5 http://admin.lsdev.co:80/&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;No Coverband&lt;/p&gt;

&lt;p&gt;Time taken for tests:   3.980 seconds /  3.999 seconds&lt;/p&gt;

&lt;p&gt;Ruby Coverband&lt;/p&gt;

&lt;p&gt;Time taken for tests:   31.489 seconds / 29.064 seconds&lt;/p&gt;

&lt;p&gt;Ruby Coverband C Ext V1&lt;/p&gt;

&lt;p&gt;Time taken for tests:   14.679 seconds / 12.033 seconds&lt;/p&gt;

&lt;p&gt;Ruby Coverband C Ext V2 with Regex&lt;/p&gt;

&lt;p&gt;Time taken for tests:   6.131 seconds / 6.321 seconds&lt;/p&gt;

&lt;h5 id=&quot;pl-app&quot;&gt;PL app&lt;/h5&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ab -n 50 -c5 http://pipeline.lsdev.co:80/pipeline/cities&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;No Coverband&lt;/p&gt;

&lt;p&gt;Time taken for tests:   31.347 seconds / 31.298 seconds&lt;/p&gt;

&lt;p&gt;Ruby Coverband&lt;/p&gt;

&lt;p&gt;(moved to, so slow &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ab -n 10 -c5 http://pipeline.lsdev.co:80/pipeline/cities&lt;/code&gt;, then multiplied by 5 to keep the numbers in line)
Time taken for tests:   336.53 seconds / 331.89 seconds&lt;/p&gt;

&lt;p&gt;Ruby Coverband C Ext V2 with Regex (back to the default 50 requests)&lt;/p&gt;

&lt;p&gt;Time taken for tests:   42.785 seconds / 43.343 seconds&lt;/p&gt;

&lt;h3 id=&quot;c-code-to-debug&quot;&gt;C code to debug&lt;/h3&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;    rb_eval_string(&quot;puts &apos;self is:&apos;&quot;);
    rb_funcall(rb_mKernel, rb_intern(&quot;puts&quot;), 1, self);
    rb_eval_string(&quot;puts &apos;klass is:&apos;&quot;);
    rb_funcall(rb_mKernel, rb_intern(&quot;puts&quot;), 1, klass);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;warnings-i-need-to-fix&quot;&gt;Warnings I need to fix&lt;/h3&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;    make
    compiling coverband_ext.c
	coverband_ext.c:44:23: warning: incompatible pointer types passing &apos;void (VALUE, VALUE, VALUE, ID, VALUE)&apos; to parameter of type &apos;rb_event_hook_func_t&apos; (aka &apos;void (*)(rb_event_flag_t, VALUE, VALUE, ID, VALUE)&apos;) [-Wincompatible-pointer-types]
    rb_add_event_hook(trace_line_handler_ext, RUBY_EVENT_LINE, 0);
	                      ^~~~~~~~~~~~~~~~~~~~~~
	/opt/boxen/rbenv/versions/1.9.3-p448/include/ruby-1.9.1/ruby/ruby.h:1427:45: note: passing argument to parameter &apos;func&apos; here
	void rb_add_event_hook(rb_event_hook_func_t func, rb_event_flag_t events,
	                                            ^
	coverband_ext.c:45:15: error: use of undeclared identifier &apos;bindval&apos;
    rb_iv_set(bindval, &quot;@tracer_set&quot;, Qtrue);
	              ^
	coverband_ext.c:53:24: warning: incompatible pointer types passing &apos;void (VALUE, VALUE, VALUE, ID, VALUE)&apos; to parameter of type &apos;rb_event_hook_func_t&apos; (aka &apos;void (*)(rb_event_flag_t, VALUE, VALUE, ID, VALUE)&apos;) [-Wincompatible-pointer-types]
	  rb_remove_event_hook(trace_line_handler_ext);
	                       ^~~~~~~~~~~~~~~~~~~~~~
	/opt/boxen/rbenv/versions/1.9.3-p448/include/ruby-1.9.1/ruby/ruby.h:1429:47: note: passing argument to parameter &apos;func&apos; here
	int rb_remove_event_hook(rb_event_hook_func_t func);
	                                              ^
	2 warnings and 1 error generated.
	make: *** [coverband_ext.o] Error 1  
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;c-extension-code-examples&quot;&gt;C Extension Code examples&lt;/h3&gt;

&lt;p&gt;Working with ifdef to detect and deal with ruby version differences&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;    #if defined(RUBY_VM)
      #include &amp;lt;ruby/re.h&amp;gt;
      #include &amp;lt;ruby/intern.h&amp;gt;
    
      #if defined(HAVE_RB_PROFILE_FRAMES)
        #include &amp;lt;ruby/debug.h&amp;gt;
      #else
        #include &amp;lt;vm_core.h&amp;gt;
        #include &amp;lt;iseq.h&amp;gt;
        // There&apos;s a compile error on 1.9.3. So:
        #ifdef RTYPEDDATA_DATA
          #define ruby_current_thread ((rb_thread_t *)RTYPEDDATA_DATA(rb_thread_current()))
        #endif
      #endif
    #else
      #include &amp;lt;st.h&amp;gt;
      #include &amp;lt;re.h&amp;gt;
      #include &amp;lt;intern.h&amp;gt;
      #include &amp;lt;node.h&amp;gt;
      #include &amp;lt;env.h&amp;gt;
      typedef rb_event_t rb_event_flag_t;
    #endif
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;interesting-related&quot;&gt;Interesting Related&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;http://samsaffron.com/archive/2013/03/19/flame-graphs-in-ruby-miniprofiler&quot;&gt;flame graphs in miniprofiler&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;other-links&quot;&gt;Other Links&lt;/h3&gt;

&lt;p&gt;try with adding ALL the headers to the folder&lt;/p&gt;

&lt;p&gt;https://github.com/banister/binding_of_caller/tree/master/ext/binding_of_caller/ruby_headers/193&lt;/p&gt;

&lt;p&gt;https://github.com/banister/binding_of_caller/blob/master/ext/binding_of_caller/extconf.rb&lt;/p&gt;

&lt;p&gt;https://github.com/banister/binding_of_caller/blob/master/ext/binding_of_caller/binding_of_caller.c&lt;/p&gt;

&lt;p&gt;plane does this as well to work on 1.9.2 and 1.9.3&lt;/p&gt;

&lt;p&gt;https://github.com/soba1104/PLine/blob/master/ext/pline/ruby_source/1.9.3/vm_core.h&lt;/p&gt;

&lt;p&gt;good ifdef examples as I move beyond and try to get it working on 193 and 2.1&lt;/p&gt;

&lt;p&gt;https://github.com/tmm1/rblineprof/blob/master/ext/rblineprof.c&lt;/p&gt;

&lt;p&gt;good book on working on ruby core code&lt;/p&gt;

&lt;p&gt;http://ruby-hacking-guide.github.io/evaluator.html&lt;/p&gt;

&lt;p&gt;more about trace&lt;/p&gt;

&lt;p&gt;https://bugs.ruby-lang.org/issues/2565&lt;/p&gt;

&lt;p&gt;WHERE LINE NUMBS COME FROM IN 1.9.3&lt;/p&gt;

&lt;p&gt;http://rxr.whitequark.org/mri/source/vm.c?v=1.9.3-p547#888&lt;/p&gt;

&lt;p&gt;1.9.3 set trace fun&lt;/p&gt;

&lt;p&gt;http://apidock.com/ruby/Kernel/set_trace_func&lt;/p&gt;

&lt;p&gt;calling a method&lt;/p&gt;

&lt;p&gt;http://blog.x-aeon.com/2012/12/13/the-ruby-c-api-basics/#TRCAB_Calling&lt;/p&gt;

&lt;p&gt;sharing data between C and ruby
http://www.eqqon.com/index.php/Ruby_C_Extension_API_Documentation_(Ruby_1.8)&lt;/p&gt;

&lt;p&gt;ruby c cheet sheet
http://blog.jacius.info/ruby-c-extension-cheat-sheet/&lt;/p&gt;

&lt;p&gt;more on set_TRace_Func&lt;/p&gt;

&lt;p&gt;http://aphyr.com/posts/173-monkeypatching-is-for-wimps-use-set-trace-func&lt;/p&gt;

&lt;p&gt;http://ruby-doc.org/core-1.9.3/Kernel.html#method-i-set_trace_func&lt;/p&gt;

&lt;p&gt;http://t-a-w.blogspot.com/2007/04/settracefunc-smoke-and-mirrors.html&lt;/p&gt;

&lt;p&gt;on creating a proc object&lt;/p&gt;

&lt;p&gt;http://www.ruby-doc.org/core-1.9.3/Proc.html&lt;/p&gt;

&lt;p&gt;example c code&lt;/p&gt;

&lt;p&gt;https://github.com/tmm1/rbtrace/blob/master/ext/rbtrace.c&lt;/p&gt;

&lt;p&gt;extending ruby&lt;/p&gt;

&lt;p&gt;http://phrogz.net/programmingruby/ext_ruby.html&lt;/p&gt;

&lt;p&gt;Dtrace&lt;/p&gt;

&lt;p&gt;https://bugs.ruby-lang.org/issues/2565&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Advice from my grandfather</title>
   <link href="https://www.mayerdan.com/programming/2014/07/01/advice-from-my-grandfather"/>
   <updated>2014-07-01T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/programming/2014/07/01/advice-from-my-grandfather</id>
   <content type="html">
&lt;h2 id=&quot;advice-from-my-grandfather-show-up&quot;&gt;Advice from my Grandfather, “Show up”&lt;/h2&gt;

&lt;p&gt;My grandfather had some advice for me about being good at your job. I am not exactly sure when  he shared the advice with me, it might have been before college or when I visited after college right before my first job. But I think of it often –&lt;/p&gt;

&lt;h4 id=&quot;90-of-being-good-at-something-is-to-just-keep-showing-up&quot;&gt;“90% of being good at something is to just keep showing up”&lt;/h4&gt;

&lt;p&gt;Or the short version, “show up”. Faced with something hard, keep showing up and work at it. Take on the hard challenge no one else is taking on, even the dull challenges which are important occasionally. Push yourself, take on challenges just out of your comfort zone.&lt;/p&gt;

&lt;p&gt;I think in the end this is a bit related to the common advice that it takes &lt;a href=&quot;http://en.wikipedia.org/wiki/Outliers_%28book%29&quot;&gt;10,000 hours to become a expert&lt;/a&gt;. Want to get to 10,000 hours? Keep showing up and working at your craft, it can &lt;a href=&quot;http://www.newyorker.com/online/blogs/sportingscene/2013/08/psychology-ten-thousand-hour-rule-complexity.html&quot;&gt;take over 10 years&lt;/a&gt; to become an expert.&lt;/p&gt;

&lt;h3 id=&quot;showing-up-motivates-me&quot;&gt;Showing up: motivates me&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;if there is there a really important project that no one seems to find ‘cool’ enough to work on, show up&lt;/li&gt;
  &lt;li&gt;if there are unexpected issues days before a release, dig in and solve it for the release.&lt;/li&gt;
  &lt;li&gt;if the team is finished with the 90% interesting &lt;!--more--&gt; part of the project but that last mile needs to get done, show up for the team to help pull it through to the end.&lt;/li&gt;
  &lt;li&gt;If production issues are taking systems down and no one seems to remember how it works, dig in.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Showing up is important for a variety of reasons. The actions you take shape the person you are. Being ready to pitch in often, and your team will appreciate your efforts and know your willing to help get whatever job done. Your manager and your company will appreciate you for it. You will be trusted to make other good decisions because you have shown up and come through in the past.&lt;/p&gt;

&lt;h3 id=&quot;showing-up-is-a-great-way-to-learn&quot;&gt;Showing up: is a great way to learn&lt;/h3&gt;

&lt;p&gt;Want to be a leader or become one of the most knowledgable on the team? Showing up is a great way to always learn. You might not start as the best, but if you are one of the people that consistently shows up and steps in when needed, you will become one. How do you become skilled in all areas of a career, by taking on tasks and challenges when they need to be done even if you don’t know the solution.&lt;/p&gt;

&lt;p&gt;You can see knowledge spread to people who work this way, some of the junior developers co-workers have on multiple occasions stuck around to watch over the shoulders of more experience devs while they dove into and solved an emergency. Sure enough, next time similar issues came up, the junior developers who had watched before stepped in and jumped on the tasks themselves while a senior dev checked in to see if they needed help. Often solving issues before any of the more senior devs were available to help. It is amazing how much knowledge gets shared this way.&lt;/p&gt;

&lt;p&gt;If you like to learn new things showing up is always a great thing to do as well. Who wants to just get really good at solving the same problem over and over? Unknown mystery exceptions occurring a handful of times? Add monitoring, metrics, read up on the related systems, put in probes to verify what you ‘think’ you understand. Track it down and suddenly you know more about a subsystem than anyone else around, and solved a problem lurking in the system.&lt;/p&gt;

&lt;p&gt;The number of things I have learned this way has been to many to count, it is something I have always like about being a developer, there is no real way to trap me to a specific job. As software eats the world, I am learning the skills and tools to quickly make myself productive in nearly any area of a business. System security, intrusion detection, business analytics, performance, debugging, real time dashboards, big data, personalization, mobile, infrastructure… All things I have mostly learned on the job, because we needed someone to take on a new challenge.&lt;/p&gt;

&lt;p&gt;So if you want to learn a ton, be one of the best on your team, become irreplaceable at your company, expand your skill set, and become overall excellent at your job. Keep showing up, especially when no one else can help out at the moment.&lt;/p&gt;

&lt;h3 id=&quot;know-the-systems-congrats-youve-earned-the-the-tour-guide-badge&quot;&gt;Know the systems? Congrats you’ve earned the the “tour guide” badge&lt;/h3&gt;

&lt;p&gt;Once you’re a go-to to answer questions about the systems, a large part of your job is teaching all that knowledge to others on the team. As Adam keys describes in &lt;a href=&quot;http://www.confreaks.com/videos/3068-bigruby2014-developers-are-from-mars-developers-are-from-venus&quot;&gt;Developers are From Mars, Developers are From Venus&lt;/a&gt;, you will reach a tour guide level of knowledge.&lt;/p&gt;

&lt;h3 id=&quot;one-word-of-warning&quot;&gt;One word of warning&lt;/h3&gt;

&lt;p&gt;This doesn’t mean you need to work all the time. It doesn’t mean let people or business walk all over you assigning you more work than one can handle. It doesn’t mean you should always be doing the work no one else would be willing to. It does mean taking on any challenge and not ignoring important parts of the business, to only work on fun or traditional ‘developer’ projects.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Talk Proposal Help</title>
   <link href="https://www.mayerdan.com/programming/2014/03/25/production-code-analysis-talk-proposal"/>
   <updated>2014-03-25T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/programming/2014/03/25/production-code-analysis-talk-proposal</id>
   <content type="html">
&lt;p&gt;I wrote my first CFP and submitted a talk to &lt;a href=&quot;http://goruco.com/&quot;&gt;GoRuCo&lt;/a&gt;. Sadly, I wasn’t selected, but I think thing I have something worth working on. I got some additional feedback that I hadn’t received in time to update my GoRuCo proposal. I have incorporated the changes and now plan to submit my talk idea to &lt;a href=&quot;http://www.rubynation.org/&quot;&gt;RubyNation&lt;/a&gt; (6 days left of CFPs). I would love to get some additional feedback on other ways to improve the proposal or make sure it is interesting to the audience.&lt;/p&gt;

&lt;p&gt;If you have any feedback on the proposal below you can use &lt;a href=&quot;http://bit.ly/1hWVh9f&quot;&gt;draftin to submit your changes to my document&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;talk-title&quot;&gt;Talk Title&lt;/h2&gt;

&lt;p&gt;Production Code Analysis: Tales of deleting 200K+ LOC&lt;/p&gt;

&lt;h2 id=&quot;talk-description&quot;&gt;Talk Description:&lt;/h2&gt;

&lt;p&gt;In a long-lived Ruby application with many contributors, it’s common to feel like you have lost control of the codebase. Refactoring is difficult in these situations, but tools are available to help you regain the control you once had. I will cover driving refactoring based on production metrics and analysis as a solution to this problem.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Start simple: what you can learn from New Relic and other performance monitors&lt;/li&gt;
  &lt;li&gt;Fill in missing pieces with event tracking, email tracking, partial tracking, i18n locale tracking, and one-off trackers&lt;/li&gt;
  &lt;li&gt;Quickly comparing performance between two implementations&lt;/li&gt;
  &lt;li&gt;Introduce Coverband code coverage in production,  which detects unused code.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;talk-format&quot;&gt;Talk Format:&lt;/h2&gt;

&lt;p&gt;30 Min&lt;/p&gt;

&lt;h2 id=&quot;intended-audience&quot;&gt;Intended Audience:&lt;/h2&gt;

&lt;p&gt;Intermediate&lt;/p&gt;

&lt;h2 id=&quot;speaker-bio&quot;&gt;Speaker Bio:&lt;/h2&gt;

&lt;p&gt;I moved to Ruby from Java while trying to create a startup. We were funded by Techstars then O’Reilly AlphaTech Ventures, and briefly known in the Ruby community for Devver (cloud accelerated testing) and Caliper (code metrics). Failing to find a revenue stream, I joined the fast growing company LivingSocial. At LS, I have worked on internal, mobile, consumer, and platform initiatives. Currently tech lead for Consumer Deals, helping tame our mono-Rails beast. I write about code at &lt;a href=&quot;http://mayerdan.com&quot;&gt;http://mayerdan.com&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;has-this-talk-been-given-in-the-past-if-so-where&quot;&gt;Has this talk been given in the past? (If so, where?):&lt;/h2&gt;

&lt;p&gt;No, this is the first CFP I have ever written, I have been kind of scared of public speaking ;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;note:&lt;/strong&gt; Since writing this CFP I gave a portion of this talk as a 5 minute lightning talk at mountain west Ruby conf&lt;/p&gt;

&lt;h2 id=&quot;links-to-past-talks-youve-given-video-audio-etc&quot;&gt;Links to past talks you’ve given (video, audio, etc):&lt;/h2&gt;

&lt;p&gt;None. Mostly I have written about code on my blog, http://mayerdan.com. For instance, I &lt;a href=&quot;http://mayerdan.com/ruby/2013/12/17/coverband-production-code-coverage/&quot;&gt;introduced Coverband on my blog&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Other Samples:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://mayerdan.com/ruby/2012/11/11/bugs-per-line-of-code-ratio/&quot;&gt;bugs per line of code&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://mayerdan.com/programming/2014/01/29/investigating-api-tooling/&quot;&gt;investigating api tooling&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>Investigating Api Developer Tooling</title>
   <link href="https://www.mayerdan.com/programming/2014/01/29/investigating-api-tooling"/>
   <updated>2014-01-29T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/programming/2014/01/29/investigating-api-tooling</id>
   <content type="html">
&lt;h3 id=&quot;evaluating-api-development-tools&quot;&gt;Evaluating API development tools&lt;/h3&gt;

&lt;p&gt;I have been evaluating various API tools lately. In part as I develop more APIs myself for personal projects, as well as the importance they play in the systems I work on professionally. I have learned that all web projects will have some sort of API backend and multiple front ends: web, mobile apps, native desktop, internal reporting tools, etc, etc.&lt;/p&gt;

&lt;p&gt;If you can generate any sort of interesting data eventually people will want it in other formats. This has changed how I look at data sources, with data I am starting to think about API first. Many other developers are starting to focus more on internal and external APIs as well, which is why there is a variety of great tooling and competing standards growing related to it.&lt;/p&gt;

&lt;p&gt;I had been playing around with some API tools such as &lt;a href=&quot;http://apiary.io/&quot;&gt;Apiary&lt;/a&gt;, and &lt;a href=&quot;https://github.com/square/fdoc&quot;&gt;Fdoc&lt;/a&gt; on my side projects. At work, various discussions about the future of our APIs has recently become a focus. &lt;a href=&quot;http://therealadam.com/&quot;&gt;Adam Keys&lt;/a&gt;, &lt;a href=&quot;https://twitter.com/ccjr&quot;&gt;Cloves Carneiro&lt;/a&gt;, and &lt;a href=&quot;http://www.timschmelmer.com/&quot;&gt;Tim Schmelmer&lt;/a&gt; are leading the charge in having better tooling around our APIs. The discussions lead to some features we wanted to support, which caused me to start to take a much closer look at some of the API tools available. I decided to try to take notes as I dug in to research &lt;!--more--&gt; a variety of options.&lt;/p&gt;

&lt;p&gt;image courtesy of &lt;a href=&quot;http://www.flickr.com/photos/marc_smith/8924975889/&quot;&gt;marc_smith&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;features-we-wanted&quot;&gt;Features we wanted&lt;/h3&gt;

&lt;p&gt;The most common features we wanted after discussing our APIs looking over existing tools.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Support for multiple languages. (preferably all the ones we use in the company)&lt;/li&gt;
  &lt;li&gt;Support for automatically generating client code from an &lt;a href=&quot;http://en.wikipedia.org/wiki/Interface_description_language&quot;&gt;IDL definition&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Support for generating service-side generation of API documentation, based on the IDL definition of the APIs&lt;/li&gt;
  &lt;li&gt;Support for cURL / browser based service interaction, to analyze requests / responses in a “human-readable” format&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Other features were mentioned, but this was the most common and most desired list of features. Since we are considering tools for integration for professional apis, I wanted solutions that would work self-hosted / behind the firewall. Which makes some of the hosted solutions like &lt;a href=&quot;http://apiary.io/&quot;&gt;Apiary&lt;/a&gt; less interesting although large parts of their solution is open sourced, like &lt;a href=&quot;http://apiblueprint.org/&quot;&gt;API Blueprint&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The growing number of APIs we wanted something similar to &lt;a href=&quot;https://developers.google.com/discovery/&quot;&gt;Google APIs Discovery Service&lt;/a&gt;. Google’s tools are great, but not really an option as they haven’t open sourced the document generation, service discovery server, or much server side language tooling. What has been open sourced is a &lt;a href=&quot;https://code.google.com/p/google-apis-client-generator/&quot;&gt;client code generator&lt;/a&gt;, which doesn’t have as much language support as we would like (Java, Java/GWT, .NET, and PHP). Also, open sourced was the &lt;a href=&quot;https://code.google.com/p/google-apis-explorer/&quot;&gt;google-apis-explorer&lt;/a&gt; which while cool doesn’t quite cover full documentation needs. These tools would work for you if you build out APIs that match their schema, but their supported languages are limited and don’t include my primary language Ruby.&lt;/p&gt;

&lt;h3 id=&quot;why-not-just-standard-restful-json-apis&quot;&gt;Why not just standard restful JSON apis&lt;/h3&gt;

&lt;p&gt;Because this is the state of most APIs I use today. Without any tooling just building rest-like APIs response formats, tool chains, documentation is slightly different. Often documentation is or not vary accessible. API Client are a mix of thin configurations of &lt;a href=&quot;https://github.com/rest-client/rest-client&quot;&gt;rest-client&lt;/a&gt; or large custom client gems with heavy models. Across many APIs this makes the tests, caching, usability, and usage expectations a new challenge with each API.&lt;/p&gt;

&lt;p&gt;I want to get more benefits than just moving from various custom presenters and JSON formatters to a consistent usage of a JSON serializing tool / specific JSON formats. Since most of the APIs I work with are in Ruby there are a number of tools that solve only that limited set of issues. I am just looking for a bigger win than the various Libraries below provide:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/nesquena/rabl&quot;&gt;RABL&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/rails/jbuilder&quot;&gt;Jbuilder&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/fabrik42/acts_as_api&quot;&gt;Acts_as_API&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/apotonick/roar&quot;&gt;Roar&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/intridea/grape&quot;&gt;Grape&lt;/a&gt; (although admittedly this one starts to get closer, with some of the optional integrations with other gems)&lt;/li&gt;
  &lt;li&gt;and more…&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For people interested in just having a solid Ruby api with a great format and code to help you build that one of the above options might be a great choice. The change log has a good post on &lt;a href=&quot;http://thechangelog.com/a-few-tools-to-build-a-json-api-in-a-ruby-web-app/&quot;&gt;crafting JSON output&lt;/a&gt;, which shows some examples, although it is a little dated. Also, watch the various &lt;a href=&quot;http://railscasts.com/episodes?search=api&quot;&gt;Railscast API videos&lt;/a&gt; before you get started, it can help save you from a silly mistake.&lt;/p&gt;

&lt;h3 id=&quot;benefits-beyond-json-formats&quot;&gt;Benefits Beyond JSON Formats&lt;/h3&gt;

&lt;p&gt;One piece of the puzzle is using standardize JSON formats. Try not to just make up your URIs and data representation yourself, I promise you will regret it. Right now many APIs I use and some I have developed (I know for shame) are hand rolled and the structure of the data isn’t ideal. Many apis are fairly loose and inconsistent in terms of how they serialize data. Some JSON format standards could help us improve that, as well as provide validators that could be integrated into the testing process, etc. Others only help in terms of defining where endpoints exist, how endpoints are accessed, and what the responding data looks like. While these are interesting, they are more interesting when paired with tooling based on these standards. Most of the tools mention in my list utilize one of these formats and some could support multiple formats in conjunction which seems promising.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://jsonapi.org/&quot;&gt;JSON API&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;A format by &lt;a href=&quot;http://twitter.com/wycats&quot;&gt;Yehuda Katz&lt;/a&gt; and &lt;a href=&quot;http://twitter.com/steveklabnik&quot;&gt;Steve Klabnik&lt;/a&gt;.&lt;/li&gt;
      &lt;li&gt;The default ember data format.&lt;/li&gt;
      &lt;li&gt;The format restricts how you represent your JSON data, but to a list of tried and true best practices for representing your objects.&lt;/li&gt;
      &lt;li&gt;Example implementations exist for Ruby and other languages.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.jsonrpc.org/specification&quot;&gt;JSON-RPC&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;A JSON standard with a highly supported version 2.0 spec.&lt;/li&gt;
      &lt;li&gt;Defines rules for making &lt;a href=&quot;http://en.wikipedia.org/wiki/JSON-RPC&quot;&gt;JSON remote procedure calls&lt;/a&gt;, has client support in many languages.&lt;/li&gt;
      &lt;li&gt;Defines good input, client, and error behavior.&lt;/li&gt;
      &lt;li&gt;Doesn’t define or enforce good JSON data serialization of your objects. Allows for any format.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://json-schema.org/&quot;&gt;JSON Schema&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;A format to describe APIs in JSON.&lt;/li&gt;
      &lt;li&gt;The basis of a &lt;a href=&quot;http://json-schema.org/implementations.html&quot;&gt;growing list of tools&lt;/a&gt;, including some on this list like swagger.&lt;/li&gt;
      &lt;li&gt;While this format describes how your JSON api data is represented. It doesn’t enforce any good data serialization habits, allows for any format.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://avro.apache.org/docs/1.7.5/spec.html#json_encoding&quot;&gt;Apache Avro JSON&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;While Avro has a whole set of tooling one can choose to just support its format&lt;/li&gt;
      &lt;li&gt;The format helps define and enforce good data serialization habits.&lt;/li&gt;
      &lt;li&gt;Good language and platform support.&lt;/li&gt;
      &lt;li&gt;Useful beyond APIs Hadoop and log processors, could utilize object and message serialization. Which would improve some issues we have seen with pure JSON.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;why-not-thrift-protocol-buffers&quot;&gt;Why not Thrift, Protocol Buffers&lt;/h3&gt;

&lt;p&gt;The largest reason is in the end I want human readable APIs. I want to be able to debug and test with standard curl commands. We want to be able to use simple Ajax calls without additional tooling. The benefits of Thrift and Protocol Buffers seem to not be large enough to force our stack to always use non standard tooling and limit our choices in terms of integrating other options. Finally the support for these formats isn’t quite as strong as we would like in terms of native mobile support. The only one of the data serialization and RPC systems that seems to not have as many limitations is Apache Avro, which will be covered in more data in the comparison list.&lt;/p&gt;

&lt;h3 id=&quot;api-tooling-options&quot;&gt;Api Tooling Options&lt;/h3&gt;

&lt;h5 id=&quot;tools-with-offerings-most-similar-to-google-apis-discovery-service&quot;&gt;Tools with offerings most similar to Google APIs Discovery Service&lt;/h5&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/mashery/iodocs&quot;&gt;ioDocs&lt;/a&gt;: Host or Self Serve API docs, built and supported by &lt;a href=&quot;http://www.mashery.com&quot;&gt;Mashery&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;pros:&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;Large community support and great documentation&lt;/li&gt;
      &lt;li&gt;Very nice interactive documentation, &lt;a href=&quot;http://dev.mashery.com/iodocs&quot;&gt;documentation live demo&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;Open source support for the &lt;a href=&quot;https://developers.google.com/discovery/v1/reference#resource_discovery&quot;&gt;Google Discovery Document Format&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;Built on the JSON Schema standard&lt;/li&gt;
      &lt;li&gt;Allows for service discovery for all APIs&lt;/li&gt;
      &lt;li&gt;Could be used with a JSON format, adding format validation to the tool chain for future APIs would be easy&lt;/li&gt;
      &lt;li&gt;Supports various authentication mechanisms&lt;/li&gt;
      &lt;li&gt;Many example integrations&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;cons:&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;No server code or comment integration, so the JSON docs need to be maintained and kept in sync by hand&lt;/li&gt;
      &lt;li&gt;Client code generation has limited support and doesn’t Ruby or Scala&lt;/li&gt;
      &lt;li&gt;Less tooling available than other options&lt;/li&gt;
      &lt;li&gt;While trying to be a open source implementation of Google’s Discovery Service it is missing pieces and will always be behind it since it relies on it’s tooling some places&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://developers.helloreverb.com/swagger/&quot;&gt;Swagger&lt;/a&gt;: An API stack built by &lt;a href=&quot;https://helloreverb.com/&quot;&gt;Reverb&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;pros:&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;Very usable, interactive documentation, &lt;a href=&quot;http://swagger.wordnik.com/#!/pet/&quot;&gt;Live Demo&lt;/a&gt; of the interactive docs&lt;/li&gt;
      &lt;li&gt;Built on the JSON Schema standard&lt;/li&gt;
      &lt;li&gt;Good community support, active &lt;a href=&quot;https://groups.google.com/forum/#!forum/swagger-swaggersocket&quot;&gt;swagger google group&lt;/a&gt;, highly responsive IRC room&lt;/li&gt;
      &lt;li&gt;Picked up and in use by several large companies like Salesforce&lt;/li&gt;
      &lt;li&gt;All related tools open sourced, with active contributions&lt;/li&gt;
      &lt;li&gt;Stub server support&lt;/li&gt;
      &lt;li&gt;Code generation support in many languages include our primary focuses (Javascript, Ruby, Scala, Andriod, iOS)&lt;/li&gt;
      &lt;li&gt;Docs can be written for existing APIs, opposed to having to build new APIs to support the format&lt;/li&gt;
      &lt;li&gt;Has integrated server side support for generating the swagger JSON docs&lt;/li&gt;
      &lt;li&gt;Integrated Server code (or annotated comments) more likely to keep in docs and clients in sync with changes&lt;/li&gt;
      &lt;li&gt;Allows for service discovery for all APIs&lt;/li&gt;
      &lt;li&gt;Could be used with a JSON format, adding format validation to the tool chain for future APIs would be easy&lt;/li&gt;
      &lt;li&gt;Supports various authentication mechanisms&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;cons:&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;Client code generation is mixed quality, good JS and Scala libs, while Ruby isn’t so great&lt;/li&gt;
      &lt;li&gt;Server code generation is mixed quality, again Scala being a great example with Ruby being OK&lt;/li&gt;
      &lt;li&gt;Current Ruby server side implementation likely would require comments in code, as the framework integration seems too immature&lt;/li&gt;
      &lt;li&gt;The Ruby code both server and client would likely need heavy contributions&lt;/li&gt;
      &lt;li&gt;While encourages good restful behavior, doesn’t really enforce any data format standards&lt;/li&gt;
      &lt;li&gt;Some of the tools are still in early / active development. Because of this documentation is a bit lacking, errors are difficult to interperate. This leads to a larger learning curve than some other tools&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://raml.org/&quot;&gt;RAML&lt;/a&gt;: A YAML description language for rest-line JSON apis, built by &lt;a href=&quot;http://www.mulesoft.com/&quot;&gt;Mulesoft&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;pros:&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;Very polished tooling &amp;amp; extremely good documentation, seems to have community support via &lt;a href=&quot;http://raml.org/about.html&quot;&gt;RAML workgroup&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;Sharable &lt;a href=&quot;https://api-notebook.anypoint.mulesoft.com/?ref=apihub&quot;&gt;API notebooks&lt;/a&gt; to share example usage and scripts, might be an interesting way to share configuring and usage.&lt;/li&gt;
      &lt;li&gt;Interactive Documentation, &lt;a href=&quot;http://api-portal.anypoint.mulesoft.com/twitter/api/twitter-rest-api/docs/raml&quot;&gt;Live Documentation Demo&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;Nearly all components open sourced&lt;/li&gt;
      &lt;li&gt;Many example integrations&lt;/li&gt;
      &lt;li&gt;Stub Server support&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;cons:&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;Early code generation tools, not yet stable. Ruby not supported&lt;/li&gt;
      &lt;li&gt;Documentation of the API lives entirely outside of standard code flows and tools&lt;/li&gt;
      &lt;li&gt;While a open format, it is kind of a odd non-standard format&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h5 id=&quot;highly-integrated-with-ruby&quot;&gt;Highly Integrated with Ruby&lt;/h5&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/rails-api/rails-api&quot;&gt;Rails-API&lt;/a&gt; combined with &lt;a href=&quot;https://github.com/rails-api/active_model_serializers&quot;&gt;ActiveModel::Serializer&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;pros:&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;Supports a common and standardized format &lt;a href=&quot;http://jsonapi.org/&quot;&gt;JSON API&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;Heavily integrated with Rails&lt;/li&gt;
      &lt;li&gt;Great Javascript client support&lt;/li&gt;
      &lt;li&gt;Enforced good data structure for API&lt;/li&gt;
      &lt;li&gt;Simple model for building dynamic clients&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;cons:&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;No built in documentation support&lt;/li&gt;
      &lt;li&gt;Little in the way of tooling: validations, client generators, or other tools&lt;/li&gt;
      &lt;li&gt;Still a work in progress&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/Sutto/rocket_pants&quot;&gt;Rocket Pants&lt;/a&gt;: opinionated rails api framework
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;pros:&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;Highly integrated into rails and ActiveRecord (AR error support, AR object mapping support.)&lt;/li&gt;
      &lt;li&gt;Most standard rails coding style&lt;/li&gt;
      &lt;li&gt;build in support for easy caching&lt;/li&gt;
      &lt;li&gt;Easy Ruby client generation via &lt;a href=&quot;https://github.com/Sutto/api_smith&quot;&gt;ApiSmith&lt;/a&gt; integration&lt;/li&gt;
      &lt;li&gt;Consistent and enforced good data structure for data serialization&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;cons:&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;Doesn’t include pretty documentation generation support&lt;/li&gt;
      &lt;li&gt;Also a pro, but heavily tied to rails and no other language support&lt;/li&gt;
      &lt;li&gt;Custom format doesn’t yet support &lt;a href=&quot;http://json-schema.org/&quot;&gt;JSON Schema&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;Client generation is Ruby only&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/square/fdoc&quot;&gt;FDoc&lt;/a&gt;: Documentation format and verification, built by &lt;a href=&quot;https://squareup.com/&quot;&gt;Square&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;pros:&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;Works with Rails or Sinatra&lt;/li&gt;
      &lt;li&gt;Integrated testing and verification support&lt;/li&gt;
      &lt;li&gt;Supports &lt;a href=&quot;http://json-schema.org/&quot;&gt;JSON schema&lt;/a&gt; standard&lt;/li&gt;
      &lt;li&gt;Includes documentation page generation support&lt;/li&gt;
      &lt;li&gt;Company &lt;a href=&quot;http://corner.squareup.com/2012/06/fdoc.html&quot;&gt;sponsored support via Square&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;cons:&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;The documentation pages leave a lot to be desired. Not interactive, not very skimable. Very bare bones&lt;/li&gt;
      &lt;li&gt;No client generation support&lt;/li&gt;
      &lt;li&gt;Limited non Ruby language support&lt;/li&gt;
      &lt;li&gt;The doc files are kept in separate files, no server code or comments integration&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/seomoz/interpol&quot;&gt;Interpol&lt;/a&gt;: A toolkit for working with API endpoint definition files, built by &lt;a href=&quot;http://moz.com/&quot;&gt;Seomoz&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;pros:&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;Integrated Ruby testing tools to validate endpoints&lt;/li&gt;
      &lt;li&gt;Stub server support&lt;/li&gt;
      &lt;li&gt;Development tools to ensure API endpoint responses match current schema, keep things in sync&lt;/li&gt;
      &lt;li&gt;Documentation generation support&lt;/li&gt;
      &lt;li&gt;Request parsing validation libraries&lt;/li&gt;
      &lt;li&gt;Supports JSON Schema&lt;/li&gt;
      &lt;li&gt;Would allow support of existing APIs&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;cons:&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;Definition not integrated via code or comments, but kept as a separate YAML file&lt;/li&gt;
      &lt;li&gt;The documentation pages are pretty bare bones, not interactive&lt;/li&gt;
      &lt;li&gt;No client generation support&lt;/li&gt;
      &lt;li&gt;While you define how data is serialized it doesn’t enforce any good serialization habits&lt;/li&gt;
      &lt;li&gt;Ruby only for server side integrations&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h5 id=&quot;other-options&quot;&gt;Other Options&lt;/h5&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://avro.apache.org/docs/1.7.5/index.html&quot;&gt;Apache Avro&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;pros:&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;Better native support than Thrift or Protocol Buffers, see why &lt;a href=&quot;http://tech.flurry.com/2012/07/12/apache-avro-at-flurry/&quot;&gt;Flurry chose Avro&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;While supporting a pure JSON format they have a faster binary format as well&lt;/li&gt;
      &lt;li&gt;Good platform and language support&lt;/li&gt;
      &lt;li&gt;“Simple integration with dynamic languages. Code generation is not required to read or write data files nor to use or implement RPC protocols. Code generation as an optional optimization, only worth implementing for statically typed languages.”&lt;/li&gt;
      &lt;li&gt;Advantages in integration with Hadoop and log processing&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;cons:&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;while sort of human readable it can be a bit more difficult&lt;/li&gt;
      &lt;li&gt;while sort of curl-able, writing valid curl calls can be a bit more difficult &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;curl  curl -v -H &quot;Accept: application/json&quot; -H &quot;Content-type: application/json&quot; -X POST -d &apos;{&quot;adSpaceName&quot;:&quot;splash_screen&quot;,&quot;location&quot;:{&quot;lat&quot;:12.231212,&quot;lon&quot;:23.3435},}&apos; http://localhost:8080&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;http://www.igvita.com/2010/02/16/data-serialization-rpc-with-avro-ruby/&quot;&gt;Avro Ruby code needs some love&lt;/a&gt; and performance improvements&lt;/li&gt;
      &lt;li&gt;No built in documentation generators&lt;/li&gt;
      &lt;li&gt;A bit heavy weight with a higher learning curve than others&lt;/li&gt;
      &lt;li&gt;No way to support old existing APIs could only move over as part of upgrades&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://barrister.bitmechanic.com/&quot;&gt;Barrister&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;pros:&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;built on the JSON-RPC 2.0 standard&lt;/li&gt;
      &lt;li&gt;Integrated Ruby server and client side support&lt;/li&gt;
      &lt;li&gt;Generates static documentation pages, based on nice Docco standard. &lt;a href=&quot;http://barrister.bitmechanic.com/calc.html&quot;&gt;Small Example Doc&lt;/a&gt;.&lt;/li&gt;
      &lt;li&gt;Fairly bare bones clients&lt;/li&gt;
      &lt;li&gt;Extremely simple learning curve, could get up and running quickly&lt;/li&gt;
      &lt;li&gt;Could work with existing APIs&lt;/li&gt;
      &lt;li&gt;Server integration likely doesn’t support our needs&lt;/li&gt;
      &lt;li&gt;IDL generated as a separate file, no tight server code or comments integration&lt;/li&gt;
      &lt;li&gt;Supports multiple languages&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;cons:&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;Doesn’t enforce good data serialization or URI practices&lt;/li&gt;
      &lt;li&gt;Scala support not listed yet&lt;/li&gt;
      &lt;li&gt;Smaller more limited tools than most&lt;/li&gt;
      &lt;li&gt;Seems to have smaller community support&lt;/li&gt;
      &lt;li&gt;Likely would need to hand roll some supporting tooling for both clients and servers&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Roll Your Own&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;pros:&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;Complete control over all pieces and stages of the tool chain&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://blog.heroku.com/archives/2014/1/8/json_schema_for_heroku_platform_api&quot;&gt;Heroko’s JSON schema support for platform API&lt;/a&gt;. Heroku did it! They support standard formats but rolled their own versions of document and client code generators. Seems very close to Swagger format and mentions Swagger standard, but doesn’t seem fully compatible and they went their own way for some of their tooling
        &lt;ul&gt;
          &lt;li&gt;&lt;a href=&quot;https://github.com/heroku/heroics&quot;&gt;Heroics&lt;/a&gt;: Heroku’s Ruby client generation from JSON Schema&lt;/li&gt;
          &lt;li&gt;&lt;a href=&quot;http://www.paasmag.com/2014/01/09/auto-generating-a-go-api-client-for-heroku/&quot;&gt;Generating a Go client&lt;/a&gt; a tutorial building a Go client&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;Leverage tools released from other companies efforts&lt;/li&gt;
      &lt;li&gt;We can really mix and match some of the formats and tools mentioned above trying to get the best of everything
        &lt;ul&gt;
          &lt;li&gt;For instance one can choose to use JSON-api (or Avro JSON) format with nearly any of the documentation / verification tools&lt;/li&gt;
          &lt;li&gt;We could take a system like FDoc or Barrister and add tooling to extend it to generate more robust swagger or ioDocs output&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;cons:&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;Higher learning curve&lt;/li&gt;
      &lt;li&gt;Higher maintenance costs&lt;/li&gt;
      &lt;li&gt;Could make a bad call, all the other projects would have more community involvement&lt;/li&gt;
      &lt;li&gt;May be harder to support existing APIs&lt;/li&gt;
      &lt;li&gt;Likely overall looser integration than working with a fully integrated stack&lt;/li&gt;
      &lt;li&gt;There are pretty big projects so it might be a large undertaking, depending on features you want to support&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;next-steps-towards-better-apis&quot;&gt;Next Steps Towards Better APIs&lt;/h3&gt;

&lt;p&gt;It is great that there are so many good options being built to improve tooling around developing APIs. Obviously, there are so many options it isn’t really clear what the best option is. The next step seem to be to pick out and implement a couple of the options to explore them in more detail. Either trying a different option for a couple different services or implementing several of the options on the same API to see which seems like the best fit.&lt;/p&gt;

&lt;p&gt;In fact I have already started to do this with Swagger, which is why it has a more detail describing it than several of the other options. One nice thing about researching all of these options and implementing one of them, has been learning how bad many of my APIs are. Seriously, trying to layer tooling like this on top of an awkward API and data response objects makes it abundantly clear that a poor planning was involved. It immediately makes the serializations used for the models seem awkward and overly verbose and the URI endpoints poorly chosen. Going through the exercise should improve your APIs. Building a new API with any of the tools or libraries above would likely result in a better API because it would force you to really think about the contract the API is describing when it is being implemented.&lt;/p&gt;

&lt;p&gt;I will follow up with more specifics on some example implementations of some of the options above. So stay tuned and see you next time.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Self learning Programming, teach yourself to code</title>
   <link href="https://www.mayerdan.com/programming/2014/01/13/self-learning-programming"/>
   <updated>2014-01-13T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/programming/2014/01/13/self-learning-programming</id>
   <content type="html">
&lt;p&gt;I think everyone in today’s world should be learning a little about how to code. If you use a computer at some point you will want to better understand how it works, or you will want to be able to create something on it as opposed to using it only for consumption. Even learning some of the fundamentals of programming will help you understand how the applications you use to socialize or work function. The basics of creating a webpage, applying styles, and adding interactivity/data storage will come in handy far more often than you can imagine even if you never become a “developer”.&lt;/p&gt;

&lt;p&gt;Since the early days of the internet, you could basically find everything you needed to learn about programming online. This is especially true today given the explosion of online education that has happened in the last few years not just for programming, but for learning nearly anything one would want to study. I have been really interested in online and self learning education in general. As various people have asked about programming or wanting help making basic web pages, I have been specifically looking at developer education online. It is amazing how quickly the space is growing and how great the resources have become. Both for those who is interested in learning just a bit to make their own web pages, &lt;!--more--&gt; and for for those that want to learn it all and make a career out of software development. As education approaches the career level there are offline components as well, but even those programming bootcamps are a new and interesting education model..&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note: Since I program primarily in Ruby, the resources are slightly skewed towards Ruby. Many of the resources, offer multiple languages and there are additional programs focused on other languages that might interest you.&lt;/strong&gt;&lt;/p&gt;

&lt;h3 id=&quot;self-guided-free---where-to-start&quot;&gt;Self Guided Free - Where To Start&lt;/h3&gt;

&lt;p&gt;Before going to a school or even an online class that has a schedule and assignments, I recommend starting with self learning at your own pace. Spend some time on nights and weekends and learn some of the basics and see what parts of programming might interest you.&lt;/p&gt;

&lt;p&gt;I really recommend everyone starts by learning the basics of web programming. The beginner friendly courses at &lt;a href=&quot;http://www.codecademy.com/&quot;&gt;CodeAcademy&lt;/a&gt; is where I recommend everyone starts:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.codecademy.com/tracks/web&quot;&gt;HTML &amp;amp; CSS&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.codecademy.com/tracks/javascript&quot;&gt;Javascript&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Finally a “backend” language like &lt;a href=&quot;http://www.codecademy.com/tracks/ruby&quot;&gt;Ruby&lt;/a&gt; or &lt;a href=&quot;https://www.codecademy.com/catalog/language/python&quot;&gt;Python&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After that, learning more and getting a deeper understand of a programming language of computer science basics in general makes sense.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://tryruby.org/levels/1/challenges/0&quot;&gt;TryRuby.org: free interactive Ruby tutorial great way to play around and learn some Ruby basics quickly&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;If you are interested in Ruby, I highly recommend the free &lt;a href=&quot;http://tutorials.jumpstartlab.com/projects/ruby_in_100_minutes.html&quot;&gt;Ruby in 100 minutes&lt;/a&gt; tutorial by &lt;a href=&quot;http://jumpstartlab.com/&quot;&gt;Jumpstart lab&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;Jumpstart Lab offers further &lt;a href=&quot;http://tutorials.jumpstartlab.com/&quot;&gt;self learning Ruby and Javascript tutorials&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://techcrunch.com/2013/12/09/codecademy-releases-its-first-educational-app-a-k-a-my-new-subway-time-killer/&quot;&gt;CodeAcademy: free mobile 1 hour programming intro app&lt;/a&gt;, a fun way to learn with spare time on your phone.&lt;/li&gt;
  &lt;li&gt;Khan Academy: one of the first free online classes, &lt;a href=&quot;https://www.khanacademy.org/cs&quot;&gt;offers a great self guided CS set of courses&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://rubymonk.com&quot;&gt;Rubymonk: free interactive tutorials&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://code.org/learn&quot;&gt;Code.org: part of the “hour of code” initiative, it offers tutorials and additional resources to get started learning to code&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://mislav.uniqpath.com/poignant-guide/&quot;&gt;Why’s Poignant guid to Ruby&lt;/a&gt; - the most interesting and artist programming book you will ever find.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;free---unstructured&quot;&gt;Free - Unstructured&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.teachingtree.co/&quot;&gt;Teaching Tree, free online video lectures it doesn’t have order courses but can quickly help you learn specific concepts in depth&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.comparitech.com/blog/information-security/hacking-python-courses-online/&quot;&gt;6 best online courses for ethical hackers&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;free---structured&quot;&gt;Free - Structured&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.coursera.org/category/cs-programming&quot;&gt;Coursera: Free university support online courses&lt;/a&gt;, these couses have start times enrollment periods, and require keeping up with the classes pace.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;minimal-costs--free-intros&quot;&gt;Minimal Costs / Free Intros&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.codeschool.com/&quot;&gt;code school: free beginner courses&lt;/a&gt;($29 a month for intermediate and advanced courses)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://rubyoffrails.com/&quot;&gt;RubyOffRails: videos, mentorship, office hours&lt;/a&gt;($39 a month and 80% Discount for Women)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.udemy.com/courses/Technology&quot;&gt;Udemy: self paced video training technology courses&lt;/a&gt; (some free)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.udacity.com/&quot;&gt;Udacity: Self paced small cost self contained online coursed&lt;/a&gt; (some free)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://onemonthrails.com/&quot;&gt;One Month Rails: self paced video tutorials and online community&lt;/a&gt; ($99)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://pragmaticstudio.com/&quot;&gt;Pramatic Studio: self paced video courses&lt;/a&gt;, from the authors of the most famous Ruby book&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://ruby.railstutorial.org/&quot;&gt;Rails Tutorial&lt;/a&gt; (free book, $50 for training videos)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://cli.learncodethehardway.org&quot;&gt;Learn the Command Line the Hard way&lt;/a&gt; (other materials from the Learn X the hard way series)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;face-to-face-courses-1-5-days---get-a-taste-or-level-up&quot;&gt;Face to Face Courses (1-5 days) - Get a Taste or Level Up&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://jumpstartlab.com/&quot;&gt;Jumpstart Lab&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://thenewcircle.com/training/ruby/ruby_on_rails.html&quot;&gt;5 days with Ruby and Rails&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;often you can find 3 day programming courses on eventbright, for example &lt;a href=&quot;http://www.eventbrite.com/e/cancelled-learn-ruby-rails-the-hard-and-fast-way-tickets-8484743095&quot;&gt;Learn Ruby + Rails the Hard and Fast Way&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Many conferences offer train camps before or during conference. Just check upcoming conferences. For example training at &lt;a href=&quot;http://www.lonestarruby.org/2013/lsrc#training-information&quot;&gt;Lonestar Rubyconf&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;bootcamps---make-a-career-out-of-it&quot;&gt;Bootcamps - Make a Career Out Of It&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.launchacademy.com/&quot;&gt;Launch Academy&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;$12,500&lt;/li&gt;
      &lt;li&gt;6 weeks interactive&lt;/li&gt;
      &lt;li&gt;10 week in person bootcamp&lt;/li&gt;
      &lt;li&gt;career placement support&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.gschool.it/&quot;&gt;GSchool&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;$20,000&lt;/li&gt;
      &lt;li&gt;24 week in person bootcamp&lt;/li&gt;
      &lt;li&gt;career placement support&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.starterleague.com/&quot;&gt;The Starter League&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;$12,000&lt;/li&gt;
      &lt;li&gt;12 weeks in person bootcamp&lt;/li&gt;
      &lt;li&gt;career placement support&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.bloc.io/&quot;&gt;bloc.io&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;$4,250&lt;/li&gt;
      &lt;li&gt;on-line video / mentoring bootcamp&lt;/li&gt;
      &lt;li&gt;approximately 12 weeks, but learn at your own pace&lt;/li&gt;
      &lt;li&gt;online meeting with mentor 3 times a week.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.betamore.com/academy&quot;&gt;Betamore Academy&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;$3,000 - $3,400&lt;/li&gt;
      &lt;li&gt;10 or 12 weeks&lt;/li&gt;
      &lt;li&gt;front-end or back-end course focus, in person bootcamp&lt;/li&gt;
      &lt;li&gt;career placement support&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://codingdojo.com&quot;&gt;coding dojo&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;$9,850&lt;/li&gt;
      &lt;li&gt;12 weeks in person bootcamp&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://flatironschool.com/rubycurriculum.html&quot;&gt;The Flatiron School&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;$12,000&lt;/li&gt;
      &lt;li&gt;4 weeks online&lt;/li&gt;
      &lt;li&gt;12 week in person bootcamp&lt;/li&gt;
      &lt;li&gt;career placement support&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://devbootcamp.com&quot;&gt;Dev Bootcamp&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;$12,200&lt;/li&gt;
      &lt;li&gt;9 week in person bootcamp&lt;/li&gt;
      &lt;li&gt;career placement support&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.hackreactor.com&quot;&gt;HackReactor&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;$17,780&lt;/li&gt;
      &lt;li&gt;12 weeks in person bootcamp&lt;/li&gt;
      &lt;li&gt;career placement support (98% graduate hiring rate)&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.mobilemakers.co/#what-is-the-mobile-makers-academy&quot;&gt;Mobile Makers&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;$9,000&lt;/li&gt;
      &lt;li&gt;8 weeks in person bootcamp&lt;/li&gt;
      &lt;li&gt;mobile / iOS focus&lt;/li&gt;
      &lt;li&gt;career placement support&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.thisismetis.com/&quot;&gt;Metis&lt;/a&gt; - with the guys from ThoughtBot
    &lt;ul&gt;
      &lt;li&gt;$12,500&lt;/li&gt;
      &lt;li&gt;12 weeks in person bootcamp&lt;/li&gt;
      &lt;li&gt;career placement support&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;no-silver-bullet&quot;&gt;No Silver Bullet&lt;/h3&gt;

&lt;p&gt;I think all of this is really promising and interesting, but the industry is young. Mistakes will be made - some will be poorly trained, and some will be flat out ripped off while seeking knowledge. Make sure to research your choices, and just like college you really will get out of it what you put into it. As &lt;a href=&quot;http://en.wikipedia.org/wiki/Reading_Rainbow&quot;&gt;LeVar Burton says on Reading Rainbow&lt;/a&gt;, “But you don’t have to take my word for it.”&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://medium.com/learning-to-code/f5b3cc66659a&quot;&gt;How to learn Ruby on Rails: one mans self learning story&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.npr.org/2013/12/31/258420151/the-online-education-revolution-drifts-off-course&quot;&gt;NPR: The Online Education Revolution Drifts Off Course&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.npr.org/2012/09/30/162053927/online-education-grows-up-and-for-now-its-free&quot;&gt;NPR: Online Education Grows Up, And For Now, It’s Free&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.fastcompany.com/3023456/become-an-ios-developer-in-8-weeks-the-truth-about-hack-schools&quot;&gt;Fast Company: “Become an iOS developer in 8 weeks”: The Truth about Hack Schools&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://net.tutsplus.com/tutorials/ruby/the-best-way-to-learn-ruby-on-rails/&quot;&gt;A guide to self learning Ruby&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://worrydream.com/MeanwhileAtCodeOrg/&quot;&gt;Meanwhile at code.org&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.hanselman.com/blog/ACoderAProgrammerAHackerADeveloperAndAComputerScientistWalkIntoAVennDiagram.aspx&quot;&gt;What exactly do you mean, “you want to learn to code?”&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Thanks&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Thanks goes out to &lt;a href=&quot;https://twitter.com/athal7&quot;&gt;Andrew Thal&lt;/a&gt;, &lt;a href=&quot;https://twitter.com/wengzilla&quot;&gt;Ed Weng&lt;/a&gt;, and &lt;a href=&quot;http://www.linkedin.com/pub/emily-rocheleau/12/5a1/9a1&quot;&gt; Emily Rocheleau &lt;/a&gt; for reading the draft of this post and making improvements. I am sure anyone who read the post understands it better for the help.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A friend is running a to help people find a dev camp that fits them well. Check out &lt;a href=&quot;http://coursereport.com&quot;&gt;Coursereport.com&lt;/a&gt; - A programming bootcamp directory that features reviews, ratings for over 100 bootcamps worldwide.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Coverband Production Code Coverage</title>
   <link href="https://www.mayerdan.com/ruby/2013/12/17/coverband-production-code-coverage"/>
   <updated>2013-12-17T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/ruby/2013/12/17/coverband-production-code-coverage</id>
   <content type="html">
&lt;p&gt;Rubyists have long used code coverage to help guide development. While our coverage reports are generated from tests, I wanted to see coverage of the production system as it was getting exercised by our users. After discussing with a number of developers and some failed attempts, I built &lt;a href=&quot;https://github.com/danmayer/coverband&quot;&gt;coverband&lt;/a&gt; to record production code coverage.&lt;/p&gt;

&lt;h2 id=&quot;why-production-code-coverage&quot;&gt;Why production code coverage&lt;/h2&gt;

&lt;p&gt;Dead code is a bug waiting to happen. It makes reasoning about a system more complicated, maintaining large codebases is hard enough without thousands of lines of un-executed code.&lt;/p&gt;

&lt;p&gt;If your team is managing an aging production system, a rotating group of developers and years of refactoring can create many dead code paths. It is often easy to find all the endpoints being used with data from &lt;a href=&quot;http://newrelic.com&quot;&gt;New Relic&lt;/a&gt;, or any other web analytics. It is much harder to find all the helpers, unused model methods, and other code that has been refactored out of use. Using Coverband, we have found entire models which no longer get executed by any code path, but still had test coverage. It also made it easy to see conditional paths which are no longer followed in all directions. Dead code often has test coverage slowing your test suite, and occasionally developers have to spend time maintaining or fixing it when upgrading Ruby versions or frameworks. The less code you have the easier a system is to reason about, and the more clear the abstractions become.&lt;/p&gt;

&lt;h2 id=&quot;how-does-it-work&quot;&gt;How Does it work&lt;/h2&gt;

&lt;p&gt;Initially I was trying to use Ruby’s standard library &lt;a href=&quot;http://www.ruby-doc.org/stdlib-1.9.3/libdoc/coverage/rdoc/Coverage.html&quot;&gt;Coverage&lt;/a&gt;. I tried to use Coverage on both Ruby 1.9.3 and Ruby 2.0 and ran into issues with it crashing. It didn’t seem to play nicely with the idea of sampling the requests. It would often run smoothly for a short while, but would eventually segfault even on tiny example applications.&lt;/p&gt;

&lt;p&gt;This led me to look for other solutions. Eventually I came across &lt;a href=&quot;http://ruby-doc.org/core-1.9.3/Kernel.html#method-i-set_trace_func&quot;&gt;set_trace_func&lt;/a&gt;, which was added in 1.9.x to give Ruby some basic tracing functionality. This has been improved for Ruby 2 with the &lt;a href=&quot;http://www.ruby-doc.org/core-2.0.0/TracePoint.html&quot;&gt;TracePoint&lt;/a&gt; api. While there are a few weird quirks with Ruby tracing where it doesn’t quite capture everything you expect, in general it works very well to allow capturing live code usage. Coverband uses a combination of Stdlib’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Coverage&lt;/code&gt; to take a small baseline measurement of code executed while booting. Then it uses a &lt;a href=&quot;https://github.com/danmayer/coverband/blob/master/lib/coverband/middleware.rb&quot;&gt;Rack::Middleware layer&lt;/a&gt; enabling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;set_trace_func&lt;/code&gt; to get runtime coverage.&lt;/p&gt;

&lt;p&gt;Since collecting the coverage data does have a performance overhead, I wanted to be able to slowly gather data without affecting all requests. So Coverband implements a sampling strategy allowing a small percentage of requests to be monitored. On smaller apps, I have a large percentage of requests being sampled while on large, high traffic sites I have been running Coverband with as little as 1% sampling and am still collecting very useful data.&lt;/p&gt;

&lt;p&gt;While I think there are various improvements that can be made to Coverband, after running it on a few production projects it has already proved to be useful. Our team has been able to find large chunks of unexecuted code, leading to a series of my favorite commits, the elusive negative change commit.&lt;/p&gt;

&lt;p&gt;This post will not cover the full usage and setup, as Coverband is under active development. The current detailed instructions can be found in the project’s Readme. If you want to try it out, follow the setup instructions on the &lt;a href=&quot;https://github.com/danmayer/coverband&quot;&gt;Coverband github page&lt;/a&gt;. Any issues or feature requests can be passed along as github issues.&lt;/p&gt;

&lt;h2 id=&quot;what-do-you-get&quot;&gt;What do you get&lt;/h2&gt;

&lt;p&gt;To avoid reinventing the wheel, Coverband coverage output looks just like most Ruby users have come to expect. It outputs in a &lt;a href=&quot;https://github.com/colszowka/simplecov&quot;&gt;Simplecov&lt;/a&gt; compatible format. This makes it easy to use any &lt;a href=&quot;https://github.com/danmayer/coverband/blob/master/lib/coverband/reporter.rb#L51&quot;&gt;Simplecov formatter&lt;/a&gt;, which is what Coverband does by default.&lt;/p&gt;

&lt;h5 id=&quot;thanks-livingsocial&quot;&gt;Thanks Livingsocial&lt;/h5&gt;

&lt;p&gt;This post is a cross-post about &lt;a href=&quot;https://techblog.livingsocial.com/blog/2013/12/17/coverband-production-ruby-code-coverage/&quot;&gt;coverband on the Livingsocial tech blog&lt;/a&gt;. I work at Livingsocial and we are hiring. At work we are using coverband in production on some applications to help drive refactoring and cleanup efforts.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Configuring a new OS X Mavericks development machine</title>
   <link href="https://www.mayerdan.com/programming/2013/12/01/configuring-osx-mavericks-dev-machine"/>
   <updated>2013-12-01T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/programming/2013/12/01/configuring-osx-mavericks-dev-machine</id>
   <content type="html">
&lt;p&gt;These are the steps I took to setup a new OS X 10.9 (Mavericks) machine for development. I have heard on twitter that each time you setup a new machine you get 1% better and at this rate you can become good at it in a few hundred years! Ha while that seems to be true I am getting happier with the process and I am now at least trying to document it. This post won’t serve as a rich tutorial on the configuration process, but should give some ideas on good ways to setup your own machine or ideas on how to fix some common gotchas I ran into with my setup.&lt;/p&gt;

&lt;p&gt;I know this is a very opinionated approach without much explanation, so if you are new to development I recommend finding richer tutorial as a this is largely intended for my own documentation.&lt;/p&gt;

&lt;h1 id=&quot;initial-setup-steps&quot;&gt;Initial setup steps&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;verify full disk encryption&lt;/li&gt;
  &lt;li&gt;update password on account / machine (if one was previously configured)&lt;/li&gt;
  &lt;li&gt;install all OS X updates&lt;/li&gt;
  &lt;li&gt;install X code (this and the updates can be slow get this started as soon as you can)
    &lt;ul&gt;
      &lt;li&gt;options download install pretty much all the requested packages&lt;/li&gt;
      &lt;li&gt;cmd line &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xcode-select --install&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;while-xcode-installs&quot;&gt;While Xcode installs&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;install chrome&lt;/li&gt;
  &lt;li&gt;while X code installs install &lt;a href=&quot;http://www.irradiatedsoftware.com/downloads/?file=SizeUp.zip&quot;&gt;size up&lt;/a&gt;, or your prefered window manager&lt;/li&gt;
  &lt;li&gt;install dropbox, get sync started&lt;/li&gt;
  &lt;li&gt;get ssh keys
    &lt;ul&gt;
      &lt;li&gt;tar czf key.tar.gz .ssh/&lt;/li&gt;
      &lt;li&gt;gpg -c key.tar.gz&lt;/li&gt;
      &lt;li&gt;scp it over from other machines or encypted file sync via Dropbox&lt;/li&gt;
      &lt;li&gt;gpg –output key.tar.gz –decrypt key.tar.gz.gpg&lt;/li&gt;
      &lt;li&gt;tar xvzf key.tar.gz&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;download and install &lt;a href=&quot;https://www.virtualbox.org/wiki/Downloads&quot;&gt;virtualbox&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;after-xcode-install--run-boxen&quot;&gt;After Xcode, install / run Boxen&lt;/h1&gt;

&lt;p&gt;Installing Boxen will take a long time, so you can continue any download and configuration steps while it runs although some things you want to install are dependent on Boxen completing.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://github.com/boxen/our-boxen&quot;&gt;Boxen setup steps&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;cd our-boxen-master/
sudo mkdir -p /opt/boxen
sudo chown ${USER}:staff /opt/boxen
git clone https://github.com/boxen/our-boxen /opt/boxen/repo
cd /opt/boxen/repo
script/boxen
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;install &lt;a href=&quot;http://www.iterm2.com/#/section/home&quot;&gt;iterm2&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;install coco-emacs or Aquamacs&lt;/li&gt;
  &lt;li&gt;install zsh brew install zsh
    &lt;ul&gt;
      &lt;li&gt;change default shell to zsh &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chsh -s /bin/zsh&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;verify your running zsh after opening new terminal &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ps -o comm $$&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;install Pygments &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo easy_install Pygments&lt;/code&gt; #my dotfiles use Pygments&lt;/li&gt;
  &lt;li&gt;git clone &lt;a href=&quot;https://github.com/danmayer/dotfiles&quot;&gt;dotfiles&lt;/a&gt;, or however you sync your environment
    &lt;ul&gt;
      &lt;li&gt;sync your non git dot files encypted via scp or pgp dropbox sync (I keep .env_extras that contains some sensative keys so it is not stored on github)&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;configure VPN (if needed)&lt;/li&gt;
  &lt;li&gt;install &lt;a href=&quot;http://mouapp.com/&quot;&gt;Mou&lt;/a&gt; (markdown editor)&lt;/li&gt;
  &lt;li&gt;install campfire client (&lt;a href=&quot;http://propaneapp.com/&quot;&gt;Propane&lt;/a&gt;)&lt;/li&gt;
  &lt;li&gt;install IM client, &lt;a href=&quot;https://adium.im/&quot;&gt;Adium&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;configure &lt;a href=&quot;https://support.google.com/a/answer/48758?hl=en&quot;&gt;Adium for gchat&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brew install qt&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;at the time required HEAD &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brew update; brew install qt --HEAD&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gem install thrift -v &apos;0.9.0&apos; -- --with-cppflags=&apos;-D_FORTIFY_SOURCE=0&apos;&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brew install Mysql55&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;
        &lt;p&gt;initialize mysql:&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;mysql_install_db --verbose --user=`whoami` --basedir=&quot;/opt/boxen/homebrew/Cellar/mysql55/5.5.30/&quot; --datadir=/opt/boxen/homebrew/var/mysql55 --tmpdir=/tmp
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gem install mysql2 -v &apos;0.2.18&apos; -- --with-mysql-dir=/opt/boxen/homebrew/opt/mysql55/&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brew install imagemagick&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;get-your-code&quot;&gt;Get your code&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;begin to git clone all the things you love&lt;/li&gt;
  &lt;li&gt;start to rbenv install all the Ruby versions you need&lt;/li&gt;
  &lt;li&gt;bundle install all the things&lt;/li&gt;
  &lt;li&gt;verify tests are passing or apps start&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;updates-since-installed&quot;&gt;Updates Since installed&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://twitter.com/obfuscurity/status/403776103929888768&quot;&gt;rbenv to get latest ruby 1.9.3-p484 thanks @obfuscurity&lt;/a&gt;&lt;/p&gt;

  	cd /opt/boxen/rbenv/plugins/ruby-build
    &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;  git pull; cd
  rbenv install 1.9.3-p484
  rbenv global 1.9.3-p484
  gem install bundler
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;configure default global node.js &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nodenv global v0.10.21&lt;/code&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;possible-improvements&quot;&gt;Possible Improvements&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Should I install emacs starter kit or start over? I need to fix my emacs setup&lt;/li&gt;
  &lt;li&gt;Automated way to reset all the OS X default apps to open file types?&lt;/li&gt;
  &lt;li&gt;Automate some of the app downloads and installs&lt;/li&gt;
  &lt;li&gt;Automate some of the common ruby version installs&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>Sinatra using respond_to with url extensions (.json)</title>
   <link href="https://www.mayerdan.com/ruby/2013/10/07/sinatra-respond_to-supporting-extensions"/>
   <updated>2013-10-07T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/ruby/2013/10/07/sinatra-respond_to-supporting-extensions</id>
   <content type="html">
&lt;p&gt;Since I work most of the time in Rails, I found that using the &lt;a href=&quot;http://www.sinatrarb.com/contrib/respond_with.html&quot;&gt;Sinatra responds_to&lt;/a&gt; provided in &lt;a href=&quot;http://www.sinatrarb.com/contrib/&quot;&gt;Sinatra-Contrib&lt;/a&gt; to be a bit confusing. It didn’t match my expectations mostly in terms of setting content type based on url extensions. It worked fine when curling a url with the proper accepts settings &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;curl -i -H &quot;Accept: application/json&quot;&lt;/code&gt; but not when using a browser with the .json extension on the end of the url.&lt;/p&gt;

&lt;p&gt;I ended up solving this by setting the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;request.accept&lt;/code&gt; array based matching a optionally appended format on the url. 
It was also hard to use named url parameters along with a optional format parameter across all the urls I wanted to support. I saw one solution that allowed a &lt;a href=&quot;http://stackoverflow.com/questions/9775591/required-and-optional-parameters-for-sinatra-route&quot;&gt;optional format on a url&lt;/a&gt;, but it required modifying the path of each url. I didn’t want to alter each of my uris with complex regex. I eventually got everything working as I would like, with a bit of hacking.&lt;/p&gt;

&lt;p&gt;I added a before filter that checks the current request, if it matches the extension json (although this would work for any format), I set the request.accept headers. I then remove the extension from the path_info so that the urls work with the standard Sinatra route helpers. After adding that hack, the Sinatra-Contrib respond_to works as I expected.&lt;/p&gt;

&lt;h4 id=&quot;sinatra-optional-format-before-filter&quot;&gt;Sinatra optional format before filter&lt;/h4&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;require &apos;sinatra/contrib&apos;

before /.*/ do
  if request.url.match(/.json$/)
    request.accept.unshift(&apos;application/json&apos;)
    request.path_info = request.path_info.gsub(/.json$/,&apos;&apos;)
  end
end

#...

get &apos;/projects&apos;, :provides =&amp;gt; [:html, :json] do
  @projects = Project.projects
  respond_to do |format|
    format.json { @projects.to_json }
    format.html { erb :index }
  end
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;is-there-a-better-way&quot;&gt;Is there a better way?&lt;/h3&gt;

&lt;p&gt;Perhaps I am missing something, it took me a bit to figure out a good solution to this. Normally I find Sinatra matches my expectations very well and follows the principle of least surprise. That is why I decided to share my solution and see if there was just some other more simple way to solve the problem.&lt;/p&gt;

&lt;p&gt;Anyways, using this I was able to make the &lt;a href=&quot;http://churn.picoappz.com/index.json&quot;&gt;churn-site api&lt;/a&gt; pretty nice to browse over a browser. Especially if you have the &lt;a href=&quot;https://chrome.google.com/webstore/detail/jsonview/chklaanhfefbnpoihckbnefhakgolnmc&quot;&gt;JSONview&lt;/a&gt; chrome extension which I highly recommend.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>JS bookmarklet to switch between environment and shell function to open branch in github</title>
   <link href="https://www.mayerdan.com/javascript/2013/09/24/js-server-switcher-bookmark-and-git-open-shell-function"/>
   <updated>2013-09-24T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/javascript/2013/09/24/js-server-switcher-bookmark-and-git-open-shell-function</id>
   <content type="html">
&lt;p&gt;I got annoyed manually typing a new host to switch between development, staging, and production environments for some of my projects. After having yet another typo take me off to the wrong place and loosing my train of thought. I decided to whip up a better solution.&lt;/p&gt;

&lt;h3 id=&quot;switch-environments-using-bookmarklets&quot;&gt;Switch environments using bookmarklets&lt;/h3&gt;

&lt;p&gt;Basically when switching environments I was manually applying a regex replacement in my head. Then I would type out that replacement in a error prone way. Instead let’s just write it once and let the browser take care of it. To do that we create a bookmark prepended with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;javascript:&lt;/code&gt; so the browser will run it. Then manipulate the current &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;document.location&lt;/code&gt; to take you where you want to go.&lt;/p&gt;

&lt;h5 id=&quot;example-url-swapping-bookmarklets&quot;&gt;Example url swapping bookmarklets&lt;/h5&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;#bookmark named to_development
javascript:(document.location.href = document.location.href.replace(document.location.host,&apos;localhost:3000&apos;))
	
#bookmark named to_churn_staging
javascript:(document.location.href = document.location.href.replace(document.location.host,&apos;churn-site-staging.heroku.com&apos;).replace(&apos;:3000&apos;,&apos;&apos;))
	
#bookmark named to_churn_production
javascript:(document.location.href = document.location.href.replace(document.location.host,&apos;churn.picoappz.com&apos;).replace(&apos;:3000&apos;,&apos;&apos;))
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is a really simple solution, which is very handy. Recently, I have found myself building a hand full of tiny custom bookmarklets to help with little tasks. More than JS bookmarklets, you can use tricks like this all over the place.&lt;/p&gt;

&lt;h3 id=&quot;beyond-bookmarklets-tiny-shell-helper-functions&quot;&gt;Beyond bookmarklets, tiny shell helper functions&lt;/h3&gt;

&lt;p&gt;I have been automating tasks with shell functions as well. The same basic idea as the bookmarklets above of having a tiny function to replace a repetitive error prone task. I think encoding logic like this into a bookmarklet or a shell function is a great way to remember the tricks you learn, and encode the knowledge in a way that is sharable. In fact &lt;a href=&quot;https://twitter.com/tcopeland&quot;&gt;@tcopeland&lt;/a&gt; shared a great shell function with me recently, to quickly jump from the terminal to my branch view in the browser.&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;function open_branch { open http://github.com/danmayer/\`basename \`pwd\`\`/tree/$(git symbolic-ref head| sed -e &apos;s/.*\///g&apos;); }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The code above will open my browser to github on my current project and the branch I am currently working on.&lt;/p&gt;

&lt;p&gt;I use a number of other shell functions to simplify my day to day tasks. I highly recommend building some of your own functions and getting them checked into your dotfiles repo (you do have your dotfiles in a git right?). If you have any great bookmarklets or shell functions definitely let me know by commenting or sending a note to &lt;a href=&quot;http://twitter.com/danmayer&quot;&gt;@danmayer&lt;/a&gt; on twitter.&lt;/p&gt;

&lt;h5 id=&quot;update&quot;&gt;update&lt;/h5&gt;

&lt;p&gt;In response to this post, &lt;a href=&quot;https://github.com/webandy&quot;&gt;Andy A.&lt;/a&gt; sent me a improved version of the bash &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;open_branch&lt;/code&gt; function. Beyond being more flexible it adds some additional functionality like displaying your PRs and latest commit. Check out his improve &lt;a href=&quot;https://github.com/andyatkinson/dotfiles/blob/master/bash/functions&quot;&gt;github helper shell functions&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Smokescreen: Smoke tests for Ruby</title>
   <link href="https://www.mayerdan.com/ruby/2013/09/19/smokescreen-smoke-tests-for-ruby"/>
   <updated>2013-09-19T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/ruby/2013/09/19/smokescreen-smoke-tests-for-ruby</id>
   <content type="html">
&lt;p&gt;This post is a cross-post with the &lt;a href=&quot;https://techblog.livingsocial.com/blog/2013/09/19/smokescreen-smoke-tests-for-ruby/&quot;&gt;livingsocial tech blog&lt;/a&gt;. Livingsocial was nice enough to help opensource smokescreen so I could blog about it.&lt;/p&gt;

&lt;p&gt;There is a staggering number of definitions of &lt;a href=&quot;http://en.wikipedia.org/wiki/Smoke_testing&quot;&gt;smoke testing&lt;/a&gt;, and a good subset about how it relates to software development. I think the most common definition related to software can be quoted from the wikipedia article, “A subset of test cases that cover the most important functionality of a component or system.” You can also find some good explanations covering &lt;a href=&quot;http://stackoverflow.com/questions/745192/what-is-a-smoke-testing-and-what-will-it-do-for-me&quot;&gt;smoke tests on stack overflow&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As our test suite grew we had a need for quickly ‘smoke testing’ a project before deployment. We had a need because it was no longer reasonable to run the full test suite before every small commit. We wanted to run a smart subset of tests before deploy. Our smoke tests wouldn’t cover everything, but it would catch silly mistakes (syntax error, broken config files, etc) and check for catastrophic errors that could impact key actions, like our purchase flow. Obviously this is only necessary for projects that have very large and slow test suites, if your project has a suite that completes in 5 minutes or less congrats!&lt;/p&gt;

&lt;p&gt;At the moment running smokescreen prior to deploy is part of our deployment ‘checklist’, but it could be easily integrated directly into our deployment tools. Eventually one could see integrating &lt;!--more--&gt; smoke tests directly into the deployment process to run on the production boxes prior to starting up new unicorn workers. While it doesn’t protect us from every mistake it gives us confidence to deploy as frequently as we do while reducing the risk of making larger mistakes.&lt;/p&gt;

&lt;p&gt;There are a number of existing gems related to smoke testing available, &lt;a href=&quot;https://github.com/timonv/smokeit&quot;&gt;smokeit&lt;/a&gt; a tool for smoketesting urls, similarly &lt;a href=&quot;https://github.com/yyuu/antismoker&quot;&gt;antismoker&lt;/a&gt; helps build rake tasks to test urls which can be run as part of CI. Our primary need wasn’t to quickly check endpoints for http 200 success responses, but to get fast feedback on the critical functions of our app. We wanted to always run a minimum set of tests to cover our most important actions, as well as run tests likely to be impacted by the recent changes. We ended up building smokescreen to meet our needs, originally as a rake task and eventually extracting it out to a gem.&lt;/p&gt;

&lt;h3 id=&quot;setting-up-smokescreen&quot;&gt;Setting up Smokescreen&lt;/h3&gt;

&lt;p&gt;Setting up smokescreen is fairly easy, include the gem in your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Gemfile&lt;/code&gt;, and then require it in your primary &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rakefile&lt;/code&gt; or in any of your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lib/tasks/other_rakefiles&lt;/code&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;#in Gemfile
group :development, :test do
  gem &apos;smokescreen&apos;
end

#in a Rakefile
require &apos;smokescreen&apos;

#below require in rake file
Smokescreen.configure(:critical_tests =&amp;gt; [&quot;test/functional/purchases_controller_test.rb&quot;, &quot;test/functional/important_controller_test.rb&quot;])
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After you require smokescreen make sure to configure the critical tests. Just pass in an array of tests that you want to be be considered part of the critical set of tests.&lt;/p&gt;

&lt;h3 id=&quot;after-configuring-smokescreen&quot;&gt;After configuring Smokescreen&lt;/h3&gt;

&lt;p&gt;You can view all the tasks together running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bundle exec rake -T smoke&lt;/code&gt;, as you can tell there are four main groupings of tests current, deploy, previous, and recent.&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 test:smokescreen:critical:functionals                # Run tests for critical:functionals
rake test:smokescreen:current:all                         # run most important tests as well as tests for currently changed files (git diff)
rake test:smokescreen:current:changed                     # run tests related to currently changed files (git diff)
rake test:smokescreen:current:changed_tests               # run currently changed test files (git diff)
rake test:smokescreen:current:functionals                 # Run tests for functionals
rake test:smokescreen:current:functionals:changed_tests   # Run tests for functionals:changed_tests
rake test:smokescreen:current:units                       # Run tests for units
rake test:smokescreen:current:units:changed_tests         # Run tests for units:changed_tests
rake test:smokescreen:deploy:all                          # run most important tests as well as tests for files changed since the last deploy
rake test:smokescreen:deploy:changed                      # run tests related to files changed since last deploy
rake test:smokescreen:deploy:functionals                  # Run tests for functionals
rake test:smokescreen:deploy:units                        # Run tests for units
rake test:smokescreen:previous:all                        # run most important tests as well as tests for previously changed files (last commit, git show)
rake test:smokescreen:previous:changed                    # run tests related to previously changed files (last commit, git show)
rake test:smokescreen:previous:changed_tests              # run previously changed test files (last commit, git show)
rake test:smokescreen:previous:functionals                # Run tests for functionals
rake test:smokescreen:previous:functionals:changed_tests  # Run tests for functionals:changed_tests
rake test:smokescreen:previous:units                      # Run tests for units
rake test:smokescreen:previous:units:changed_tests        # Run tests for units:changed_tests
rake test:smokescreen:recent:all                          # run most important tests as well as recently effected tests (git diff + git show)
rake test:smokescreen:recent:changed                      # run tests for currently changed files (git diff)
rake test:smokescreen:recent:changed_tests                # run recently changed test files (current and previous commit test files)
rake test:smokescreen:recent:functionals                  # Run tests for functionals
rake test:smokescreen:recent:functionals:changed_tests    # Run tests for functionals:changed_tests
rake test:smokescreen:recent:units                        # Run tests for units
rake test:smokescreen:recent:units:changed_tests          # Run tests for units:changed_tests
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;running-smokescreen&quot;&gt;Running Smokescreen&lt;/h3&gt;

&lt;p&gt;The most common task I run is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rake test:smokescreen:recent:all&lt;/code&gt;, as it is the most comprehensive. That task runs the critical tests, test files related to currently change files (as found by git diff), and test files related to recently changed files (as found by git show). It is easy to target more specifically if you choose.&lt;/p&gt;

&lt;p&gt;I also find running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rake test:smokescreen:previous:changed_tests&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rake test:smokescreen:previous:all&lt;/code&gt; handy when doing code review on another developers cherry-picked commit. Making sure the tests don’t have one of the “it works on my machine” bugs.&lt;/p&gt;

&lt;p&gt;Smokescreen has only been used on a small set of projects, and doesn’t currently support anything other than &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;test:unit&lt;/code&gt;. Now that it is extracted out from a single application, I plan to user it in other projects and hopefully it will be easy to make some improvements to the tool. I already know there are easy wins that could be made to improve the process of matching likely related test files to recent changes. Other possible future features are parallel tests support, recently failing tests from CI or previous local test runs, and rspec support.&lt;/p&gt;

&lt;p&gt;Finally wanted to mention, as we have improved our test suite, and moved CI to run a massive split of &lt;a href=&quot;https://github.com/grosser/parallel_tests&quot;&gt;parallel tests&lt;/a&gt;, smokescreen has been less important than in the past, but it is still something I run nearly daily and have enjoyed having in my toolbox.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Introducing Churn Site to measure code churn</title>
   <link href="https://www.mayerdan.com/ruby/2013/09/16/introducing-churn-site-to-measure-code-churn"/>
   <updated>2013-09-16T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/ruby/2013/09/16/introducing-churn-site-to-measure-code-churn</id>
   <content type="html">
&lt;h3 id=&quot;inspiration-for-the-project&quot;&gt;Inspiration for the project&lt;/h3&gt;

&lt;p&gt;I have been interested in code metrics for awhile. I have built a few dev tools, and in the past tried to build startup based on code metrics. Recently I have been working on various side projects. As part of that, I wanted to put a little more work into the &lt;a href=&quot;https://github.com/danmayer/churn&quot;&gt;churn gem&lt;/a&gt; that I maintain. I decided building a site around &lt;a href=&quot;http://churn.picoappz.com/about&quot;&gt;code churn&lt;/a&gt; would force me to much more familiar as a client/user of the gem. Which would help me find more of the rough edges inspiring me improve the the project. I also thought I could learn some interesting stats by running churn against a larger variety of projects. Finally, I just wanted to play around with the idea of disposable temporary worker servers, which I have been toying around with and it seemed like a great fit for running churn against arbitrary projects.&lt;/p&gt;

&lt;p&gt;Check out &lt;a href=&quot;http://churn.picoappz.com&quot;&gt;churn-site&lt;/a&gt; now, or read a bit more about the project.&lt;/p&gt;

&lt;h3 id=&quot;churn-site-details&quot;&gt;Churn-Site details&lt;/h3&gt;

&lt;p&gt;I put the app together pretty quickly using Sinatra (using &lt;a href=&quot;http://mayerdan.com/ruby/2013/08/20/fast-prototyping-with-sinatra/&quot;&gt;Sinatra-Template&lt;/a&gt;), &lt;a href=&quot;http://www.chartjs.org/&quot;&gt;Chart.js&lt;/a&gt;, and personal projects that aren’t yet ready for release. The site is fairly simple, it has a list of projects and allows anyone to add projects or trigger churn builds against a project. For each project it collects churn data over time per commit and charts the results over time. To simplify collecting the churn data it supports &lt;a href=&quot;http://churn.picoappz.com/instructions&quot;&gt;github webhooks&lt;/a&gt;, which make it simple to keep churn up to date.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: left&quot;&gt; &lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt; &lt;/th&gt;
      &lt;th style=&quot;text-align: right&quot;&gt; &lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;a href=&quot;http://churn.picoappz.com/metricfu/metric_fu&quot;&gt;churn data for Metric-Fu&lt;/a&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt; &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;At the moment the most interesting data is probably the average churn of a method in your project and the number of methods with churn greater than that. Which I think gives interesting insight to problematic methods at a much more specific level than only looking only at file level churn (which is the standard unit for many churn tools).&lt;/p&gt;

&lt;p&gt;I am hoping to add class and method break downs for additional languages, but at the moment Ruby is the only language supported for anything beyond file level data.&lt;/p&gt;

&lt;p&gt;If the churn project interests you, there is a shared &lt;a href=&quot;https://waffle.io/danmayer/churn&quot;&gt;project tracker&lt;/a&gt; with upcoming features queued up.&lt;/p&gt;

&lt;p&gt;If you are more interested in the site, feel free to check out the source code to &lt;a href=&quot;https://github.com/danmayer/churn-site&quot;&gt;churn-site&lt;/a&gt;, although it will likely be a bit hard to contribute to yet as it relies on some of my other yet to be released projects. I think after a bit more cleanup, and adding support for uploading metrics directly from the churn gem, it will be much easier to contribute to the churn-site.
If you have ideas for additional graphs of ways to break down the churn data let me know.&lt;/p&gt;

&lt;p&gt;I am interested in how the community can make code metrics simpler while still being able to build rich dashboards like &lt;a href=&quot;http://metric-fu.rubyforge.org/&quot;&gt;Metric-Fu&lt;/a&gt; or &lt;a href=&quot;https://codeclimate.com/&quot;&gt;Code Climate&lt;/a&gt;. I reached out to the &lt;a href=&quot;https://groups.google.com/forum/#!topic/metric_fu/LgI8js5rZ18&quot;&gt;Metric-Fu Google Group&lt;/a&gt;, with some additional details about the project to get some early feedback. From that email thread, &lt;a href=&quot;http://twitter.com/kerrizor&quot;&gt;@kerrizor&lt;/a&gt; was nice enough to send my first PR for the site. Then share some good ideas for future features such as, showing the velocity of churn over time and showing a breakdown of the percentage of commits that involve a file or class.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Fast Prototyping with Sinatra</title>
   <link href="https://www.mayerdan.com/ruby/2013/08/20/fast-prototyping-with-sinatra"/>
   <updated>2013-08-20T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/ruby/2013/08/20/fast-prototyping-with-sinatra</id>
   <content type="html">
&lt;h2 id=&quot;quickly-prototyping-apps-with-sinatra&quot;&gt;Quickly prototyping apps with Sinatra&lt;/h2&gt;

&lt;p&gt;When I am quickly playing around with a new app or idea. I often want to be very minimal and fast. Half the time I never even get the app up and running anywhere other than my machine. Sometimes I never get past an index.html with some &lt;!--more--&gt; words and inline JS. I have found that being able to quickly setup a nice basic development environment makes it more likely I will play around with ideas, and explore new projects.&lt;/p&gt;

&lt;p&gt;After various approaches I have found that for me, I am very happy to do my fast prototyping in Sinatra. I can quickly get an application or api started with Sinatra, with a  bit of help it provides all the basics I need. After a couple projects, I have simple deployment of the projects down for either EC2 or Heroku. I can start up and get into the details of my idea amazingly quickly, which is great. After starting a couple projects with Sinatra I was tired of taking the time to setup some of the basic Sinatra boiler plate, the way I liked. Eventually after refactoring a few projects, I found a Sinatra style and setup that I like. I standardized on gems and configurations that solved common problems that I ran into. I built out a skeleton app to help me get up and running on my projects more quickly. It is simply my personal &lt;a href=&quot;https://github.com/danmayer/sinatra_template&quot;&gt;Sinatra Template&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;why-use-a-sinatra-template-or-skeleton&quot;&gt;Why use a Sinatra Template (or skeleton)?&lt;/h2&gt;

&lt;p&gt;Starting a brand new app takes time and much of the initial configuration is the same. This doesn’t mean I don’t fully understand the app, Sinatra Template tiny and it has been abstracted out of a few real projects. I built on it and then improved on my template as I began using it to generate newer apps.&lt;/p&gt;

&lt;p&gt;If I choose Sinatra over Rails for a web project, in part I am assuming less front end needs, overall smaller project, and a codebase that requires less structure. If this is more than a prototype, microsite, or expected to grow into a larger application, I recommend just starting with Rails. For that you can take a look at &lt;a href=&quot;http://mayerdan.com/ruby/2013/05/27/fast-start-rails-with-composer/&quot;&gt;quickly starting Rails apps with Composer&lt;/a&gt;, which is another post in this series. So why not just start with a blank Sinatra app. Sinatra is very barebones, my Sinatra Template gives me the basics I expect.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;environments (multiple procfiles), server startup (foreman), console (configured Pry), tests, my preferred license, development mode app reloading (shotgun), better_errors in development, flash message support, sessions, logging, admin authorization&lt;/li&gt;
  &lt;li&gt;A solid starting design, Sinatra Template has &lt;a href=&quot;http://twitter.github.io/bootstrap/&quot;&gt;Twitter Bootstrap&lt;/a&gt; configured and ready to go out of the gate&lt;/li&gt;
  &lt;li&gt;I am setup to perform most of my basic JS needs via Jquery which is ready to go from the beginning&lt;/li&gt;
  &lt;li&gt;Having a nice familiar base app as a starting point helps me more quickly get to building application specific pieces of code and get my idea on the page. Using a normal configuration with gems I am familiar with.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Really I am not advocating others to use my Sinatra Template, it is so small and simple that I recommend building your own. Feel free to use mine (or one of the many listed at the bottom of this post) as a example and starting point. Then customize it the way you like, as you built multiple Sinatra apps. Become familiar with your setup, and update it as you improve your Sinatra projects. When I add a new gem or technique to a couple of my projects I back port the features to my original template. Things like the better_errors gem, Pry, and Foreman weren’t originally part of my Sinatra template.&lt;/p&gt;

&lt;h2 id=&quot;nothing-to-development-with-sinatra-template&quot;&gt;Nothing to development with Sinatra Template&lt;/h2&gt;

&lt;p&gt;The example below is creating a new application called test_project.&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;cd ~/projects/
git clone https://github.com/danmayer/sinatra_template.git
cd ~/projects/sinatra_template
rake clone_web project=test_project
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This should give you output 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;building project: test_project
running mkdir test_project

running cp -R ./sinatra_template/web/ ./test_project/

running cp ./sinatra_template/README.md ./test_project/

running rm -rf .git

running git init
Initialized empty Git repository in /Users/danmayer/projects/test_project/.git/
running rm -rf .rvmrc

running rm -rf tmp/*

running echo &quot;rvm use ruby-1.9.3-p392 --create&quot;  &amp;gt;&amp;gt; .rvmrc

running find ./ -type f -exec sed -i &apos;&apos; &apos;s/SINATRA_TEMPLATE/test_project/g&apos; {} \;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then you can start to work on your project&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;cd ~/projects/test_project
#agree to the rvm ruby version
bundle install
foreman start
open http://localhost:3000

#also a single test should exist and pass
bundle exec rake
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;from-development-to-production&quot;&gt;From development to production&lt;/h2&gt;

&lt;p&gt;Great we have an app how do we get it out there for the world to see? For personal projects I mostly deploy initially to Heroku and only move to a larger more real production environment if the project really demands it. Getting a basic app to Heroku is easy. Obviously in the instructions below you need to give your app a different name.&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;heroku apps:create sinatratestproject
git add .
git commit -a -m &quot;initial commit&quot;

git push heroku master
#thats it you should be live
open http://sinatratestproject.herokuapp.com
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;sinatra-from-nothing-to-production&quot;&gt;Sinatra from nothing to production&lt;/h2&gt;

&lt;p&gt;Following the steps above it how I start most of my Sinatra apps these days. Getting me up and running very quickly so that I can focus on the goals at hand and not worry about some of the initial app creation boilerplate. I have used this on a couple projects now and I am pretty happy with my Sinatra setup. I have continued to patch and evolve my base Sinatra template, and I expect it will continue to improve over time.&lt;/p&gt;

&lt;p&gt;I built both &lt;a href=&quot;http://blog2ebook.picoappz.com/&quot;&gt;blog2ebook&lt;/a&gt; and the &lt;a href=&quot;http://churn.picoappz.com/&quot;&gt;Code Churn&lt;/a&gt; site using Sinatra Template, if you would like to checkout some examples.&lt;/p&gt;

&lt;h4 id=&quot;like-the-idea-but-not-my-sinatra-template-alternatives&quot;&gt;Like the idea but not my Sinatra Template, Alternatives&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/zapnap/sinatra-template&quot;&gt;Zapnap’s Sinatra Template&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/oren/sinatra-template&quot;&gt;Oren’s Sinatra Template&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://blog.yannick.io/ruby/2012/07/28/sinatra-skeleton.html&quot;&gt;Sinatra Skeleton&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/abachman/heroku-sinatra-skeleton&quot;&gt;Abachman’s Sinatra Skeleton&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>Thoughts on Continuous Deployment</title>
   <link href="https://www.mayerdan.com/programming/2013/08/04/thoughts-on-continuous-deployment"/>
   <updated>2013-08-04T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/programming/2013/08/04/thoughts-on-continuous-deployment</id>
   <content type="html">
&lt;h2 id=&quot;manual-continuous-deployment-vs-automated-continuous-deployment&quot;&gt;Manual Continuous Deployment vs. Automated Continuous Deployment&lt;/h2&gt;

&lt;p&gt;Recently there has been discussion around continuous deployment where I work. I have always liked the practice. I strongly believe in quick and small changes to production apps leading to the most value for the users and being the safest way to quickly iterate towards a larger goal. However our primary project has always practiced a looser version of continuous deployment, a manual continuous deployment. The manual but continual deploy process suites the current state of our project, while still giving us many of the advantages. I believe there are some times when following strictly automated continuous deployment should be considered dangerous, and I thought it would make sense to explain my concerns a bit.&lt;/p&gt;

&lt;p&gt;First let me describe two different continuous deployment processes&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Manual Continuous Deployment:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Manually trigger by developers, not automated by CI servers&lt;/li&gt;
  &lt;li&gt;Commiter / Deployer, follows a checklist of basic deploy guidelines&lt;/li&gt;
  &lt;li&gt;In general any significant change is deployed nearly immediately after merging to masters, sometimes this means commits go out in clusters or groups&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Automated Continuous Deployment:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Automated by CI server, deploys after non failing test suite run&lt;/li&gt;
  &lt;li&gt;Each commit is tested individually and deployed individually&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At first look every developer is probably thinking clearly automated deploy is the way to go. We should remove repetitive processes from the developer &lt;!--more--&gt; flow! Trusting developers with check lists is error prone, it is hard to coordinate deploys. I would agree that in the ideal project with a perfect infrastructure, that is the correct choice. The reality is most large projects have some baggage and state of the project means it might not be the best or a realistic choice for every project.&lt;/p&gt;

&lt;h2 id=&quot;issues-associated-with-automated-continuous-deployment&quot;&gt;Issues associated with automated continuous deployment&lt;/h2&gt;

&lt;p&gt;There are many nice befits of having a fully automated continuous deployment system. I’m skipping over arguments about never automating deploys and moving forward with the assumption, that for most web applications which don’t require high levels of human testing, have large security implications, or have lives at stake that it should be a goal. Even with the  goal of automated continuous deployment, there are still some primary problems that can make a project not yet ready for automated continuous deployment. Below is a list that I would consider requirements for automating deployment.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Deploys have no negative impact on system and end users&lt;/li&gt;
  &lt;li&gt;Fast tests&lt;/li&gt;
  &lt;li&gt;Fast deploys&lt;/li&gt;
  &lt;li&gt;A developer, usually the code author should be present during deploy&lt;/li&gt;
  &lt;li&gt;Easy safe override of continuous deployment&lt;/li&gt;
  &lt;li&gt;Clear dashboard around the deploy queue, continuous deployment status, manual deployment interruptions&lt;/li&gt;
  &lt;li&gt;Strong application monitoring and alerting&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;deploys-have-no-negative-impact&quot;&gt;Deploys have no negative impact&lt;/h3&gt;

&lt;p&gt;There are still projects out there that have brief downtime during deploys. Others just have negative performance issues for users. Projects might experience a brief blip of exceptions or temporary slow down related to breaking many large caches. All of these impact how often you want to deploy. Until you can safely deploy without having any negative impact you still likely want to have a developer batch up deploys that make sense. Developers should still want to deploy the smallest change possible to effect any of the server and business metrics, but likely don’t need to deploy every test, copy, and minor tweak as it is merged into master. Regardless of automated continuous deploys or not, solving this limitation is highly recommended.&lt;/p&gt;

&lt;h3 id=&quot;fast-tests-and-fast-deploys&quot;&gt;Fast Tests and Fast Deploys&lt;/h3&gt;

&lt;p&gt;Fast tests and Fast deploys are fairly similar. Both impact the total throughput of commits and deploys possible during a day for automated deploys. If you have a 50 minute test suite and a 10 minute deploy, it effectively will take 1 hour for a commit to hit production. For automated continuous deployment you really want to deploy each commit that passes CI individually. One of the large gains from a system like this is that each commit goes out one at a time so that monitoring and issues can be traced back to the initial deploy that caused a change.&lt;/p&gt;

&lt;p&gt;Let’s pretend developers all work in one time zone and only work a 8 hour day (Ha). That would impose a limit that only be 8 individual commits to the project a day, or the team ends up facing a backlog queue of deployments (some of which occur outside of normal working hours). That is a a bad idea, and if this is something a project enforces, quickly developers will ‘hack’ around the rule to do bad things like bundling many unrelated changes per commit just to get it through the deploy queue.&lt;/p&gt;

&lt;p&gt;Both Fast Tests and Fast Deploys are very importantly related to the next important issue…&lt;/p&gt;

&lt;h3 id=&quot;a-developer-should-be-present-for-deploy&quot;&gt;A developer should be present for deploy&lt;/h3&gt;

&lt;p&gt;Automated tests don’t catch everything. Even if there are no exceptions you might have performance regressions, unexpected behavior, or other issues. Deploying when no one is around and isn’t aware their feature just went live is a bad idea. Preferably the code author and code reviewer, should be present and monitoring systems they expect to be impacted.  Occasionally only one of the two, really needs to be there depending on the magnitude of the change.&lt;/p&gt;

&lt;p&gt;If you have slow tests or a slow deploy, quickly you will see a problem where 40+ commits are merged on a single day, because you have a big team that knows small safe changes is a best practice. Many of the commits will related to improving tests, developer only tools, minor copy changes, and other simple ‘safe’ changes. These changes are treated the same by a completely automated system and when mixed with larger features, developers end up checking in code and having it deploy a undetermined time later.&lt;/p&gt;

&lt;p&gt;Our deployment checklist notes developers should be around after a deployment of their code for around 20 minutes in case any issues arise. In a automated world, with long times between commits and deployment this becomes a very tricky issue. This issue alone is the biggest blocker of moving to automated deployments. Until your tests and deployments are super fast, allowing humans to filter what to deploy when adds tremendous value.&lt;/p&gt;

&lt;h3 id=&quot;manual-overrides-and-dashboards&quot;&gt;Manual overrides, and dashboards&lt;/h3&gt;

&lt;p&gt;When a project is running automated continuous deploys, at some point unexpected exceptions will start to occur. Exceptions either from a data change, external api issue, or simple a bad commit automatically deployed. At this point developers should have the ability to react quickly if deemed necessary.&lt;/p&gt;

&lt;p&gt;This means possibly doing a rollback deploy of the last commit, or quickly making a change and committing that into the project. Either a rollback or manual commit should be able to be deployed by a developer. It should automatically freeze future continuous deployments. It should make that clear on the applications deployment dashboard. The dashboard should make clear ‘automated deploys disabled because of deploy rollback of commit SHA’, or ‘automated deploys disabled manual override deploy by user X with commit SHA’.&lt;/p&gt;

&lt;p&gt;A single one button click on the dashboard should reenable automated continuous deployment, but it should be a smart re-enable, either asking the user which she to begin deploying again with, or immediately moving to HEAD on master and skipping any previous commits queued up. Obviously if someone manual deploys a fix you don’t want to go back and deploy commits prior to it, or redeploy the same commit that user deployed themselves.&lt;/p&gt;

&lt;p&gt;Related if there are special tests going on the dashboard should allow one click disabling of continues deploy which could be temporarily necessary while debugging or isolating a single system. These dashboards should also make it clear the expected deploy time for commits currently pushed to the project. Even if there is a fast test / deploy cycle. Developers might not want to push 2 or 3 commits at the end of the work day if there are already 10 commits queued up.&lt;/p&gt;

&lt;h3 id=&quot;strong-application-monitoring-and-alerting&quot;&gt;Strong application monitoring and alerting&lt;/h3&gt;

&lt;p&gt;Really for production applications having good monitoring and alerting in place is important. I think because of the ability for a deploy queue to get backed up and occasionally trigger outside of a developers expectations, that monitoring becomes even more of a importance. Not just on simple things like response time, exception rate, but deeper business metrics like successful logins, purchases, and other important metrics. You will want to clearly see which deploy accidentally effected a metric and trigger alerts as a developer might not be able to as closely watch each individual commit being deployed.&lt;/p&gt;

&lt;h2 id=&quot;choosing-which-is-right-for-your-team-and-project&quot;&gt;Choosing which is right for your team and project&lt;/h2&gt;

&lt;p&gt;After considering some of the real world implications of automated continuous deployment. I didn’t feel it was right for our team on most of our projects at the moment. Both because we would need a bit of additional tooling around deployment and dashboards, and because our tests are far to slow. For each project the team should be able to discuss and work towards automating continuous deployment, removing any problems that might be a blocker. Removing any of the blockers will be a big win for improving the development process on the project.&lt;/p&gt;

&lt;p&gt;Some projects accepting certain deployment shortcomings might be acceptable. For instance manual overrides, freezes and intelligent restarting of automated deploys isn’t much of an issue for a project with a 20 second test suite. Which will generally finish faster than one could realistically debug any production issue. I’m personally hoping we can successfully move more of our projects towards fully automated systems, but I think it is important to know how it impacts a project and balance the value of the automated system vs the risks for a given project. I hope we can eventually have all our project’s to a place where automated continuous deployment makes sense.&lt;/p&gt;

&lt;h2 id=&quot;additional-reading&quot;&gt;Additional Reading&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;http://www.etsy.com/&quot;&gt;Etsy&lt;/a&gt; has some great processes around deployments. I recommend checking out a couple of their posts / slides.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.slideshare.net/beamrider9/continuous-deployment-at-etsy-a-tale-of-two-approaches&quot;&gt;Continuous deployment, a tale of two approaches&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://codeascraft.com/2010/05/20/quantum-of-deployment/&quot;&gt;Quantum of Deployment&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://codeascraft.com/2011/02/04/how-does-etsy-manage-development-and-operations/&quot;&gt;How Etsy manage’s development and operations&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Non Etsy articles further discussing continuous deployment.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.startuplessonslearned.com/2009/12/continuous-deployment-for-mission.html&quot;&gt;Startup lessons learned, continuous deployment&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://java.dzone.com/articles/another-look-continuous&quot;&gt;Another look at continuous deployment&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.informit.com/articles/article.aspx?p=1641923&quot;&gt;Continuous Delivery: the value proposition&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</content>
 </entry>
 
 <entry>
   <title>Easily Pushing Static Sites to S3</title>
   <link href="https://www.mayerdan.com/programming/2013/07/30/static-sites-on-s3"/>
   <updated>2013-07-30T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/programming/2013/07/30/static-sites-on-s3</id>
   <content type="html">
&lt;h2 id=&quot;simple-static-site-hosting-on-s3&quot;&gt;Simple Static Site Hosting on S3&lt;/h2&gt;

&lt;p&gt;I wanted a simple way to build and host static sites on S3. I already had various apps storing files and pulling data from S3. I figured I could whip up a tiny app to make it easy to build up a static site and deploy it to S3. I decided to go with a simple rake task that can take template ERB files along with some data and merge it with the contents of a public folder and sync that rendered static files over to S3.&lt;/p&gt;

&lt;p&gt;The first site I am using this technique on is a collection of some of my little side projects. I called the site &lt;a href=&quot;http://www.picoappz.com/&quot;&gt;PicoAppz&lt;/a&gt; and plan on hosting a number of projects there as subdomains, so I don’t have to buy a new domain for each of my little one off projects.&lt;/p&gt;

&lt;p&gt;You can see the source code for &lt;a href=&quot;https://github.com/danmayer/picoappz&quot;&gt;PicoAppz on Github&lt;/a&gt;, the code started incredibly small and has grown as I added automatically pulling the latest screenshots of my apps, rebuilding the site on deploy, and I have started to build a Sinatra based admin app which will let me control some of the meta data.&lt;/p&gt;

&lt;h2 id=&quot;static-site-publishing-code&quot;&gt;Static Site Publishing Code&lt;/h2&gt;

&lt;p&gt;The primary piece of code you would want to look at to grok the static site generation flow &lt;!--more--&gt; would be &lt;a href=&quot;https://github.com/danmayer/picoappz/blob/master/lib/pico_appz.rb&quot;&gt;lib/pico_appz&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Two primary entry points called from rake&lt;/strong&gt;&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;build&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;copy_public_folder&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;render_templates_to_tmp&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;upload_to_s3&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;preview&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;copy_public_folder&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;render_templates_to_tmp&lt;/span&gt;
    &lt;span class=&quot;sb&quot;&gt;`open ./tmp/index.html`&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;strong&gt;Then this basically breaks down to these small 3 steps&lt;/strong&gt;&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;copy_public_folder&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;copying&apos;&lt;/span&gt;
    &lt;span class=&quot;sb&quot;&gt;`cp -r ./public/ ./tmp`&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;render_templates_to_tmp&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;building&apos;&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Dir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;./lib/views/**/*.erb&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;processing &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;template&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;rendered_file&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Erubis&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Eruby&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:title&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;picoappz&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:gh_data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fetch_github_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;output_file&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;gsub&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/\.erb/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;gsub&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/\/lib\/views/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/tmp&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;output_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;w&apos;&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;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rendered_file&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;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;upload_to_s3&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;uploading&apos;&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Dir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;./tmp/**/*.*&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;directory?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;mimetype&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sb&quot;&gt;`file --mime-type -b &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;gsub&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/\n/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;mimetype&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;text/css&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/\.css/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;filename&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;gsub&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/\.\/tmp\//&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;uploading &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; to &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;write_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:content_type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mimetype&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The trickiest bit in there is that you need to have the correct mime type to set on files when you upload them to S3. It took me a couple attempts to find something that would be safe to call on either OSX or Ubuntu, but so far this seems to work everywhere I run it.&lt;/p&gt;

&lt;h2 id=&quot;setup-dns&quot;&gt;Setup DNS&lt;/h2&gt;

&lt;p&gt;I was a little confused after reading some of the information about configuring DNS for S3 sites. Most of the tutorials you will find are pretty good, but some are out of date particularly with respects to hosting root domains without a ‘www’ vs with ‘www’. I was planning on hosting a number of sites on subdomains, and decided I wanted the main site to be with ‘www’ always on.&lt;/p&gt;

&lt;p&gt;I use &lt;a href=&quot;https://dnsimple.com&quot;&gt;DNSimple&lt;/a&gt; to manage my domains, it was easy to set up two manual entries in my DNS to cover the root and www versions of the page.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;URL	picoappz.com	600		http://www.picoappz.com&lt;/code&gt; (This is a alias that will have all root traffic forward to ‘www’)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CNAME	www.picoappz.com	600		www.picoappz.com.s3-website-us-east-1.amazonaws.com&lt;/code&gt; (This CNAME points at my s3 bucket for the site.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For AWS S3, you just create a new bucket and configure some settings&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Make sure to have ‘Enable website hosting’ turned on&lt;/li&gt;
  &lt;li&gt;I set my index document to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;index.html&lt;/code&gt; which I upload as the root of the site&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Under permissions add a bucket policy similar to&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;  {
    &quot;Version&quot;: &quot;2008-10-17&quot;,
    &quot;Statement&quot;: [
      {
        &quot;Sid&quot;: &quot;AddPerm&quot;,
        &quot;Effect&quot;: &quot;Allow&quot;,
        &quot;Principal&quot;: {
          &quot;AWS&quot;: &quot;*&quot;
        },
        &quot;Action&quot;: &quot;s3:GetObject&quot;,
        &quot;Resource&quot;: &quot;arn:aws:s3:::www.picoappz.com/*&quot;
      }
    ]
  }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That should be pretty much it. Now I plan on being able to very quickly add and build out other mini-sites and side projects that I can easily host on S3. If you are interested a bit more about how it &lt;a href=&quot;https://github.com/danmayer/picoappz/blob/master/lib/grabzit.rb&quot;&gt;fetches screenshots&lt;/a&gt; and the code that actually handles &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;write_file&lt;/code&gt; to &lt;a href=&quot;https://github.com/danmayer/picoappz/blob/master/lib/server-files.rb&quot;&gt;upload to S3&lt;/a&gt; check out the source, both are in the lib directory of the project on github. It should be pretty easy to follow, make sure you set all the require ENV variables in your environment.&lt;/p&gt;

&lt;h2 id=&quot;the-result&quot;&gt;The Result&lt;/h2&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;[master][~/projects/picoappz] rake build
copying
building
processing ./views/index.html.erb
uploading
uploading ./tmp/404.html to 404.html
uploading ./tmp/css/application.css to css/application.css
uploading ./tmp/css/bootstrap-image-gallery.min.css to css/bootstrap-image-gallery.min.css
uploading ./tmp/css/bootstrap-responsive.css to css/bootstrap-responsive.css
uploading ./tmp/css/bootstrap-responsive.min.css to css/bootstrap-responsive.min.css
uploading ./tmp/css/bootstrap.css to css/bootstrap.css
uploading ./tmp/css/bootstrap.min.css to css/bootstrap.min.css
uploading ./tmp/img/Blog2Ebook-clipped.png to img/Blog2Ebook-clipped.png
uploading ./tmp/img/Blog2Ebook-full.png to img/Blog2Ebook-full.png
uploading ./tmp/img/Blog2Ebook-thumb.png to img/Blog2Ebook-thumb.png
uploading ./tmp/img/blog2ebook.png to img/blog2ebook.png
uploading ./tmp/img/blog2ebook_sm.png to img/blog2ebook_sm.png
uploading ./tmp/img/Churn-clipped.png to img/Churn-clipped.png
uploading ./tmp/img/Churn-full.png to img/Churn-full.png
uploading ./tmp/img/Churn-thumb.png to img/Churn-thumb.png
uploading ./tmp/img/churn.png to img/churn.png
uploading ./tmp/img/churn_sm.png to img/churn_sm.png
uploading ./tmp/img/GitHub-Mark-120px-plus.png to img/GitHub-Mark-120px-plus.png
uploading ./tmp/img/GitHub-Mark-32px.png to img/GitHub-Mark-32px.png
uploading ./tmp/img/GitHub-Mark-64px.png to img/GitHub-Mark-64px.png
uploading ./tmp/img/glyphicons-halflings-white.png to img/glyphicons-halflings-white.png
uploading ./tmp/img/glyphicons-halflings.png to img/glyphicons-halflings.png
uploading ./tmp/img/nothing_calendar-clipped.png to img/nothing_calendar-clipped.png
uploading ./tmp/img/nothing_calendar-full.png to img/nothing_calendar-full.png
uploading ./tmp/img/nothing_calendar-thumb.png to img/nothing_calendar-thumb.png
uploading ./tmp/img/nothing_calendar.jpg to img/nothing_calendar.jpg
uploading ./tmp/img/nothing_calendar.jpg-clipped.png to img/nothing_calendar.jpg-clipped.png
uploading ./tmp/img/nothing_calendar.jpg-full.png to img/nothing_calendar.jpg-full.png
uploading ./tmp/img/nothing_calendar.jpg-thumb.png to img/nothing_calendar.jpg-thumb.png
uploading ./tmp/img/nothing_calendar.pdf to img/nothing_calendar.pdf
uploading ./tmp/img/nothing_calendar.png to img/nothing_calendar.png
uploading ./tmp/img/nothing_calendar_sm.png to img/nothing_calendar_sm.png
uploading ./tmp/img/NothingCalendar-clipped.png to img/NothingCalendar-clipped.png
uploading ./tmp/img/NothingCalendar-full.png to img/NothingCalendar-full.png
uploading ./tmp/img/NothingCalendar-thumb.png to img/NothingCalendar-thumb.png
uploading ./tmp/index.html to index.html
uploading ./tmp/js/application.js to js/application.js
uploading ./tmp/js/bootstrap-image-gallery.min.js to js/bootstrap-image-gallery.min.js
uploading ./tmp/js/bootstrap.js to js/bootstrap.js
uploading ./tmp/js/bootstrap.min.js to js/bootstrap.min.js
uploading ./tmp/js/jquery-2.0.3.min.js to js/jquery-2.0.3.min.js
uploading ./tmp/js/load-image.js to js/load-image.js
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Executable Ruby in embeddable widgets and other terrible ideas</title>
   <link href="https://www.mayerdan.com/javascript/2013/07/13/ruby-formatter-and-runner"/>
   <updated>2013-07-13T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/javascript/2013/07/13/ruby-formatter-and-runner</id>
   <content type="html">
&lt;p&gt;I want to first say that all that follows is basically a terrible idea, but I enjoy building evil projects once in awhile. If you are looking for a nice solution to running Ruby from the web checkout &lt;a href=&quot;http://repl.it/languages/Ruby&quot;&gt;repl.it&lt;/a&gt;, &lt;a href=&quot;http://tryruby.org/levels/1/challenges/0&quot;&gt;TryRuby&lt;/a&gt;, or &lt;a href=&quot;http://codepad.org/&quot;&gt;codepad&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this post I show examples of how I setup Ruby code that can be executed on my statically generated blog. This is sort of a Ruby follow up to &lt;a href=&quot;/javascript/2012/12/15/js-formatter-and-runner/&quot;&gt;Javascript formatting and highlighting on Jekyll and Github pages&lt;/a&gt;. Although, it doesn’t show how to setup your own version as that wouldn’t be very safe or secure.&lt;/p&gt;

&lt;p&gt;Once I had javascript code execution working, I really wanted to be able to do the same with Ruby. I could even envision some real world use cases for running Ruby from a page. I happened to be working on another project that seemed like a good fit to making Ruby from JS happen. I am going to start with some examples before getting into how the remote Ruby runner works.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;(The first run might take as long as 60 seconds to return a result, more on why below)&lt;/strong&gt;&lt;/p&gt;

&lt;h4 id=&quot;example-ruby-code-snippet&quot;&gt;Example Ruby code snippet&lt;/h4&gt;

&lt;div class=&quot;ruby-runner&quot; data-sig=&quot;izI5aBu3xe1wL1C+y19Xyl4gZRw=&quot;&gt;
  &lt;pre class=&quot;code&quot;&gt;
require &apos;test/unit&apos;

class Client
  attr_accessor :name, :age
  
  def initialize(name, age)
    self.name = name
    self.age = age
  end

  def can_drink?
    age &amp;gt; 21
  end

end

ClientTest = Class.new &lt;!--more--&gt; Test::Unit::TestCase do

   def test_can_drink
     client = Client.new(&apos;bruce&apos;, 20)
     assert_equal false, client.can_drink?
   end

end
  &lt;/pre&gt;
&lt;/div&gt;

&lt;h4 id=&quot;example-ruby-code-snippet-that-results-in-a-file-artifact&quot;&gt;Example Ruby code snippet that results in a file artifact&lt;/h4&gt;

&lt;div class=&quot;ruby-runner&quot; data-sig=&quot;qwfsvQGKZk77dBfNDz8KSaj5laQ=&quot;&gt;
  &lt;pre class=&quot;code&quot;&gt;
require &apos;pdfkit&apos;

kit = PDFKit.new(&apos;http://www.reddit.com/&apos;)
Dir.mkdir(&apos;./artifacts&apos;) unless File.exists?(&apos;./artifacts&apos;)
file = kit.to_file(&apos;./artifacts/temp_pdf_kit.pdf&apos;)
  &lt;/pre&gt;
&lt;/div&gt;

&lt;h3 id=&quot;neat-hows-it-work&quot;&gt;Neat, How’s it work?&lt;/h3&gt;

&lt;p&gt;The Ruby script running is based on a couple projects that I am not really ready to release. I can explain the basic functions and components, but I am not saying this is a good idea. I also wouldn’t take this as a template for how anyone should try to embed Ruby examples into a blog. I must say that for just running some simple Ruby code this is in fact huge overkill. The pieces of my code running system are built to handle much more than simple script requests, I just realized it would be a simple feature to add to my other project for fun. Here are the steps taken to run Ruby from a static HTML site.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;A Javascript library sends Ruby code to my Heroku app called &lt;a href=&quot;http://git-hook-responder.herokuapp.com/&quot;&gt;deferred-server&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;The deferred-server verifies the code is trusted based on the signature (code can only be signed by valid users of deferred-server)&lt;/li&gt;
  &lt;li&gt;If the code is trusted the app launches a deferred-server (a sleeping ec2 instances, but could basically be any cloud provider)&lt;/li&gt;
  &lt;li&gt;On booting the deferred-server starts a instance of &lt;a href=&quot;https://github.com/danmayer/server_responder&quot;&gt;server-responder&lt;/a&gt;, which handles various requests to a deferred server, either script based or project based.&lt;/li&gt;
  &lt;li&gt;The deferred-server app sends the script payload to the now running server, hitting the configured server-responder with a package of work to do and where to store the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;future_result&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;The Javascript at this point knows where to look for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;future_result&lt;/code&gt; and is polling for completed results.&lt;/li&gt;
  &lt;li&gt;When the JS finds completed results it pulls down the results as a JSON package and can handle them for the given situation (In this case the ruby-runner displays the results and provides a link to any run artifacts).&lt;/li&gt;
  &lt;li&gt;After X amount of time of inactivity the deferred-server is put back to sleep saving on cost and resources.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;that-is-insane-i-want-to-try-it&quot;&gt;That is insane, I want to try it!&lt;/h3&gt;

&lt;p&gt;Unfortunately, the code for this isn’t really ready for general use. In fact it is quite a mess. I won’t get into why for now, but I can say that I have been playing around with many different development ideas and styles on my side projects and some really didn’t work well at all! I also have yet to come up with a security model that would make this really safe to share.&lt;/p&gt;

&lt;p&gt;As mentioned earlier it can take a long time for the first request to respond. This is because I am not just hitting one server that is ready to handle my Ruby request. I am hitting a go between server which actually manages other servers and boots them up on demand. Once the script-responder server is up and running the response time is actually pretty good even with the go between server.&lt;/p&gt;

&lt;p&gt;I could modify and combine small pieces of the two projects to allow anyone to easily setup a Heroku instance to execute arbitrary Ruby code. Which might be the best way to release and share this functionality. If there is any interest in being able to quickly and easily embed Ruby example code on blogs, let me know and I can likely whip that up. Just being able to run code on Heroku, wouldn’t satisfy my needs as I wanted to run code which couldn’t be installed onto Heroku systems.&lt;/p&gt;

&lt;h3 id=&quot;other-examples-of-running-ruby-code&quot;&gt;Other examples of running Ruby code&lt;/h3&gt;

&lt;p&gt;I will post more on deferred-server and server-responder at some point in the future. For now you can read more about the &lt;a href=&quot;http://git-hook-responder.herokuapp.com/examples&quot;&gt;deferred-server script runner&lt;/a&gt; in the examples section.&lt;/p&gt;

&lt;p&gt;Also, you can check out my first real world usage to run custom Ruby on a static page. It uses a custom JS script on my resume, which runs embedded JS to &lt;a href=&quot;http://resume.mayerdan.com/&quot;&gt;on the fly build a PDF&lt;/a&gt; of my resume (click the PDF link to see it in action, view the page source to see how it works). This has been running ‘in production’ for months without issue. The source for custom Ruby on the page is pretty hilarious.&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;&amp;lt;div class=&quot;ruby-runner-custom&quot; style=&quot;display:none;&quot; data-sig=&quot;IhFw4kpAHfpiBkbNv0s4ALR8tjc=&quot; data-auto-init=&quot;false&quot;&amp;gt;
  &amp;lt;pre class=&quot;code&quot;&amp;gt;
    require &apos;pdfkit&apos;
    kit = PDFKit.new(&apos;http://resume.mayerdan.com/?no_pdf=true&apos;)
    file = kit.to_file(&apos;./artifacts/dan_mayer_resume.pdf&apos;)
  &amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;script src=&quot;http://git-hook-responder.herokuapp.com/javascript/code-runner.js&quot; type=&quot;text/javascript&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script&amp;gt;
var addPDFLink = function(currentPluggin) {
  var element = &apos;#formats&apos;;
  $(&apos;#formats&apos;).append(&apos; | &amp;lt;a class=&quot;run-button&quot; href=&quot;#&quot;&amp;gt;PDF&amp;lt;/a&amp;gt;&apos;);
  $(element).find(&apos;.run-button&apos;).click(function(e) {
    $(element).find(&apos;.run-button&apos;).text(&apos;generating...&apos;);
    currentPluggin.runExample();
        e.preventdefault;
    return false;
  });
};
$(&apos;.ruby-runner-custom&apos;).codeRunner({&apos;initialize_method&apos; : addPDFLink, &apos;follow_files&apos; : &apos;true&apos;});
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I also use the deferred code running functionality in a slightly different way with a Ruby project. It follows the same basic flow, but opposed to JS the Ruby app can make requests to itself running on a deferred server. I use that functionality on my &lt;a href=&quot;http://blog2ebook.herokuapp.com/&quot;&gt;blog2ebook&lt;/a&gt; project to perform tasks that aren’t allowed or would take to long on Heroku, like running kindlegen to generate ebooks. This allows a app to serve as a front end on Heroku while doing larger backend tasks on a more robust server.&lt;/p&gt;

&lt;p&gt;I will keep playing around with these systems and hopefully be sharing more and putting the to good use soon.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>introducing Blog2Ebook</title>
   <link href="https://www.mayerdan.com/ruby/2013/06/20/introducing-blog2ebook"/>
   <updated>2013-06-20T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/ruby/2013/06/20/introducing-blog2ebook</id>
   <content type="html">
&lt;h2 id=&quot;take-your-blogs-to-go&quot;&gt;Take your Blogs to go&lt;/h2&gt;

&lt;p&gt;I recently have been working improving my ability to quickly prototype projects from the ground up. I feel like in my day to day work I am frequently working on very large established systems. Where slow steady progress and refactoring are king. Working on smaller apps to try out new ideas and development methods can be a fun and educational habit. I built this app initially very quickly to solve a problem of my own. I have then expanded it a bit to build out out a app that might be useful for some to some other people.&lt;/p&gt;

&lt;p&gt;The result is a tiny app, &lt;a href=&quot;http://blog2ebook.herokuapp.com/&quot;&gt;Blog2Ebook&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;what-does-it-do&quot;&gt;What does it do?&lt;/h2&gt;

&lt;p&gt;Blog2Ebook supports importing from 3 different formats to make a Kindle book:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;RSS feed to Kindle&lt;/li&gt;
  &lt;li&gt;Single post to Kindle (useful because some https sites no longer allow bookmarklets)&lt;/li&gt;
  &lt;li&gt;Copied article text to Kindle&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You might be thinking there are tons of solutions for this. Like &lt;a href=&quot;http://www.readability.com/&quot;&gt;Readability&lt;/a&gt; and &lt;a href=&quot;http://www.amazon.com/gp/sendtokindle&quot;&gt;Amazon’s send to Kindle&lt;/a&gt;. True but each seem to have some problems and is missing something I want.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Nothing seems to fetch &amp;amp; format a full blog the way I want.&lt;/li&gt;
  &lt;li&gt;Readability is mostly setup for single articles as a bookmarklet, but the newer JS restrictions on some https sites like github prevent the bookmarklet from working&lt;/li&gt;
  &lt;li&gt;Amazon allows you to email files to the Kindle, but sometimes you just want to paste some text. You have to create and attach a text file to the email opposed to just having the email body be the content.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So I built a little app and learned a ton about eBook formatting, publishing, and various eBook formats. I think I might want to self publish something in the future and this has prepared me with some knowledge of the self publishing problems and process.&lt;/p&gt;

&lt;p&gt;Supporting very simple eBook publishing of a full blog with all content is the primary goal of the project. The other features are secondary and basically around because they were easy to add on and solved some problems. At this point I am starting to look a bit more into how to best format the content and embed related content into the books.&lt;/p&gt;

&lt;h2 id=&quot;app-details-and-resources&quot;&gt;App details and Resources&lt;/h2&gt;

&lt;p&gt;You can find the project Blog2Book, &lt;a href=&quot;http://blog2ebook.herokuapp.com/&quot;&gt;convert an RSS feed to Kindle eBook&lt;/a&gt;, hosted on &lt;a href=&quot;http://heroku.com&quot;&gt;Heroku&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The source code is available on github, as &lt;a href=&quot;https://github.com/danmayer/blog2ebook&quot;&gt;Blog2Ebook is open source&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you have thoughts, problems, or ideas &lt;a href=&quot;https://github.com/danmayer/blog2ebook/issues&quot;&gt;send feedback to issues&lt;/a&gt; for the project.&lt;/p&gt;

&lt;h2 id=&quot;technologies-used&quot;&gt;Technologies Used&lt;/h2&gt;

&lt;p&gt;I will likely post a bit more about the development process and specific examples of tech usage in another post, but I wanted to provide some details on the tech supporting the project.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Built using &lt;a href=&quot;https://github.com/danmayer/sinatra_template&quot;&gt;Sinatra Template&lt;/a&gt;, a personal project I maintain to quickly bootstrap small Sinatra projects. I will describe it more in a future post, but it makes it easy to get up and running with a web or API project using Sinatra and the gems and features I am experienced using.&lt;/li&gt;
  &lt;li&gt;Single Article Publishing, document content parsing
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;http://www.readability.com/&quot;&gt;Readability’s Api&lt;/a&gt; to better support single article publishing&lt;/li&gt;
      &lt;li&gt;I also checked out &lt;a href=&quot;https://github.com/peterc/pismo&quot;&gt;Pismo&lt;/a&gt; and &lt;a href=&quot;https://github.com/cantino/ruby-readability&quot;&gt;Ruby Readability&lt;/a&gt; which is a Port of arc90’s readability project to ruby. In the end single article support most often uses Readability’s API as I found it gave the best results.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://redis-cloud.com&quot;&gt;Redis Cloud by Garantia Data&lt;/a&gt; A awesome cloud redis provider&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.mailgun.com/&quot;&gt;Mailgun&lt;/a&gt; to power email, which is the kindle delivery mechanism.&lt;/li&gt;
  &lt;li&gt;RSS feed processing was done with &lt;a href=&quot;https://github.com/rest-client/rest-client&quot;&gt;rest-client&lt;/a&gt; and &lt;a href=&quot;http://nokogiri.org/&quot;&gt;nokogiri&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Amazon’s &lt;a href=&quot;http://www.amazon.com/gp/feature.html?ie=UTF8&amp;amp;docId=1000765211&quot;&gt;KindleGen&lt;/a&gt; to package up HTML content into .mobi files. Which allows packaging images in with the content as well as embedding book meta data. (mobi support with images is on a work in progress branch on not live yet)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because this is using the free tier of some of the supporting services, I limited daily usage of the app to below the free tier limits. So please be kind to the service, if it starts to run out of capacity I will take a look at expanding it out.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Quick Application Diagrams with Yard</title>
   <link href="https://www.mayerdan.com/ruby/2013/06/02/quick-application-diagrams-with-yard"/>
   <updated>2013-06-02T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/ruby/2013/06/02/quick-application-diagrams-with-yard</id>
   <content type="html">
&lt;p&gt;Thanks to following awesome people on twitter I learned something I didn’t know about yard. That is can be used to create nice application diagrams. I saw a tweet from &lt;a href=&quot;https://twitter.com/merbist/status/336417811079495681&quot;&gt;Matt Aimonetti&lt;/a&gt;, about generating diagrams with yard. I was interested in trying it out myself. After quickly installing &lt;a href=&quot;http://www.graphviz.org/&quot;&gt;graphviz&lt;/a&gt; which I apparently no longer had on my machine, I was rewarded with some nice diagrams for my rails apps. It also works well for gems as you can see from the churn graph below. Unfortunately it doesn’t seem to work completely out of the box on Sinatra apps, so I might need to learn a bit more configuration.&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;brew install graphviz
gem install yard
yard -n
yard graph --dependencies | dot -Tpdf -o app_diagram.pdf
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Above was all that was needed to get rails app diagrams like those below.&lt;/p&gt;

&lt;p&gt;Churn&lt;/p&gt;

&lt;p&gt;Small Rails apps, Hearno&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/hearno_app_diagram.jpg&quot; alt=&quot;Hearno Diagram&quot; /&gt;&lt;/p&gt;

&lt;p&gt;and finally NothingCalendar2&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/nothingcalendar2_app_diagram.jpg&quot; alt=&quot;NothingCalendar2 Diagram&quot; /&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>fast start rails with composer</title>
   <link href="https://www.mayerdan.com/ruby/2013/05/27/fast-start-rails-with-composer"/>
   <updated>2013-05-27T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/ruby/2013/05/27/fast-start-rails-with-composer</id>
   <content type="html">
&lt;h2 id=&quot;quickly-getting-started-with-rails-32&quot;&gt;Quickly getting started with Rails 3.2&lt;/h2&gt;

&lt;p&gt;This post explains how I &lt;!--more--&gt; quickly get an application started for Rails 3.2.x.The post is part of a series covering quickly prototyping projects.&lt;/p&gt;

&lt;p&gt;When I start a new rails project, I want to quickly get going with a login system, admin and other user roles, a nice default app template / style, and a JS framework I can build on. After looking at several options I ended up going with &lt;a href=&quot;http://railsapps.github.io/rails-composer/&quot;&gt;Rails Composer&lt;/a&gt;. It has a number of packages that work out of the box. It is very configurable, it is very easy to get up and running. &lt;a href=&quot;https://railsapps.org&quot;&gt;Railsapps&lt;/a&gt; used to have a lot of free tutorials coving using rails-composer, now they &lt;a href=&quot;https://tutorials.railsapps.org/&quot;&gt;sell detailed tutorials to support their work&lt;/a&gt;. You can still generally just read the read me for a project and go through the basic options to get everything working. I generally follow the &lt;a href=&quot;https://github.com/RailsApps/rails3-bootstrap-devise-cancan&quot;&gt;Rails App for Devise with CanCan and Twitter Bootstrap&lt;/a&gt; example for my basic app base and further configure it myself from there.&lt;/p&gt;

&lt;h2 id=&quot;why-use-rails-composer&quot;&gt;Why use Rails composer?&lt;/h2&gt;

&lt;p&gt;Starting a brand new app takes time and a lot of the initial configuration is very much the same. The composer options I choose are well understood common Rails gems I would often choose for my initial setup in the first place. I understand all the pieces that I setup with Rails composer so I don’t feel like I am giving up any understanding by starting with the generated app. I would recommend making sure you ‘could’ setup any of the pieces Rails composer configures for you, and understanding them before using them in a generated project. If I am programming in my personal time I often have a problem I want to solve or a specific idea I want to test, getting to the point I can work on the primary focus with little lost has a big value to me.&lt;/p&gt;

&lt;p&gt;If I choose Rails over Sinatra for a web project, in part I am assuming I will have higher front end needs for the project. Which likely means I will have accounts, logins, roles / rights, and want a more polished look and feel. Out of the box Rails requires you to setup a lot just to meet those basic needs. With Rails composer, I am basically good to go.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;A solid starting design, Rails composer will give me &lt;a href=&quot;http://twitter.github.io/bootstrap/&quot;&gt;Twitter Bootstrap&lt;/a&gt; configured and ready to go out of the gate&lt;/li&gt;
  &lt;li&gt;I am setup to perform most of my basic JS needs via Jquery which is ready to go from the beginning&lt;/li&gt;
  &lt;li&gt;Accounts, roles, and rights are covered by : Devise, Cancan, and Rolify. Quickly covering my basic account management needs.&lt;/li&gt;
  &lt;li&gt;The composer setups up a great way to share application specific configuration that isn’t checked into the code repo via &lt;a href=&quot;https://github.com/laserlemon/figaro&quot;&gt;Figaro&lt;/a&gt;. Which also supposed publishing the environment to Heroku my preferred production deployment for personal projects.&lt;/li&gt;
  &lt;li&gt;Having a nice familiar base app as a starting point helps me more quickly get to building application specific pieces of code and get my idea out there. Using a normal configuration with gems I am familiar with.&lt;/li&gt;
  &lt;li&gt;Stand on the shoulders of giants, I don’t want to spend my time building yet another authentication system, building in features like forgot password, email confirmation, etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;nothing-to-development-with-rails-composer&quot;&gt;Nothing to development with Rails composer&lt;/h2&gt;

&lt;p&gt;The example below is creating a new application called nothingcalendar2, which is a new version of a app I created awhile ago called nothing calendar to try out some JS ideas. Now I want to convert it to a modern rails application and add some new features.&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;rvm use ruby-1.9.3

#this will ask you some configuration options
rails new nothingcalendar2 -m https://raw.github.com/RailsApps/rails-composer/master/composer.rb

cd nothingcalendar2
bunlde install --local
bundle exec rake db:create
bundle exec rake db:migrate

#set the data to match your accounts / info
open config/application.yml

#this will use the data set in the last step to create accounts in the dev DB
bundle exec rake db:seed

# You have a app
rails s
open http://localhost:3000/

# check the tests
bundle exec rake

#create a new repo
https://github.com/new
git remote add origin git@github.com:danmayer/nothingcalendar2.git
git push -u origin master
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;from-development-to-production&quot;&gt;From development to production&lt;/h2&gt;

&lt;p&gt;Great we have a app how do we get it out there for the world to see? For personal projects I mostly deploy initially to Heroku and only move to a larger more real production environment if the project really demands it. Getting a basic app to Heroku is easy. Obviously in the instructions below you need to name your app something else.&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;heroku apps:create nothingcalendar2

#edit Gemfile
#add the group option to sqlite3 as you don&apos;t want it to install on heroku
#set group to development and test
#you could use postgres locally since that is what you are using on heroku in production. That is the recommended setup but for a toy project many people don&apos;t have postgres locally
gem &apos;sqlite3&apos;, :group =&amp;gt; [:development, :test]
#add postgres to Gemfile only for production group
gem &apos;pg&apos;, :group =&amp;gt; [:production]
bundle install

git push heroku master
heroku run rake db:migrate
#push the settings in your config/application.yml to heroku
bundle exec rake figaro:heroku
#seed the database with admin account
heroku run rake db:seed

#you should be live
open http://nothingcalendar2.herokuapp.com/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;rails-from-nothing-to-production&quot;&gt;Rails from nothing to production&lt;/h2&gt;

&lt;p&gt;Following the steps above it how I start most of my rails apps these days. Getting me up and running very quickly so that I can focus on the goals at hand and not worry about some of the initial details of having a basic infrastructure to build on. I have used this on a couple projects now and I am pretty happy with the process. I will likely continue to use this to quickly test out ideas, although for many simple progress I prefer the even lighter weight Sinatra setup, but that is for another post.&lt;/p&gt;

&lt;p&gt;I think this is a great way to get going. Not everyone agrees with me for the opposing view checkout, &lt;a href=&quot;http://thunderboltlabs.com/posts/why-we-dont-use-a-rails-template.html&quot;&gt;Why we don’t use a Rails template&lt;/a&gt; by &lt;a href=&quot;http://thunderboltlabs.com/&quot;&gt;thunderboltlabs&lt;/a&gt;.&lt;/p&gt;

&lt;h4 id=&quot;like-the-idea-but-not-rails-composer-alternatives&quot;&gt;Like the idea but not Rails-Composer, Alternatives&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/carbonfive/raygun&quot;&gt;Raygun&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/thoughtbot/suspenders&quot;&gt;Suspenders&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/cbetta/primo&quot;&gt;Primo&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://appscrolls.org/&quot;&gt;Appscrolls&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</content>
 </entry>
 
 <entry>
   <title>Performance Issues and <!--more--> Bug Clusters</title>
   <link href="https://www.mayerdan.com/programming/2013/05/08/performance_bugs_cluster"/>
   <updated>2013-05-08T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/programming/2013/05/08/performance_bugs_cluster</id>
   <content type="html">
&lt;p&gt;There is common advice in programming that bugs tend to cluster in the code. I have always found that true. In part because if there is a hard problem without a clean solution it is easier to make a mistake. Another reasons is one bug results in one hack with a bad fix adding it’s own bug. &lt;strong&gt;Creating bugs all the way down&lt;/strong&gt;. I think devs in general know about the issue of bugs clustering, but I don’t think everyone knows what to do about it or that it doesn’t only apply to bugs.&lt;/p&gt;

&lt;h3 id=&quot;ok-bugs-cluster-now-what&quot;&gt;OK, bugs cluster now what?&lt;/h3&gt;

&lt;p&gt;Depending on the on the seriousness of the bug, likelihood of a one off mistake or potential cluster, there are a couple of different things that might be worth doing:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;For any bug it is worth at least doing a brief audit to see if there are similar mistakes in nearby code.
    &lt;ul&gt;
      &lt;li&gt;Did this class use the same variable with the same mistake?&lt;/li&gt;
      &lt;li&gt;Was this method accidentally called with the wrong object anywhere else&lt;/li&gt;
      &lt;li&gt;Was this a data error, logic mistake, typo, misunderstanding? (How likely is this to be a one off bug vs. a cluster)&lt;/li&gt;
      &lt;li&gt;Do you remember debugging something like this in a related area fairly recently, If so likely worth digging in more.&lt;/li&gt;
      &lt;li&gt;A quick check can find and save many related bugs from popping up, over and over.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Was this a Really serious bug?
    &lt;ul&gt;
      &lt;li&gt;Audit all related code. Looking at all calling code and everything relying on results from the buggy section of code.&lt;/li&gt;
      &lt;li&gt;Did you just find a bug in authentication, encryption, payment services, etc… You better believe you should have one or two devs look for similar errors.&lt;/li&gt;
      &lt;li&gt;Where else in the code is there anything similar to this that could be impacted&lt;/li&gt;
      &lt;li&gt;Why didn’t a test catch this bug?&lt;/li&gt;
      &lt;li&gt;After the bug is fixed was it serious enough there needs to be a follow up post-mortem, meeting, log audits, discussions with the original code author(s)?&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Hmmm this was just a tiny bug but there is similar code all over the place.
    &lt;ul&gt;
      &lt;li&gt;Did you just fix the same bug 3 places in arrow? If so time to Isolate buggy issue.&lt;/li&gt;
      &lt;li&gt;If there are many related pieces of code dealing with the same issue often in slightly different ways, it is extremely likely there is a bug.&lt;/li&gt;
      &lt;li&gt;It also means that if you hack something to fix the bug, you likely only fixed one manifestation of the bug, or by fixing the known case created a new unknown bug.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Is this bug to big of problem to fix it all right now? Fix the initial known bug. Then in a &lt;a href=&quot;http://pragmati.st/2012/03/10/the-war-on-comments/&quot;&gt;rare time where comments are better than code&lt;/a&gt;. Comment on the hack pointing to related past bugs and potential other bugs. If you have even built up the mental model of a solution or two perhaps leave that information as well. We all have had to hack around some bug just to make it through the day, but if you come back a second time to fix a bug and find a comment like that, you have some refactoring in your future, dig in and get to it.&lt;/li&gt;
  &lt;li&gt;If you wrote the code and just authored a bug fix for a issue, perhaps ask another dev to give a quick code review of the code, class, or method. Fresh eyes can do wonders to illuminate a subtle deeper issue.&lt;/li&gt;
  &lt;li&gt;Likely a obvious one, but write a test that fails then fix the bug… Exceptions on production you say! Fine fix, deploy, then locally revert the fix and get that test around the error case.&lt;/li&gt;
  &lt;li&gt;A much harder thing to fix but good to understand. Was the bug cluster caused by a bad data model. Code that isn’t representative of the problem and task at hand? If so there might be a larger discussion or refactoring to start to consider.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;clusters-its-not-just-for-bugs&quot;&gt;Clusters, it’s not just for bugs&lt;/h3&gt;
&lt;div class=&quot;blog-header&quot; data-title=&quot;Not just for bugs&quot;&gt;
&lt;/div&gt;

&lt;p&gt;Code patterns are everywhere: styles, library usage, thoughts, developers, late night bad hacking, good day, bad day, new shiny obsession, dead feature, big O blindness, N+1, slow methods, service abuse…&lt;/p&gt;

&lt;p&gt;Pretty much any pattern the human mind can recognize can and will cluster in code. If you are trying to fix up, undo, remove, or improve code it helps to understand a bit of context. Just a little extra context can explain why the code exists the way it does. Git blame your lines, methods, and features to learn some possible historical context. See what other code was added or modified around the same time. I find that helpfully when sleuthing for all other possible related bugs… I actually find more often that this sort of ‘code cluster’ is more useful when removing a feature or doing performance work. It is good to know that code pattern form clusters like this in general but, I have found that following up on the patterns particularly for performance issues can have huge payoffs. If you find a simple but large performance win, dig into the code cluster finding all related code.&lt;/p&gt;

&lt;p&gt;I was working on performance problems for a specific Rails action. I found that it was doing the most naive and simple thing possible, which is great and in the past never really cause an issue. As the data size changed it was blatantly obvious that it was making hundreds of unnecessary calls per request and reducing the result set down for display to the first 5. Basically &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;get_massive_result_collection[0…5]&lt;/code&gt;, Ok simple fix, added ability to pass a limit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;get_massive_result_collection(:limit =&amp;gt; 5)&lt;/code&gt;. I realized that when the collection method was first added to the code, it’s usage had been spread all through out the app. I got the important performance fix deployed to production and saw a order of magnitude difference proving this was a pretty valuable discovery. I immediately created a ticket to follow up on all calls to the existing collection method in the app and find how many were abusing it. Over the next week or two I was able to knock incredible amounts of time of nearly every page load as the method was being used in application layouts, common partials, and spread since it was originally implemented. The code changes where each simple after tracking down the initial performance mistake. I personally will always spend a bit of time spelunking the code now to follow up on related code after discovering a large performance win.&lt;/p&gt;

&lt;h3 id=&quot;humans-are-pattern-recognition-machines&quot;&gt;Humans are pattern recognition machines&lt;/h3&gt;
&lt;div class=&quot;blog-header&quot; data-title=&quot;Pattern recognition machines&quot;&gt;
&lt;/div&gt;

&lt;p&gt;Seriously we are are pattern recognition machines, get good at it and use it! Keep a eye out for the patterns in your code. Understand that it can help you with bug fixing, refactoring, performance, dead code removal, and more. No piece of code is written in complete isolation, if it was it really wouldn’t be able to do much. So consider the code clusters the surround your changes, and can help you prevent a bug that has gone undetected.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Repetitive Hash Syntax, My quest for a Self Referring Hash</title>
   <link href="https://www.mayerdan.com/ruby/2013/03/03/repetitive-hash-syntax-my-quest-for-a-self-referring-hash"/>
   <updated>2013-03-03T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/ruby/2013/03/03/repetitive-hash-syntax-my-quest-for-a-self-referring-hash</id>
   <content type="html">
&lt;p&gt;For years a tiny code style issue has bothered me in Ruby. I find myself frequently building up a set of variables and then building a Hash where the keys all match the variables. It is a small nit-pick, and never wastes much time, but it has always annoyed me. The wasted duplication to have &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{:some_key =&amp;gt; some_key}&lt;/code&gt; for a long list of variables, when the key and the variable are named the same. Convention over configuration and all that. Ruby is a beautiful and flexible language. Why can’t we make it simpler and cleaner to create a Hash based of some locally defined variables. It turns out more complicated than one would think, because of trying to bind to the current variable scope.&lt;/p&gt;

&lt;p&gt;In the end what I wanted was a way to create a hash that would looks something like this, which I like to call a SelfReferringHash&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;    &lt;span class=&quot;n&quot;&gt;calculation&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;word&lt;/span&gt;        &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;a word&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;        &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;more data&quot;&lt;/span&gt;
    
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:calculation&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:word&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;#in this new fake syntax I imaged &apos;&amp;lt;=&apos; to mean points to self&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;#or perhaps being able to call a method on a array&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:calculation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:word&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bind_into_hash&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;#or perhaps something a bit more normal&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;build_from_vars&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:calculation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:word&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
    
    &lt;span class=&quot;c1&quot;&gt;#all methods would have the same result of a hash like below&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:calculation&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:word&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;a word&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;more data&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;!--more--&gt;
&lt;p&gt;###Inspiring a solution&lt;/p&gt;

&lt;p&gt;I mentioned this annoyance years ago to &lt;a href=&quot;https://github.com/ahoward&quot;&gt;Ara Howard&lt;/a&gt;, and he whipped up a &lt;a href=&quot;http://drawohara.com/post/151193800/ruby-symbol-to-hash&quot;&gt;example very quickly&lt;/a&gt;, but it didn’t feel quite right. Ara proposed a &lt;a href=&quot;https://gist.github.com/ahoward/157787&quot;&gt;few iterations&lt;/a&gt; with various syntax’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:result.to_h{}&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[:a, :b, :c].to_h{}&lt;/code&gt; but the syntax was distracting. So I kind of forgot about the issue and forgot about the issue for awhile.&lt;/p&gt;

&lt;p&gt;I then recently came across this post &lt;a href=&quot;http://alex.nisnevich.com/blog/2012/07/30/fun_with_ruby_hashes.html&quot;&gt;Playing Around with Ruby Hashes&lt;/a&gt;. Where Alex wants to add various features such as setting a arbitrarily deep value on the Hash without all the intermediate checks and creation of empty hashes&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;    &lt;span class=&quot;nb&quot;&gt;hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:stats&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;category&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;statistic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;
    
    &lt;span class=&quot;c1&quot;&gt;#without littering my code with a bunch of lines like&lt;/span&gt;

    &lt;span class=&quot;nb&quot;&gt;hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:stats&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}}&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:stats&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;category&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
    &lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The post build some fun features onto Ruby’s Hash and the post inspired me to play around with building a cleaner solution.&lt;/p&gt;

&lt;p&gt;###Attempting a solution&lt;/p&gt;

&lt;p&gt;While one of Ara’s &lt;a href=&quot;https://gist.github.com/ahoward/157787&quot;&gt;symbol to hash solutions&lt;/a&gt; was pretty close to what I wanted, it Monkey patched the Array and and Symbol I wanted to avoid that. I decided that I should make a new object and subclass Hash opposed to tacking my code onto any existing objects in the system.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;    &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SuperHash&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Hash&lt;/span&gt;

      &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;is_a?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
          &lt;span class=&quot;nb&quot;&gt;hash&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;SuperHash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;from_array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;nb&quot;&gt;hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each_pair&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;elsif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;is_a?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;nil?&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;requires context block for binding&quot;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

      &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;from_array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;requires context block for binding&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;nil?&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;flatten&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;inject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;SuperHash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;h&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;h&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;eval&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&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;end&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;calculation&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;word&lt;/span&gt;        &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;a word&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;        &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;more data&quot;&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;my_hash&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;SuperHash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;from_array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:calculation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:word&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]){}&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;my_hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;class&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;my_hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;inspect&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;my_hash&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;SuperHash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:calculation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:word&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]){}&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;my_hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;class&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;my_hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;inspect&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;begin&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;my_hash&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;SuperHash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:calculation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:word&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
      &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;my_hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;class&lt;/span&gt;
      &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;my_hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;inspect&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;rescue&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;
      &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;my_hash&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;SuperHash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;my_hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;class&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;my_hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;inspect&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Running that code results in the following output, with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SuperHash.new([:calculation, :word, :data]){}&lt;/code&gt; ending up being the ‘cleanest’ way to invoke the code.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;    &lt;span class=&quot;no&quot;&gt;SuperHash&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;more data&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:calculation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:word&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;a word&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;SuperHash&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;more data&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:calculation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:word&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;a word&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;requires&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;block&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;binding&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;SuperHash&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
    &lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;In the end, this might be interesting if I had some other things to patch onto the Hash object. Unfortunately it seems like this still isn’t really that useful and it is a bit to awkward. I could never avoid the primary issue I had with Ara’s examples of having to awkwardly pass the context. All solutions seem to require you to  pass a empty block to be able to bind to the context and access the variables at that time. Having a initializer or a class method which requires &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{}&lt;/code&gt; just seems to awkward to be useable. So after all this time I am still left feeling, unsatisfied. While this is a very simple problem to understand and Ruby is a very flexible language. The issues of binding to a context and variable scoping makes building a more beautiful and usable version of a self referring Hash out of my reach.&lt;/p&gt;

&lt;p&gt;If anyone has suggestions or ideas on how to build something that is a bit cleaner or closer to my goal and doesn’t resort to passing a empty block, I would love to see any other solutions. Perhaps, I am missing some more interesting way to bend Ruby to fit my mind.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Setting mime-type / content-type when uploading files to S3</title>
   <link href="https://www.mayerdan.com/ruby/2013/02/17/s3-files-mime-types"/>
   <updated>2013-02-17T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/ruby/2013/02/17/s3-files-mime-types</id>
   <content type="html">
&lt;p&gt;I was having a issue where after uploading a file to S3, it wouldn’t allow any user to view the file with a browser, but only download the file. The issue turned out to be that S3 defaults to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;binary/octet-stream&lt;/code&gt; as the content-type. I realized, I needed to set the content-type / mime-type when uploading files to preserve their mime-type and how they are treated in the browser for the user.&lt;/p&gt;

&lt;p&gt;I am using the &lt;a href=&quot;http://fog.io/&quot;&gt;Fog.io&lt;/a&gt; library to upload files and manage EC2 servers. It turns out that the ruby std lib File object doesn’t have any methods to help with mime type. While there are many gems to help with the task I didn’t really want to add another gem dependency or me around with what seem to be a large list of not recently maintained gems. After some quick google-fu, I found a solution that would work well enough on any unix like system. I just shelled out and called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;file -Ib&lt;/code&gt; and parsed it to get a mime type.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;   &lt;span class=&quot;n&quot;&gt;directory&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;connection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;directories&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                                              &lt;span class=&quot;ss&quot;&gt;:key&lt;/span&gt;    &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;a-bucket-name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                              &lt;span class=&quot;ss&quot;&gt;:public&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
                                              &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;filename&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;./somefile.txt&apos;&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;content&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;mimetype&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sb&quot;&gt;`file -Ib &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;gsub&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/\n/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;file_options&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;ss&quot;&gt;:key&lt;/span&gt;    &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;ss&quot;&gt;:body&lt;/span&gt;   &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;ss&quot;&gt;:content_type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mime_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;ss&quot;&gt;:public&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;directory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;files&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file_options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;save&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;After preserving and uploading correct mime types, the files worked as expected. If a user clicks a txt file they see the contents in browser, same with images, etc. Anyways, that solved the issue for me. I am kind of surprised S3 doesn’t have some sort of auto-detect mime type option.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Jekyll JS code formatting and running</title>
   <link href="https://www.mayerdan.com/javascript/2012/12/15/js-formatter-and-runner"/>
   <updated>2012-12-15T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/javascript/2012/12/15/js-formatter-and-runner</id>
   <content type="html">
&lt;p&gt;This post is an example of how to setup JS code formatting for Jekyll on Github pages along with a ability to allow users to execute the example JS code.&lt;/p&gt;

&lt;p&gt;example JS code snippet:&lt;/p&gt;

&lt;div class=&quot;js-runner&quot;&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;nx&quot;&gt;alert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;hello&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;adder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;
 &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;b&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;nx&quot;&gt;adder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;/div&gt;

&lt;p&gt;This is just for fun and should make it a bit easier to blog about some JS code examples in the future. It also allowed me to play around with some JS code to build a simple Jquery plugin for my blog.&lt;/p&gt;

&lt;h3 id=&quot;instructions&quot;&gt;Instructions&lt;/h3&gt;

&lt;p&gt;To make this work on github pages for your own blog basically follow the &lt;a href=&quot;http://www.recursive-design.com/blog/2010/10/12/static-blogging-the-jekyll-way/&quot;&gt;Pygments for Jekyll instructions&lt;/a&gt;. Remember to link the generated Pygments css file in your layout file. Then I just hacked together a really quick JQuery plugin which I include on my Jekyll layout template, starting from &lt;a href=&quot;https://github.com/conzett/jquery.exampleRunner&quot;&gt;Jquery.exampleRunner&lt;/a&gt;, I modified the plugin to find any div with class &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;js-runner&lt;/code&gt; and insert a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;run&lt;/code&gt; button which will include the results. You can view the source by checking this blogs source, or view &lt;a href=&quot;/assets/javascript/jquery.exampleRunner.js&quot;&gt;my version of jquery.exampleRunner source&lt;/a&gt; directly.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>First Responder, helping large development teams coordinate and work together</title>
   <link href="https://www.mayerdan.com/ruby/2012/11/18/working-with-teams-first-responder"/>
   <updated>2012-11-18T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/ruby/2012/11/18/working-with-teams-first-responder</id>
   <content type="html">
&lt;p&gt;The team I am working on has been responsible for maintaining a large central code base. This code base is a older code base that spans to many responsibilities. It’s stability and performance is important, because of this central roll. It is a point that many other teams need to interact with. Any downtime or large exceptions can have a significant impact on the company or many other teams relying on this project as a API.&lt;/p&gt;

&lt;p&gt;As the number of developers and teams within our company has grown so has how we manage code going into this central project. We began requiring the majority of code going into the project to have &lt;a href=&quot;http://www.codinghorror.com/blog/2006/01/code-reviews-just-do-it.html&quot;&gt;code reviews&lt;/a&gt;. We also went from only having developers who had a lot of experience with the central project to many developers who only touch it a hand full of times and work on other projects for their day to day development. This generally meant when a developer from another project needed a small change they needed some help finding their way around the project and to understand the context where they would be making changes.&lt;/p&gt;

&lt;p&gt;The changes and growth lead the team I am on to being overwhelmed with requests to review code, answer questions, and help other teams integrate changes into the project. At first we tried to just &lt;!--more--&gt; deal with this ad hoc as situations arose. While this sort of worked for awhile, it lead to a number of distractions and interruptions for all the developers on our team. It also made it hard to plan and schedule our own features as we didn’t know when or which one of our developers might get pulled of task to help another team work with the codebase. We needed a better way to work with other developers and teams, and a way to help review the constant flow of new code being pushed into the project. Code needed to be reviewed often coming in from all the other devs on our team freed up to focus on getting things done.&lt;/p&gt;

&lt;p&gt;For me personally the constant flow of interruptions was endangering my ability to have and longer period of focus, or get any significant work done because of &lt;a href=&quot;http://37signals.com/svn/posts/2272-the-interruption-tax&quot;&gt;the interruption tax&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;interruptions are the enemy of work. They are the enemy of productivity, they are the enemy of creativity, they are the enemy of everything.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;–&lt;a href=&quot;http://bigthink.com/ideas/18522&quot;&gt;Jason Fried of 37Signals&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;introducing-a-first-responder&quot;&gt;Introducing a first responder&lt;/h2&gt;

&lt;p&gt;We decided to schedule one person from our team a week to do nothing but help other developers get code into our project. The first responder (on-call, bug catcher, or on-duty) developer does the majority of the code review during their week. They take emails coming in for live bugs and if it is something tiny they fix it immediately or determine if another developer on the team needs to immediately focus on that issue. They help PM get estimates on upcoming work to at least ballpark if it is worth discussing. They look over other teams proposed features, branches, and Pull Requests to varying degrees. Sometimes all that is needed is a code review of the final changes, other times having a quick meeting with a team to discuss the requirements of their feature and what changes in the central project would need to occur to support their feature. Occasionally the first-responder will end up pair programming with another developer to help share knowledge of the codebase and help them integrate their changes.&lt;/p&gt;

&lt;p&gt;We rotate who is the first responder on the team each week. It can actually be pretty hectic and a bit scary. Some days you will get slammed with so many incoming requests bouncing quickly from one idea to another, and all the context switching will wear you out. Other days, you get to learn a lot about a part of the code base that you might not have been as familiar with previously, or with another team’s work which is new to you. It is definitely a challenging week that requires a different set of skills than our normal development duties.&lt;/p&gt;

&lt;p&gt;This has freed up the rest of our team’s developers to better focus on the features and work that we had planned. It makes it less likely a unexpected hiccup will knock all our devs off schedule. It has also reduced the tiny little interruptions that destroy developers ability to focus and do real development.&lt;/p&gt;

&lt;h4 id=&quot;share-the-expectations-to-other-teams&quot;&gt;Share the expectations to other teams&lt;/h4&gt;

&lt;p&gt;When our team introduced the first responder, we emailed all developers at the company. We shared our plan for a first responder and a email address to get in touch with our team’s first responder. The email went to the first responder who is on duty that week, but also allows the rest of our team to have a high level of what is going on by lurking on email threads. We let teams know that if there is a more immediate need they can jump in our campfire room and ask questions where anyone from the team can help respond, and we always update the room info to let people know who is the first responder this week. We asked that people try email and campfire channels prior to falling back to IMing a developer directly. We also added all the information to our projects README, so that new developers joining a team can find out quickly how to best interact with the central project.&lt;/p&gt;

&lt;p&gt;We aren’t the only team at our company that has been creating the first responder position, in fact we weren’t even the first. Other teams naturally evolved similar positions. Since we have been using the first responder on our team for awhile now. I think I can safely say it has been a big improvement. I have been happy to see more teams adopting similar rotation roles as well, as it has been great to have someone as a point of contact when trying to work with a project when you less familiar with it.&lt;/p&gt;

&lt;p&gt;While the satisfaction of being the first responder is different than normal tasks, helping someone ship a big new feature for their team can be very rewarding. So can mentoring more junior developers to help them solve a problem in a better way. There is also a certain joy of helping review and ship 10 - 15 smaller deployments to production, getting a bit in the code review zone of a review deployment / cycle. I know that I believe it has helped increase the overall quality of the code getting to the production system. It has also helped us to streamline and more quickly process a rapid flow of pull requests without slowing down the development pace as much (So has standardizing and sharing a set of expectations for pull requests for our project, but more on that in another post). I don’t like adding to much process to the development flow, but I think the right amount of process helps keep healthy balance between &lt;a href=&quot;http://www.randsinrepose.com/archives/2012/11/14/stables_and_volatiles.html&quot;&gt;Stables and Volatiles&lt;/a&gt;, ensuring they can work together while trying to get some of the benefits of each group. If your team seems to be frequently knocked off task by outside requests or facing a growing backlog of necessary code reviews, I highly recommend implementing the team first responder.&lt;/p&gt;

&lt;h6 id=&quot;additional-sources&quot;&gt;Additional Sources&lt;/h6&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.naildrivin5.com/blog/2012/04/02/a-protocol-for-code-reviews.html&quot;&gt;A Protocol for Code Reviews&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://blogs.atlassian.com/2012/10/collaboration-best-practices-3-reasons-interruptions-hurt-productivity/&quot;&gt;reasons interruptions hurt productivity&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://gettingreal.37signals.com/ch07_Alone_Time.php&quot;&gt;37signals Getting Real chapter, Alone Time&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://programmers.stackexchange.com/questions/105891/where-can-i-find-articles-on-why-interruptions-are-bad-for-programmers&quot;&gt;Large collection of sources on StackOverflow about about developer interruptions&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Our first responder contact methods (email, group chat, IM) in part help people to follow &lt;a href=&quot;http://programmers.stackexchange.com/questions/94800/best-tactics-for-avoiding-colleague-interruptions&quot;&gt;Tactics for avoiding colleague interruptions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h6 id=&quot;unsolved-first-responder-issue&quot;&gt;Unsolved First Responder Issue&lt;/h6&gt;

&lt;ul&gt;
  &lt;li&gt;A developer on my team pointed out one problem we have been having with the first responder rotation. We work on a distributed team, so we have rotations that involve developers in different timezones. If the first responder is on the west coast and a early morning issue occurs someone needs to respond. Currently a east coaster will drop what they are doing to step in to the first responder role to help with whatever live bug fighting is going on. We don’t really have a good solution for this issue yet.&lt;/li&gt;
&lt;/ul&gt;

</content>
 </entry>
 
 <entry>
   <title>ratio of bugs per line of code</title>
   <link href="https://www.mayerdan.com/ruby/2012/11/11/bugs-per-line-of-code-ratio"/>
   <updated>2012-11-11T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/ruby/2012/11/11/bugs-per-line-of-code-ratio</id>
   <content type="html">
&lt;p&gt;&lt;strong&gt;The more development I do the more I feel like increased Lines Of Code (LOC), nearly always results in increased bugs.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I know that seems obvious at first, but hear me out, as many refactorings, abstractions, ‘cleaner’ code increases the LOC.&lt;/p&gt;

&lt;p&gt;I have been reviewing a lot of code lately and the larger the added LOC are the more concerned I am about being able to fully understand and review the code. Even when I fully understand the purpose of the new code, I know lurking in each line could be a subtle but important bug. Seems obvious at first, but shouldn’t the overall complexity of the change and level of difficulty of the code be a large part of the equation as well? Most of the time the answer is not really. Reviewing new code going into a project each line introduces a new place for a bug to exist, even when each individual line is incredibly simple. So reviewing a ‘simple’ code change of 600 LOC is still often times far more risky than a complex change of 100 LOC (^1).&lt;/p&gt;

&lt;p&gt;My team has been working on refactoring and cleaning up a large older code base. One of the biggest issues now is just the sheer volume of unused, near duplicated, or overly abstracted code. The cognitive load associated with understanding all the implications &lt;!--more--&gt; of a change, and who might be relying on a specific quirk in a piece of existing code.&lt;/p&gt;

&lt;p&gt;This made me think about many of the great developers I know and how they try to avoid creating large code bases. Which is why I think eventually many great developers start to really favor the most succinct code to accomplish their task. Often it is slightly less readable (chaining injects, transformations, and reductions to a collection in one line), but it leaves such a small surface area for bugs to linger. Which is why I think so many incredibly smart engineers who enjoy the beauty of code move from writing verbose ‘readable’ explicit code with big objects and reusable modules, to condense and succinct but powerful code. Often favoring simpler objects, relationships, and data structures.&lt;/p&gt;

&lt;p&gt;I think the best example of developer trajectory of verbosity to conciseness comes from Steve Yegge, who gives a outrageous example of beginner code compared to expert code, to illustrate his point in &lt;a href=&quot;http://steve-yegge.blogspot.ca/2008/02/portrait-of-n00b.html&quot;&gt;Portrait of a N00b&lt;/a&gt;. While he admits that example is a bit of a joke, he does seem to have some support for my premise.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;I happen to hold a hard-won minority opinion about code bases. In particular I believe, quite staunchly I might add, that the worst thing that can happen to a code base is size.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;– Yegge in &lt;a href=&quot;http://steve-yegge.blogspot.com/2007/12/codes-worst-enemy.html&quot;&gt;codes worst enemy&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In fact many developers talk about how &lt;a href=&quot;http://www.codinghorror.com/blog/2006/07/diseconomies-of-scale-and-lines-of-code.html&quot;&gt;LOC is the enemy of software projects&lt;/a&gt;. The linked post references 37 Signals (several times), Seth Godin, and heavily quotes Steve McConnell all supporting how much more difficult and less time efficient it is to manage large code bases.&lt;/p&gt;

&lt;p&gt;I think in part the reason large codebases are such a problem is that as the code grows, so does the number of existing bugs in the system. The ratio of bugs per lines of code is pretty constant for a project based on the methods the team has followed while adding features and code to the project. Many of the articles I am linking to, point out the the cost of adding code doesn’t grow linerly but get’s orders of magnitude worse as the code base grows.&lt;/p&gt;

&lt;h3 id=&quot;bug-to-code-ratios&quot;&gt;Bug to code ratios&lt;/h3&gt;

&lt;p&gt;The idea of bugs per lines of code isn’t really a new idea. Steve McConnell, the primary source for the previously mentioned post, has written extensively on defects per lines of code. Covering average bugs per LOC stats in his great book &lt;a href=&quot;http://www.amazon.com/Code-Complete-Practical-Handbook-Construction/dp/0735619670&quot;&gt;Code Complete&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;(a) Industry Average: &quot;about 15 - 50 errors per 1000 lines of delivered
code.&quot; He further says this is usually representative of code that has some
level of structured programming behind it, but probably includes a mix of
coding techniques.

(b) Microsoft Applications: &quot;about 10 - 20 defects per 1000 lines of code
during in-house testing, and 0.5 defect per KLOC (KLOC IS CALLED AS 1000 lines of code) in released
product (Moore 1992).&quot; He attributes this to a combination of code-reading
techniques and independent testing (discussed further in another chapter of
his book).

(c) &quot;Harlan Mills pioneered &apos;cleanroom development&apos;, a technique that has
been able to achieve rates as low as 3 defects per 1000 lines of code during
in-house testing and 0.1 defect per 1000 lines of code in released product
(Cobb and Mills 1990). A few projects - for example, the space-shuttle
software - have achieved a level of 0 defects in 500,000 lines of code using
a system of format development methods, peer reviews, and statistical
testing.&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Going further McConnell talks about the value of &lt;a href=&quot;http://www.stevemcconnell.com/ieeesoftware/bp09.htm&quot;&gt;defect tracking&lt;/a&gt;. As well as the speed of writing &lt;a href=&quot;http://www.stevemcconnell.com/articles/art04.htm&quot;&gt;quality software in relationship to the defect rate&lt;/a&gt;, and bugs related to a team’s &lt;a href=&quot;http://www.stevemcconnell.com/articles/art02.htm&quot;&gt;software development process maturity&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I think McConnell places too high of cost on bugs, at least for modern agile web development. He is mostly discussing the issues in the context of shipped (Microsoft, NASA, and defense), opposed continuously deployed web apps where most bugs can be solved quickly and the mean time to recovery matters more than have a extremely low bug rate. (yes +1 for Facebook’s &lt;a href=&quot;http://spectrum.ieee.org/at-work/innovation/facebook-philosophy-move-fast-and-break-things&quot;&gt;Move Fast and Break Things&lt;/a&gt;). I do agree with McConnell that more bugs in the software will also slow the speed of development, as it increases the cognitive load.&lt;/p&gt;

&lt;p&gt;Working on a large project with many developers of varying skill, I often push for easily readable code. I think it is important that anyone on the team can work on the code. When more easily readable code starts to increase total code size, these two ideas are at odds. That dichotomy is what brought all of this to the front of my mind. I have been increasingly been hesitant to believe a refactoring just for the sake of slight readability at the cost of increased lines of code is a good thing.&lt;/p&gt;

&lt;p&gt;I do think heavily functional and succinct code is far better for back end systems and complex functions and less likely to cause maintainability problems. So while I might slowly be writing more chained and dense code, I don’t think it works everywhere. I have yet to see how that kind of succinct ‘clean’ code can handle user input exceptions, and highly conditional view layers. I think part of it might be why experienced developers like to work on frameworks and more isolated services where one can produce and enforce such ‘pretty’ code.&lt;/p&gt;

&lt;h2 id=&quot;final-thoughts&quot;&gt;Final Thoughts&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;Keep your code tiny. Fight extra complexity and lines of code and strike down upon it with great vengeance &amp;amp; furious anger…&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;–&lt;a href=&quot;http://quotes.dictionary.com/And_I_will_strike_down_upon_thee_with&quot;&gt;not quite what was said in Pulp Fiction&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I think bugs to LOC ratio is one of the main reasons having a large codebase is bad. I believe the cognitive load of having to write code that functions around all the bugs/quirks in a code base is why progress slows so much on larger code bases.  I think large codebases being bad is one of the reasons why SOA, &lt;a href=&quot;https://www.youtube.com/watch?v=2rKEveL55TY&quot;&gt;Micro-Service Architecture&lt;/a&gt;, Heroku’s &lt;a href=&quot;http://rubyconf2008.confreaks.com/lightweight-web-services.html&quot;&gt;Lightweight Web Services&lt;/a&gt;, and Github’s &lt;a href=&quot;http://zachholman.com/posts/scaling-github-employees/&quot;&gt;building mini-apps&lt;/a&gt; is the solution all larger apps end up moving towards.&lt;/p&gt;

&lt;p&gt;While I still place a incredibly high value on the readability of code, and often find a Collection#each accumulating into another variable more readable than Collection#inject. I think developers should error on the side of less code, unless there are clear improvements to having more code. Especially when trying to refactor to improve the readability of the code.&lt;/p&gt;

&lt;p&gt;Based on this I feel like refactorings that are introducing more lines of code for the sake of readability are often just moving the complexity around. It often feels like a win, but it has a good probability to introduce more bugs. In some cases is actually only more clear to the refactoring author.&lt;/p&gt;

&lt;p&gt;Related to refactoring making code larger, while small testable methods are great. If each method just is another layer of abstraction deferring to yet another method before the meat of the problem is solved, it is less likely to be a improvement. Abstractions have a cognitive load and each leaky abstraction layer is another potential buggy line of code. Design patterns can be great but knowing when they are overkill takes time.&lt;/p&gt;

&lt;p&gt;I am not saying all refactoring is bad, in fact I think bug LOC ratios increase the importance of some refactorings. A refactoring that doesn’t make the code easier to read (and likely not any more difficult) but delivers the same functionality while reducing the total LOC is a big win. A win in terms of maintainability, it reduces the far too frequent bugs where multiple files/functions need to be ‘kept in sync’ to ensure correct functionality.&lt;/p&gt;

&lt;p&gt;One failing in my keep the project as small as possible idea, is user input and UX. All those dedicated to succinct code, please show me great examples of user input error handling and complex conditional view layers. I still haven’t seen good ways to approach these issues short of writing more code and trying to make all of the conditions as clear and small as possible. (Often products made for developers solve this by only allowing for tiny number of possible conditions. I don’t think that approach works in reality when working with designers and UX teams, who often have clear use cases for each conditional)&lt;/p&gt;

&lt;h6 id=&quot;additional-sources&quot;&gt;Additional Sources&lt;/h6&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.informit.com/articles/article.aspx?p=30306&quot;&gt;Software Quality Metrics Overview, detailing defect tracking&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://amartester.blogspot.com/2007/04/bugs-per-lines-of-code.html&quot;&gt;Source of McConnell’s defects per lines of code&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.stevemcconnell.com/articles/art06.htm&quot;&gt;McConnell, also thinks bugs ratios get worse with larger projects&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://confreaks.com/videos/1121-gogaruco2012-cruft-and-technical-debt-a-long-view&quot;&gt;While SOA helps solve the large code issue it introduces new complexities, covered a bit in Wycat’s code cruft talk&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.kitchensoap.com/2012/10/25/on-being-a-senior-engineer/&quot;&gt;Section ‘Novices versus Experts’, might explain the more ridged verbose earlier code&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;(1) Obviously different bugs have different costs, and critical path bugs are much worse than minor presentational bugs. I am ignoring this issue to get to the larger point.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Bitnami EC2 https and environment variables setup"</title>
   <link href="https://www.mayerdan.com/ruby/2012/10/28/bitnami-ec2-https-and-env-vars"/>
   <updated>2012-10-28T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/ruby/2012/10/28/bitnami-ec2-https-and-env-vars</id>
   <content type="html">
&lt;p&gt;I have a project where I am using the &lt;a href=&quot;http://bitnami.org/&quot;&gt;Bitnami ruby stack&lt;/a&gt;. I used bitnami, because I wanted to avoid having to deal with a bunch of server setup just to play around with a box. It has worked well, but I have had some stumbling blocks, originally I had trouble &lt;a href=&quot;http://mayerdan.com/ruby/2012/10/21/installing-nokogiri-on-bitnami-ec2/&quot;&gt;installing nokogiri on Bitnami boxes&lt;/a&gt;, after getting past that I still needed to search around for solutions to setting up environment variables on Bitnami boxes. Then finally I needed to look up configuring https for my new box so I could pass secure API keys.&lt;/p&gt;

&lt;h2 id=&quot;addining-environment-variables-on-bitnami-servers&quot;&gt;Addining environment variables on Bitnami servers&lt;/h2&gt;

&lt;p&gt;To add Bitnami environment variables for the Bitnami user (the default login user), just update the standard users &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.bashrc&lt;/code&gt; located at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/home/bitnami/.bashrc&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;To add environment variables for web processes and servers started by Bitnami’s default boot up, add variables to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/opt/bitnami/scripts/setenv.sh&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Add environment variables the standard way to either or both configs&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 SOME_ENV_VAR=&apos;accessible to servers&apos;
export ANOTHER_VAR=&apos;still here&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In my case I am using a Sinatra server behind apache2 served by passenger, so I can then just access the variables with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ENV[&apos;SOME_ENV_VAR&apos;]&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;setting-up-apache2--passenger-for-https-on-bitnami-stack&quot;&gt;Setting up Apache2 / Passenger for https on Bitnami stack&lt;/h2&gt;

&lt;p&gt;Since I was passing some API keys over to my Sinatra server, I wanted to make sure I could hit the API via https. It turns out Bitnami has pretty good docs on setting up a &lt;a href=&quot;http://wiki.bitnami.org/Components/Apache#How_to_enable_SSL_to_access_through_https.3f&quot;&gt;Bitnami Apache2 server with http enabled&lt;/a&gt;. For me basically just the few steps enabled https&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;emacs /opt/bitnami/apache2/conf/httpd.conf
#uncomment LoadModule ssl_module modules/mod_ssl.so
#uncomment Include conf/extra/httpd-ssl.conf
emacs /opt/bitnami/apache2/conf/extra/httpd-ssl.conf
# update the default virtual host for https traffic listening on 442

&amp;lt;VirtualHost _default_:443&amp;gt;
  ServerName www.server.com
  DocumentRoot /opt/bitnami/apps/my_sinatra_app/public
  &amp;lt;Directory /opt/bitnami/apps/my_sinatra_app/public&amp;gt;
      Allow from all
      Options -MultiViews
  &amp;lt;/Directory&amp;gt;

#   General setup for the virtual host                                                                                               
DocumentRoot &quot;/opt/bitnami/apps/my_sinatra_app/public&quot;
ServerName www.server.com:443
ServerAdmin you@example.com
ErrorLog &quot;/opt/bitnami/apache2/logs/error_log&quot;
TransferLog &quot;/opt/bitnami/apache2/logs/access_log&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That got me part of the way there, but the instructions on creating a self-signed cert could have been a bit better and more clear, here is what I ran to generate my cert.&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;$ cd /opt/bitnami/apache2/conf
$ openssl genrsa -des3 -out privkey.pem 1024 
$ openssl req -new -key privkey.pem -out cert.csr
$ openssl rsa -in privkey.pem -out server.key
$ openssl x509 -in cert.csr -out server.crt -req -signkey server.key -days 365
$ apachectl restart    
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That should do it, you should now be able to reach the same Ruby app behind https that was initially running on http. This let me get to the next step of playing around with my personal project, but really I need to get all of the Bitnami box setup automated with a chef script, but that is for another time.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Fix to install Nokogiri on bitnami EC2 instance</title>
   <link href="https://www.mayerdan.com/ruby/2012/10/21/installing-nokogiri-on-bitnami-ec2"/>
   <updated>2012-10-21T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/ruby/2012/10/21/installing-nokogiri-on-bitnami-ec2</id>
   <content type="html">
&lt;p&gt;Recently I set up a ec2 server, using the great &lt;a href=&quot;http://bitnami.org/&quot;&gt;Bitnami ruby stack&lt;/a&gt;. I used bitnami, because I wanted to avoid having to deal with a bunch of server setup just to play around with a box. It worked great, but for some reason, I couldn’t get the Nokogiri gem to install on the box. After trying many many solutions, I found this post on &lt;a href=&quot;http://stackoverflow.com&quot;&gt;StackOverflow&lt;/a&gt; about &lt;a href=&quot;http://stackoverflow.com/questions/9725679/installing-nokogiri-1-5-2&quot;&gt;installing nokogiri 1.5.2 on bitnami&lt;/a&gt;. This was exactly what I was looking for, so my solution came down to, running the following command, with all libs, etc explicitly passed in.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo gem install nokogiri -- --with-xml2-dir=/opt/bitnami/common --with-xslt-dir=/opt/bitnami/common --with-xml2-include=/opt/bitnami/common/include/libxml2 --with-xslt-include=/opt/bitnami/common/include --with-xml2-lib=/opt/bitnami/common/lib --with-xslt-lib=/opt/bitnami/common/lib&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Thanks StackOverflow, and thanks Bitnami&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Rails tests gotcha with I18n.locale</title>
   <link href="https://www.mayerdan.com/2012/01/14/Rails-tests-gotcha-with-I18nlocale"/>
   <updated>2012-01-14T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2012/01/14/Rails-tests-gotcha-with-I18nlocale</id>
   <content type="html">
&lt;p&gt;A example of a bad I18n rails test… Found some tests failing in a project because tests in different files would change I18n.locale, changes like this make your tests fail if the tests are not run in the standard order. since this took me awhile to track down, I thought I should share a quick example for others to find. So if you are having a issue like this remember to add a teardown method that resets I18n.locale to the default local.&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;&amp;lt;script src=&quot;https://gist.github.com/1568648.js?file=I18n_bad_test.rb&quot;&amp;gt; &amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Ruby processing: making it snow</title>
   <link href="https://www.mayerdan.com/2011/12/27/Ruby-processing-making-it-snow"/>
   <updated>2011-12-27T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2011/12/27/Ruby-processing-making-it-snow</id>
   <content type="html">
&lt;p&gt;ruby, processing, graphicsI re-found a fun holiday project I did a couple years ago &lt;a href=&quot;https://github.com/danmayer/Processing-Snow&quot;&gt;making snow with Ruby processing&lt;/a&gt;.    &lt;a href=&quot;/assets/snow-processing.jpg&quot;&gt;&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Cleaning up files after hacking</title>
   <link href="https://www.mayerdan.com/2011/12/17/Cleaning-up-files-after-hacking"/>
   <updated>2011-12-17T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2011/12/17/Cleaning-up-files-after-hacking</id>
   <content type="html">
&lt;p&gt;This blog was hacked awhile ago. It was annoying, I hadn’t updated my blog software for awhile and there was a security hole. It was a good learning experience of how to deal with the intrusion. It was also nice to be able to quickly whip together some scripts to help clean everything up.     This let me see which files were recently modified&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;`find . -type f -mtime -3 | grep -v &quot;/Maildir/&quot; | grep -v &quot;/logs/&quot;``
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The exploiters were modifying my .htaccess and adding a few files of their own, this deleted their files&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;    &amp;lt;script src=&quot;https://gist.github.com/1491781.js?file=gistfile1.txt&quot;&amp;gt; &amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This code helped remove exploit code that was injected into all of the pages on the site.&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;    &amp;lt;script src=&quot;https://gist.github.com/1491783.js?file=remove_code.rb&quot;&amp;gt; &amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After upgrading my software and installing security patches, a bit of other cleanup, and file permission fixes everything was back to normal.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>ActiveRecord / Rails case insensitive ordering</title>
   <link href="https://www.mayerdan.com/2011/11/16/ActiveRecord-Rails-case-insensitive-ordering"/>
   <updated>2011-11-16T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2011/11/16/ActiveRecord--Rails-case-insensitive-ordering</id>
   <content type="html">
&lt;p&gt;Rails, Ruby, ActiverecorderIt is pretty common to want to have the results of a query sorted by order. I wanted the results sorted by name on a user object. Oddly it is hard to find how to make a string based order case insensitive on google. So for other having the same issue, it is simple, just add lower(field) in the order clause and it will sort in a case insensitive manner. Now I don’t care when a user doesn’t capitalize the first letter of their name.    &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;User.order(&apos;lower(name)&apos;).all&lt;/code&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Announcing NothingCalendar</title>
   <link href="https://www.mayerdan.com/2011/11/14/Announcing-NothingCalendar"/>
   <updated>2011-11-14T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2011/11/14/Announcing-NothingCalendar</id>
   <content type="html">
&lt;p&gt;Ruby, Rails, learning, projectsI wanted a small project to play around with. A project that was small and simple that I could actually get done in my free time. An app I would use myself. I ended up deciding to build &lt;a href=&quot;http://nothingcalendar.com&quot;&gt;NothingCalendar&lt;/a&gt;, which was inspired by &lt;a href=&quot;http://calendaraboutnothing.com&quot;&gt;CalendarAboutNothing&lt;/a&gt;. It let’s users track events, anything they feel like tracking over time. Mark off days you worked out, kept to your diet, worked on writing, studied, quit smoking, or anything else.    The idea was a simple way that I could add marks on a calendar on my phone, and have it sync-able and shareable. I also wanted it to be useable when you are offline, since phones loose connection often in metros, planes, lines, etc.    One goal with the project is to keep it is small enough that I can build it and play around with different ideas. It is such a small app, that I could rewrite the entire thing with a new methodology if I wanted. Which is exactly what I plan on starting to do. I wanted a app that I could practice new skills and techniques on, while still being useful, and something I would use on a day to day basis.    I started working on the project with the idea to build up a chain of doing some open source experimental development having a playground to try things and learn. A small app where I could push myself to look at new technologies, and to force myself to release them into a production environment, opposed to another one off script or tests that never see the light of day. I highly recommend forcing yourself to do a bit of development every day, it is recommended by a bunch of smart people (&lt;a href=&quot;http://pragprog.com/the-pragmatic-programmer&quot;&gt;Andrew Hunt and David Thomas’s The Pragmatic Programmer: From Journeyman to Master&lt;/a&gt;, &lt;a href=&quot;http://www.amazon.com/Passionate-Programmer-Remarkable-Development-Pragmatic/dp/1934356344&quot;&gt;Chad Folwer’s The Passionate Programmer&lt;/a&gt;, &lt;a href=&quot;http://www.amazon.com/Hackers-Painters-Big-Ideas-Computer/dp/0596006624&quot;&gt;Paul Graham’s Hackers and Painters&lt;/a&gt;). I was in part inspired by &lt;a href=&quot;http://twitter.com/#!/eee_c&quot;&gt;Chris Strom (@eee_c)&lt;/a&gt;, who has done incredible work keeping up a chain and blogging about what he learns.     NothingCalendar is a testament to what you can do in about 15 minutes a day. That is about how much time I have spent on it most evenings. Just forcing myself to do a bit every day, has led to my longest &lt;a href=&quot;http://calendaraboutnothing.com/~danmayer&quot;&gt;streak on CalendarAboutNothing&lt;/a&gt; (66 days at the moment). Just remembering to code a bit everyday, keeps the project in your mind, keeps you improving it. It also makes refactoring little things a perfect task, you only have a few minutes take something on.  Complete a tiny refactoring of from a todo list and check it in. I frequently just check in #TODO comments, and grep for them when I don’t have much time or an idea of what to work on. After a tiny todo, call it a day and know you will have more time sometime soon to do something more substantial.     Please give NothingCalendar a try, and let me know how it goes or any problems / feature ideas. I will will be working more more on it each day, and trying to rewrite large parts of the app using some new ideas.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>working with Facebook  Dialog Feed and urls with params</title>
   <link href="https://www.mayerdan.com/2011/11/13/working-with-Facebook-Dialog-Feed-and-urls-with-params"/>
   <updated>2011-11-13T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2011/11/13/working-with-Facebook--Dialog-Feed-and-urls-with-params</id>
   <content type="html">
&lt;p&gt;Facebook’s documentation always sucks. There are many examples of working with facebooks dialog feed, but none of them include passing urls that have params. They are all simple examples like share ‘http://example.com’, well if you want to share ‘http://example.com?foo=bar&amp;amp;buzz=baz’ you are kind of out of luck. It took me far longer than I care to admite to figure out the right way to get facebook to share a link that included params.    I was using ruby and rails, creating a link with _url and params, and then trying to encode it and send it to facebook. Below is what ended up working. Note that it seems to be a bit of an odd encoding, but it allows one to send urls with params and encoded ‘?’ and ‘&amp;amp;’ properly through facebook and have the share url include the params your expecting.    &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;facebook_shared\_url = action\_url({:foo =&amp;gt; &apos;bar&apos;, :buzz =&amp;gt; &apos;baz&apos;})facebook\_url = &quot;https://www.facebook.com/dialog/feed?app_id=#{FACEBOOK_CLIENT_ID}&amp;amp;redirect_uri=#{URI.encode(facebook_shared\_url).gsub(&apos;&amp;amp;amp;&apos;,&apos;%26&apos;)}&amp;amp;description=#{text}&amp;amp;name=#{via}&amp;amp;link=#{URI.encode(facebook_shared\_url).gsub(&apos;&amp;amp;amp;&apos;,&apos;%26&apos;)}&quot;&lt;/code&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Grep a filtered list of files</title>
   <link href="https://www.mayerdan.com/2011/11/12/Grep-a-filtered-list-of-files"/>
   <updated>2011-11-12T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2011/11/12/Grep-a-filtered-list-of-files</id>
   <content type="html">
&lt;p&gt;I am working on a project that has view formats. Frequently when grepping the project, I want to grep only one format. I added a couple bash aliases to help me pipe a list of files to grep. Grepping through a specific list of files can be useful in many situations. Here is how to grep a list of files or set up quick aliases to make it easy to do.    First you need to get a list of files, in my case I do this with find and looking for the touch format    &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;alias find_touch_views=&apos;find app/views/ -name &quot;*.touch.erb&quot;&apos;&lt;/code&gt;    This generates the list of files you want to grep, now you just pipe that list to grep using xargs to grep each file for a specific word.    &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;alias grep_touch_views=&apos;find_touch_views | xargs grep -Ri &quot;$0&quot;&apos;&lt;/code&gt;    in the end this lets me run    &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grep_touch_views touch-nav&lt;/code&gt;    Modify these to fit your needs and add them to your ~/.bash_profile    &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;alias find_touch_views=&apos;find app/views/ -name &quot;*.touch.erb&quot;&apos;alias grep_touch_views=&apos;find_touch_views | xargs grep -Ri &quot;$0&quot;&apos;&lt;/code&gt;    which gives me a list of all the views that contain touch-nav.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Git clone from local filesystem to fetch local branches</title>
   <link href="https://www.mayerdan.com/2011/10/29/Git-clone-from-local-filesystem-to-fetch-local-branches"/>
   <updated>2011-10-29T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2011/10/29/Git-clone-from-local-filesystem-to-fetch-local-branches</id>
   <content type="html">
&lt;p&gt;Github, Git, environment, So I am working on a project that has a very long test suite. Yes, that is a problem and we should start to do something about it. For now that problem exists but it leads to wanting to do some weird things. I wanted to work on code in a branch, while my tests were running in another. I had cloned the project twice so I had two versions, of the project, the issue was the branch I wanted to work in hadn’t been pushed remote. I then realized since it is git I could easily solve the problem by pointing my second version of my project at the first and fetching all of my local branches! Git is pretty great that you can solve things like this. It also means you could easily just run git off a shared network filesystem, no reason to really setup a fancy git server.    Here are the steps make a second version of a project and then fetch the local branches from your first version    # if you haven’t clone a second version of the project do that from the filesystem or the git server&amp;gt; cd project_2&amp;gt; git clone /Users/danmayer/projects/project_1&amp;gt; Cloning into project_1…    # in the second version of your project add a remote repo pointing at the original version of the project on your filesystem&amp;gt; cd project_1&amp;gt; git remote add local /Users/danmayer/projects/project_1/&amp;gt; git fetch local&amp;gt; git branch #=&amp;gt; a list of the branches on your first projects directory    This is a pretty simple thing to do, but when I ran into the issue it took me a bit of time to realize how simple it was to solve this problem. It also just illustrates just how versatile git really is.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Simplifying new gemset creation for RVM</title>
   <link href="https://www.mayerdan.com/2011/10/23/Simplifying-new-gemset-creation-for-RVM"/>
   <updated>2011-10-23T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2011/10/23/Simplifying-new-gemset-creation-for-RVM</id>
   <content type="html">
&lt;p&gt;Ruby, environment, developmentI found myself annoyed that each time I created a new project I had to go through the same steps to create and setup my gemset. I am also trying to be better about automating things I do over and over. I decided I should put something in my .bash_profile to simplify my frequent task of making a new gemset on a brand new project.     The manual way I ended up creating gemsets in the past, was:* cd to project_directory* rvm use ree (my preferred local ruby run time)* rvm gemset create projectGemSet* echo ‘rvm use ree@projectGemSet’ &amp;gt; .rvmrc* cd ../* cd project_directiory* agree to trust the .rvmrc file    That is pretty simple, but gets a bit annoying to do over and over. I figured I could very quickly whip up a solution in my bash profile. Unfortunately, due to my shell scripting skills kind of sucking, I ended up using Ruby.    Basically after adding my new ruby script to my .bash_profile, I can now just type ‘init_gemset newgemset’, and I will be left in my project using my new gemset. The final script is below.&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;    &amp;lt;script src=&quot;https://gist.github.com/1257690.js?file=init_gemset.rb&quot;&amp;gt; &amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;One thing to note is the time saved with automating a repetitive task vs. time spent to automate it. In the end because of spending a decent amount of time trying to write this as a shell script without using Ruby. I likely spent more time getting this script working, than it will ever save me. It also isn’t even fully reusable for other people in it’s current state (the .bash_profile function needs a full path to the ruby script in it, and my preferred RVM ruby is hard coded). It could be made more easily reusable / sharable (likely making it a gem, or sending a pull request to RVM itself with this as a feature). Doing this the fully ‘right’ way would just add more time spent, on something I initially did to save a few moments.    I wanted to get back to what I was originally working on, and I had something that would work for me. So I will leave it here as a simple gist. Working on this also made me think about what is worth spending time automating. In terms of savings this likely wasn’t worth much, but there are many times that a repetitive task is really begging for automation.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>ActiveRecord Association objects can act weird (bug in association#any?)</title>
   <link href="https://www.mayerdan.com/2011/10/02/ActiveRecord-Association-objects-can-act-weird-bug-in-associationany"/>
   <updated>2011-10-02T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2011/10/02/ActiveRecord-Association-objects-can-act-weird-bug-in-associationany</id>
   <content type="html">
&lt;p&gt;Rails, RubyI found a weird issue when working with &lt;a href=&quot;http://apidock.com/rails/ActiveRecord/Associations/AssociationProxy&quot;&gt;ActiveRecord AssociationProxy objects&lt;/a&gt;, they weren’t proxying the method correctly.    The issue arose when trying to use the #any? method on an array which should be true when empty. When calling this on a AssociationProxy object that has 0 items it was returning false. Oddly enough inspecting or to_s the association and then calling #any? resulted in the correct behavior. It was so weird though that after looking at other options I ended up using the present?, which always behaved how I expected it to. Even weirder is the issue only presented itself in the rails server debug and production modes. It always acted as one would expect in rails console. I started to dig in more, but ended up yak shaving a lot of other stuff to try to learn exactly what the cause was. If anyone understands the issue better, I would be happy to know more.    Until then, be aware that at least in the 2.3.X versions of rails, ActiveRecord AssociationProxy#any? acts funky and should not be trusted. Use a work around like present? or length &amp;gt; 0 (both worked correctly in console and on dev/production server environments). This is one of these issues where there is so much going on in the rails stack that it is hard to determine where the issue lies.    So for those googling this issue, beware AssociationProxy.any? on has_many relationships.    Example code:&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;&amp;lt;script src=&quot;https://gist.github.com/1250980.js?file=AR_bug.rb&quot;&amp;gt; &amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Google Closure Compiler And Ruby</title>
   <link href="https://www.mayerdan.com/2011/07/04/Google-Closure-Compiler-And-Ruby"/>
   <updated>2011-07-04T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2011/07/04/Google-Closure-Compiler-And-Ruby</id>
   <content type="html">
&lt;p&gt;I have repeatedly needed to compress / minify a collection of javascript files. Which I had been doing by hand with &lt;a href=&quot;http://closure-compiler.appspot.com/home&quot;&gt;Google’s closure compiler&lt;/a&gt;. I started to get annoyed with this process and went looking for a better solution. If you are using Rails, there is a great solution using &lt;a href=&quot;http://documentcloud.github.com/jammit/&quot;&gt;Jammit&lt;/a&gt; asset packager library for Rails. I was not using rails, and just needed something that could write the files on demand. A bit of quick searching came up withh this post &lt;a href=&quot;http://tinyhippos.com/2010/01/08/compressing-javascript-code-with-google-closure-compiler-and-ruby/&quot;&gt;Compressing Javascript Code With Google Closure Compiler And Ruby&lt;/a&gt;. It basically met my needs, but needed to be modified a bit to take a collection of files and write that collection of compresses files back to disk.    Here is the solution I ended up with:&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;    &amp;lt;script src=&quot;https://gist.github.com/1063584.js&quot;&amp;gt; &amp;lt;/script&amp;gt;    
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Emacs swap buffer window method</title>
   <link href="https://www.mayerdan.com/2011/06/05/Emacs-swap-buffer-window-method"/>
   <updated>2011-06-05T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2011/06/05/Emacs-swap-buffer-window-method</id>
   <content type="html">
&lt;p&gt;So I really like split screen in emacs. I am in that mode pretty much all the time. One thing that annoyed me was opening a file on the side of the screen I wanted. Sometimes I want code left, tests right others it is project A left project B right or often server code left, client code right. Anyways I often opened a file on the wrong side of the screen and wanted to move the buffer. Previously I always closed the buffer, then switched to the other emacs window (C-x o) then reopened the file. This annoyed me to know end, so I finally went looking for a solution. I found something that was close, but not what I wanted, which &lt;a href=&quot;http://stackoverflow.com/questions/1774832/how-to-swap-the-buffers-in-2-windows-emacs&quot;&gt;swaps two buffers positions left and right&lt;/a&gt;, so I started with that code and modified it to do just what I want. I then bound the function to “C-x /” which wasn’t in use and I can now swap buffer windows with ease. Also, kind of fun to do a bit of lisp/emacs hacking and breaking out of my Ruby world a bit.&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;    &amp;lt;script src=&quot;https://gist.github.com/1009137.js?file=swap_buffer_window.el&quot;&amp;gt; &amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you notice I have it also close the current tab for the buffer and move the cursor context to the new buffer on the other side of the screen. Most people hate and don’t use tabs in emacs so that one tab closing could be commented out. I love tabs because I am a very spacial person and the tabs let me keep the physical location of the buffers in my head. This was written to run in Aquamacs, but will work equally well in standard emacs. Just drop it in your .emacs file or emacs config directory.     Code can be found at this gist, &lt;a href=&quot;https://gist.github.com/1009137&quot;&gt;Emacs swap buffer window&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Force all Sinatra traffic to https</title>
   <link href="https://www.mayerdan.com/2010/08/07/Force-all-Sinatra-traffic-to-https"/>
   <updated>2010-08-07T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2010/08/07/Force-all-Sinatra-traffic-to-https</id>
   <content type="html">
&lt;p&gt;I wanted to have all traffic on a site go through https. Since the site was on heroku. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@env[&apos;rack.url_scheme&apos;])==&apos;https&apos;&lt;/code&gt; wasn’t a sufficient way of detecting if on https. You need to also check &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(@env[&apos;HTTP_X_FORWARDED_PROTO&apos;]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Below I wrote a simple before filter which you could place in the production configuration. After talking with the author of rack-ssl-enforcer I got that patched to work on heroku as well. So that is an even simpler option.&lt;/p&gt;

&lt;p&gt;Anyways, I spent far to long googling sinatra https, sinatra ssl, sinatra over https, sinatra secure, and heroku https, and couldn’t find good pages on this for awhile. So here is the info for anyone else wanted to host secure Sinatra pages on heroku.&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;      &amp;lt;script src=&quot;http://gist.github.com/512887.js?file=sinatra_https_redirect.rb&quot;&amp;gt; &amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Adding Custom JavaScript Handlers for PhoneGap Android</title>
   <link href="https://www.mayerdan.com/2010/07/10/Adding-Custom-JavaScript-Handlers-for-PhoneGap-Android"/>
   <updated>2010-07-10T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2010/07/10/Adding-Custom-JavaScript-Handlers-for-PhoneGap-Android</id>
   <content type="html">
&lt;p&gt;One of the advantages I mentioned about PhoneGap, was being able to write custom native code, and interface with it via JavaScript. Their Aren’t great example of how to do this out there, so I figured a quick blog post was in order.    I search around for how to do this when getting started with PhoneGap, and the best example I could find was, &lt;a href=&quot;http://www.somms.net/2010/05/28/native-messages-for-android-phonegap/&quot;&gt;Native messages for Android PhoneGap&lt;/a&gt;. Following this example, I was able to get my own Javascript interface working.    Basically you create a class (using java for android example):&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;&amp;lt;script src=&quot;http://gist.github.com/470919.js?file=CustomJS.java&quot;&amp;gt; &amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I create this private class in the main Java class which extends DroidGap (the class that boots up PhoneGap and creates the webview UI).&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;&amp;lt;script src=&quot;http://gist.github.com/470920.js?file=extendDroidGap.java&quot;&amp;gt; &amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then in your html you can call in javascript bridged Java functions like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;var myData = CUSTOM.getData();&lt;/code&gt; which will return the java data (in this case a string) to the javascript. I have used the JS bride to begin new Android Activities, Close the WebView and move entirely to Native code. Background the WebUI to do something native for awhile, then return the WebView back to the front, Etc.&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;&amp;lt;script src=&quot;http://gist.github.com/470922.js?file=customJSPhoneGap.js&quot;&amp;gt; &amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This can be done similarly for the iPhone and Objective-C to Javascript with PhoneGap. This lets you leverage extra native flexibility while still building the majority of your app as JS, CSS, and HTML.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Mobile Development and My Recommendation for PhoneGap</title>
   <link href="https://www.mayerdan.com/2010/07/06/Mobile-Development-and-My-Recommendation-for-PhoneGap"/>
   <updated>2010-07-06T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2010/07/06/Mobile-Development-and-My-Recommendation-for-PhoneGap</id>
   <content type="html">
&lt;p&gt;In the last month or so I have been exploring mobile development. I wrote basic native Android and iPhone apps. I also build cross platform apps using Rhodes, Titanium, and PhoneGap. I thought it would be good to share my thoughts and experiences on the mobile dev space.    If you want full control and are doing a lot of local heavy processing (CPU bound) native is the way to go. I program to the web data without access I don’t do much interesting. When the whole app basically relies on live data I feel javascript, HTML, and CSS is the way to go. Not only am I already familiar with it, but basically the app can share code with the mobile site. Also, using JS, HTML, and CSS gives the most flexibility in terms of supporting multiple devices. Since I wasn’t using anything specific to a single mobile device or really pushing the hardware, I quickly moved away from fully native apps. It wasn’t worth the time and extra effort to write nearly the same app in Java, Objective C, and whatever else. Game developers, and those doing a bunch with video, photos, etc are probably most interested in native apps and probably won’t be as interested in other options.    This lead me to explore &lt;a href=&quot;http://rhomobile.com/products/rhodes/&quot;&gt;Rhodes&lt;/a&gt;, &lt;a href=&quot;http://www.appcelerator.com/&quot;&gt;Titanium&lt;/a&gt;, and &lt;a href=&quot;http://phonegap.com&quot;&gt;PhoneGap&lt;/a&gt; as alternatives to purely native apps. All of which let &lt;!--more--&gt; you leverage HTML and CSS for building a UI.    Rhodes lets you write code in Ruby using a MVC framework. You leverage html (erb/haml) views using a Ruby back end to make remote requests, interact with a DB, and interact with native phone components. It is a cool framework, and obviously since it is Ruby, I enjoyed writing the code. However, it obscured the actual process of making an app for a device a bit much for my comfort. If something went wrong anywhere in the stack or if you wanted to break out of the Rhodes way to do something native on a platform it was difficult to do. It tries to manage the entire app development end to end hiding any per device customizations away from you. Since I wanted to be able to use some existing libraries for Android and iPhones, this limitation was a dealbreaker.    Titanium and PhoneGap use the same basic approach as each other providing a javascript library, which interfaces with native code for each device. You write an app in JS/HTML/CSS and make calls to the JS library to interact with native elements like the accelerometer, GPS, buttons, etc. I think it’s a big win to be able to write most of the app in familiar web languages basically utilizing Ajax and local storage to give a nice mobile experience. Getting up and running on either Titanium or PhoneGap was painless. Again, in the end I wanted more control over some native components which caused me to choose PhoneGap over Titanium. While the concept is the same for both of these products, their approach is very different.    Titanium is a bit more all or nothing of an approach, you develop in Titanium’s little world and it is best to not go out ofit. PhoneGap takes a extremely lightweight and minimalist approach to getting your project running on each device. It gives you basically the bare necessities and gets out of your way. PhoneGap doesn’t try to configure the build environment and it doesn’t manage simulators. To create a app with PhoneGap for any mobile platform you point to the same web directory (HTML, CSS, JS, and images), then generate a native app for the device. At that point you have the same native app project you would have if you were to start from scratch but you have a few nice libraries and code started for you. If you don’t like the way PhoneGap does something, that is fine delete it, override it, extend it. You need to do something native PhoneGap doesn’t support add your own JS handlers and interact with new or existing native code. I needed to allow for FaceBook connect login on Android, which PhoneGap doesn’t support no problem. FaceBook maintains a Android SDK, which I imported into my PhoneGap project and added JS handlers which call to the FaceBook Android native code. There is a bug in Androids SSL cert verification for WebKit that was causing errors for HTTPS access, drop into java override the SSL Cert verification and fixed that. This would be difficult to do with Rhodes or Titanium because they don’t allow you easily write and interact with your own native code. The project has to work only through the API provided to you to stay cross compatible. Which is great in some ways, but very limiting if you want to break out and just tweak a few little things.    I have been extremely happy working with PhoneGap, it rocks. It lets you build a native mobile app as a locally cached and privileged mobile website. You can leverage existing mobile site code for the native app. It will still be better than a pure website experience. It lets you piggyback on skill web developers already have. It is a great way to make use of HTML5  local DBs, local data, and HTML templates. It encourages you follow best practices for building a mobile site that works offline and requests only minimal JSON so it can fast over slow internet.     Before building an app with PhoneGap I recommend building a mobile site. Learn about best practices for the mobile web. If you are new to developing sites for mobile, I highly recommend following the recommendations from &lt;a href=&quot;http://www.engineyard.com/video/12678746&quot;&gt;Yehuda Katz on building great mobile sites&lt;/a&gt;. He discusses some things specific to Rails 3, but mostly just good JS practices.    In many ways when building a PhoneGap app you are just building a mobile website. You have access to utilizing some native things that you can’t with pure HTML/CSS. Local storage is a good example, it is supported by HTML5, but the earlier Android webkit didn’t support it. Using PhoneGap you have local storage on old devices and it can use standard HTML5 local storage on newer devices.    Last but not least, PhoneGap has a great &lt;a href=&quot;http://www.phonegap.com/community&quot;&gt;active community&lt;/a&gt;. Drop into #phonegap on freenode and people are always around to help out. The mailing list has a constant flow of questions and answers. While &lt;a href=&quot;http://www.phonegap.com/docs&quot;&gt;PhoneGap docs&lt;/a&gt; are a little out of date, there are people actively working on improving them. There are many example applications and open code on github to check out how to use PhoneGap. Also, the code is small and simple enough it is a great project to get involved in and give back. After working with it awhile, you will start to understand the internals and could help contribute patches, features, and plugins to the community.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Ruby Fitbit Api Has Been Updated</title>
   <link href="https://www.mayerdan.com/2010/07/03/The-Ruby-fitbit-API-has-been-updated"/>
   <updated>2010-07-03T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2010/07/03/The-Ruby-fitbit-API-has-been-updated</id>
   <content type="html">&lt;p&gt;—| &lt;!--more--&gt; 
layout: posttail
authors: [“Dan Mayer”]
title: “The Ruby-fitbit API has been updated”
category:
tags: []
—&lt;/p&gt;

&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;More about it can be found on here, &lt;a href=&quot;http://github.com/danmayer/ruby-fitbit&quot;&gt;Ruby-fitbit api on github&lt;/a&gt;. Also, the original announcement can be found on &lt;a href=&quot;/assets/2010/05/ruby_fitbit_api.php&quot;&gt;my blog&lt;/a&gt;.    Basically there is now detailed access to any of the data types for any given day. This can be used to backup your fitbit or gather the data for your own charting purposes. Which I will likely start taking advantage of soon for my fitbit blog widget.    Example:    ~/projects/ruby-fitbit(master) &amp;gt; ruby bin/ruby-fitbit MYEMAIL MYPASS steps 06/22/2010Calories Burned 1012Steps Taken 0Milkes Walked .00Activity Levels Durations:Sedentary 16hrs 7minLightly 0minFairly # =&amp;gt; {fitbit.fairly_active}Very 0minsteps data for data Tue Jun 22 00:00:00 -0400 2010+——————————–+——-+&lt;/td&gt;
      &lt;td&gt;0&lt;/td&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;+——————————–+——-+&lt;/td&gt;
      &lt;td&gt;Tue Jun 22 21:50:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;91.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 05:05:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 16:15:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 10:40:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 19:55:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;69.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 14:20:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;15.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 08:45:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;1.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 03:10:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 23:35:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 01:15:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 18:00:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 12:25:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 06:50:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 21:40:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;11.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 10:30:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 04:55:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 16:05:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 19:45:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;543.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 14:10:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 08:35:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;347.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 03:00:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 23:25:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 01:05:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 17:50:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 12:15:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 06:40:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 21:30:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;25.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 10:20:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 04:45:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 15:55:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 19:35:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;19.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 14:00:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 08:25:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 02:50:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 23:15:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 06:30:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 00:55:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 17:40:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 12:05:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 21:20:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;23.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 15:45:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 10:10:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 04:35:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 19:25:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;535.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 02:40:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 13:50:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 08:15:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 23:05:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 11:55:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 06:20:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 00:45:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 17:30:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 21:10:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 15:35:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 10:00:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 04:25:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 19:15:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;595.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 08:05:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 02:30:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 13:40:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 22:55:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 17:20:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 11:45:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 06:10:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 00:35:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 21:00:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 04:15:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 15:25:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 09:50:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 19:05:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;425.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 13:30:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 07:55:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 02:20:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 22:45:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 00:25:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 17:10:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;22.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 11:35:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 06:00:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 20:50:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 09:40:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 04:05:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 15:15:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 18:55:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;62.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 13:20:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 07:45:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 02:10:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 22:35:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 05:50:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 00:15:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 17:00:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;35.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 11:25:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 20:40:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 15:05:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 09:30:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 03:55:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 18:45:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 02:00:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 13:10:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 07:35:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 22:25:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 11:15:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 05:40:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 00:05:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 16:50:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 20:30:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 14:55:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 09:20:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 03:45:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 18:35:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 07:25:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 01:50:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 13:00:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;91.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 22:15:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;445.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 16:40:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 11:05:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 05:30:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 20:20:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 14:45:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 09:10:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 03:35:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Wed Jun 23 00:00:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 07:15:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 01:40:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 18:25:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 12:50:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 22:05:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;80.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 16:30:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 10:55:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 05:20:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 20:10:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 03:25:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 14:35:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 09:00:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;370.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 23:50:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 12:40:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 07:05:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 01:30:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 18:15:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 21:55:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;69.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 16:20:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 10:45:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 05:10:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 20:00:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 08:50:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;13.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 03:15:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 14:25:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 23:40:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 18:05:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 12:30:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 06:55:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 01:20:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 21:45:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;17.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 05:00:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 16:10:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 10:35:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 19:50:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;155.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 14:15:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;40.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 08:40:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;203.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 03:05:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 23:30:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 01:10:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 17:55:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 12:20:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 06:45:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 21:35:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;53.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 10:25:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 04:50:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 16:00:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 19:40:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;438.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 14:05:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 08:30:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;12.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 02:55:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 23:20:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 06:35:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 01:00:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 17:45:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 12:10:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 21:25:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;32.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 15:50:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 10:15:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 04:40:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 19:30:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;499.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 02:45:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 13:55:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 08:20:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 23:10:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 12:00:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 06:25:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 00:50:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 17:35:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 21:15:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;67.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 15:40:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 10:05:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 04:30:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 19:20:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;604.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 08:10:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 02:35:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 13:45:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 23:00:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 17:25:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 11:50:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;10.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 06:15:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 00:40:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 21:05:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 04:20:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 15:30:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 09:55:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 19:10:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;291.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 13:35:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 08:00:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 02:25:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 22:50:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 17:15:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 11:40:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 06:05:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 00:30:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 20:55:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 04:10:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 15:20:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 09:45:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 19:00:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;161.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 13:25:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 07:50:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 02:15:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 22:40:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 00:20:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 17:05:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 11:30:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 05:55:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 20:45:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 09:35:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 04:00:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 15:10:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 18:50:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 13:15:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 07:40:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 02:05:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 22:30:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 05:45:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 00:10:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 16:55:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;37.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 11:20:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 20:35:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;26.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 15:00:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 09:25:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 03:50:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 18:40:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 01:55:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 13:05:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;75.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 07:30:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 22:20:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;22.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 11:10:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 05:35:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 00:00:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 16:45:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 20:25:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 14:50:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 09:15:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 03:40:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 18:30:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 07:20:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 01:45:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 12:55:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 22:10:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;268.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 16:35:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 11:00:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 05:25:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 20:15:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 03:30:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 14:40:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 09:05:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;74.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 23:55:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 12:45:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 07:10:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 01:35:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 18:20:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 22:00:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 16:25:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 10:50:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 05:15:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 20:05:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 08:55:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 03:20:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 14:30:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;16.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 23:45:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 18:10:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 12:35:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 07:00:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Tue Jun 22 01:25:00 -0400 2010&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;+——————————–+——-+289 rows in setdone&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
</content>
 </entry>
 
 <entry>
   <title>Classy Joe Nassy</title>
   <link href="https://www.mayerdan.com/2010/06/28/Classy-Joe-Nassy"/>
   <updated>2010-06-28T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2010/06/28/Classy-Joe-Nassy</id>
   <content type="html">
&lt;p&gt;This is just a tribute to a developer I work with, saying “keep it classy.” This is classy Joe, AKA classy Joe Nassy.&lt;/p&gt;

&lt;p&gt;&lt;img alt=&quot;classy joe&quot; src=&quot;https://s3.amazonaws.com/wastedbrains/classy-joe.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Another page dedicated to &lt;a href=&quot;http://wastedbrains.com/post/38067591408/classy-joe&quot;&gt;classy joe&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Javascript document.addEventListener PhoneGap deviceready issues</title>
   <link href="https://www.mayerdan.com/2010/06/26/Javascript-documentaddEventListener-PhoneGap-deviceready-issues"/>
   <updated>2010-06-26T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2010/06/26/Javascript-documentaddEventListener-PhoneGap-deviceready-issues</id>
   <content type="html">
&lt;p&gt;I had some issues with the deviceready callback. I had seen some similar posts, and figured I could save someone some time. Basically when using document.addEventListener order matters a lot. It all comes down to exactly when this line is loaded and what is already defined.     &amp;lt;pre&amp;gt;document.addEventListener(“deviceready”, deviceSetup, true);&amp;lt;/pre&amp;gt;    We have custom JS, and originally it was loaded second, so the deviceready event was fired before, the listener was set.&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;    &amp;lt;script src=&quot;lscustom.js&quot; type=&quot;text/javascript&quot; charset=&quot;utf-8&quot;&amp;gt; &amp;lt;/script&amp;gt;
    &amp;lt;script src=&quot;phonegap.js&quot; type=&quot;text/javascript&quot; charset=&quot;utf-8&quot;&amp;gt; &amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After that I was still having an issue and it turns out the method you define register as the callback needs to be defined before you add the listener. So having this in our lscustom.js works.   &lt;br /&gt;
        var deviceSetup = function(){   console.log(“Device Setup”); };
        document.addEventListener(“deviceready”, deviceSetup, true);
        While, the below fails. document.addEventListener(“deviceready”, deviceSetup, true);
        var deviceSetup = function(){   console.log(“Device Setup”); };&lt;/p&gt;

&lt;p&gt;Anyways since I ended up wasting a bit of time searching and debugging this issue, I figured I would share my debugging session.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Code Highlighting on MovableType</title>
   <link href="https://www.mayerdan.com/2010/06/21/Code-Highlighting-on-MovableType"/>
   <updated>2010-06-21T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2010/06/21/Code-Highlighting-on-MovableType</id>
   <content type="html">
&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;def code_highlighting  puts &quot;this is a highlight&quot;end&lt;/code&gt; I found a &lt;a href=&quot;http://blogspot.makotokw.com/portfolio/movabletype/syntaxhighlighter/&quot;&gt;syntaxhighlighter plugin&lt;/a&gt;, which makes it simple to use &lt;a href=&quot;http://alexgorbatchev.com/wiki/SyntaxHighlighter&quot;&gt;SyntaxHighlighter&lt;/a&gt; on Movable Type. After downloading and unzipping the plugin in your MT directory. Just add the &amp;lt;pre&amp;gt; MTSyntaxHighlighterInclude &amp;lt;/pre&amp;gt; tag to your templates and then choose the SyntaxHighlighter format when making a post.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Appcelerator Titanium older SDKs</title>
   <link href="https://www.mayerdan.com/2010/06/16/Appcelerator-Titanium-older-SDKs"/>
   <updated>2010-06-16T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2010/06/16/Appcelerator-Titanium-older-SDKs</id>
   <content type="html">
&lt;p&gt;If you have a need to work with a app on a older SDK Titanium doesn’t make it very easy for you. In fact they don’t seem to have historical files available for download or any info on how to install older SDKs.    You can get a old version of the 0.8.x (in this case 0.8.2) SDK here:&lt;a href=&quot;http://share1t.com/dto0nv&quot;&gt;Titanium 0.8.2 SDK download&lt;/a&gt;    And after a bunch of searching I found that you should move the file on OSX to this location:/Library/Application Support/Titanium/mobilesdk/osx/0.8.2    After that restart Titanium and the SDK should be available to you in the SDK dropdown when you create or import projects.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Ruby Dev Available for Contracting</title>
   <link href="https://www.mayerdan.com/2010/06/12/Ruby-Dev-Available-for-Contracting"/>
   <updated>2010-06-12T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2010/06/12/Ruby-Dev-Available-for-Contracting</id>
   <content type="html">
&lt;p&gt;&lt;a href=&quot;http://avdi.org/devblog&quot;&gt;Avdi&lt;/a&gt; a ex-Devver, developer, has just announced that &lt;a href=&quot;http://avdi.org/devblog/2010/06/11/open-for-business/&quot;&gt;ShipRise LLC&lt;/a&gt; is open for business. He is looking to do freelancing/contracting for Ruby and other hard development problems.  I can attest to Avdi’s skill in breaking down hard problems (like distributed test running) into elegant simple solutions. If you are looking for a great dev to work with you on a project, ping him.    Besides Ruby and general coding, Avdi is ready to help with “Dispersed team facilitation”, which he has long term experience with. If you have a small distributed team of developers, he can help get everyone on the right path to work together, while being apart. He is gather a ton of information to help remote workers at his new blog &lt;a href=&quot;http://wideteams.com/&quot;&gt;Wide Teams&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>New gig at LivingSocial</title>
   <link href="https://www.mayerdan.com/2010/06/03/New-gig-at-LivingSocial"/>
   <updated>2010-06-03T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2010/06/03/New-gig-at-LivingSocial</id>
   <content type="html">
&lt;p&gt;livingsocial, ruby, jobsI just started my new gig at &lt;a href=&quot;http://livingsocial.com&quot;&gt;LivingSocial&lt;/a&gt;. I had been a fan of their product ever since moving to DC. I had loved how it got groups of my friends to get together and plan something. I also really enjoyed how it gave me new ideas of cool things to do in my city. Especially since I just moved to DC and didn’t really know what to do. We have bought deals to restaurants we wanted to try, restaurants we already knew and loved, kennedy center brunch with a group of friends, a wine tour with a group of 5 of us going. We have really enjoyed the savings, but even more so that it helps get us out there doing and trying new things.    Anyways I will be joining the development team, obviously doing Ruby stuff, since that is what I am focused on now. The team is already impressive and has accomplished a lot and scaled the site to request levels I have yet to deal with. I am excited to get to work and learn with the team, while bringing some of my skills from past experiences to help with the team.     Yesterday, I pushed my first code live to production, which made me an official member of the team. Now to dig in and wrap my head around this code base.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Ruby Fitbit API</title>
   <link href="https://www.mayerdan.com/2010/05/26/Ruby-Fitbit-API"/>
   <updated>2010-05-26T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2010/05/26/Ruby-Fitbit-API</id>
   <content type="html">
&lt;p&gt;&lt;a href=&quot;http://fitbit.com&quot;&gt;Fitbit&lt;/a&gt; has been slow to release an official API, so I started work on a screen scraping version. It will become a nice gem interface to the real API when it is released. It is pretty limited at the moment, only able to pull the most basic current days info.     You can check it out here &lt;a href=&quot;http://github.com/danmayer/ruby-fitbit/&quot;&gt;ruby-fitbit&lt;/a&gt; on github.    Example output&amp;lt;pre&amp;gt;~/projects/ruby-fitbit(master) &amp;gt; ruby bin/ruby-fitbit my@email.com MYPASSCalories Burned 834Steps Taken 552Milkes Walked .23Activity Levels Durations:Sedentary 11hrs 28minLightly 19minFairly 16minVery 0min    done&amp;lt;/pre&amp;gt;    I also connected a quick sinatra app on &lt;a href=&quot;http://heroku.com&quot;&gt;Heroku&lt;/a&gt; to it so that I could embed the data in my personal blog as a widget. I will clean that up and release the code for it soon. Here is the widget at the moment. I need to add good CSS that is customizable by the user. It would also be good to make it easy for others to host their own widgets opposed to just my own data.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Converting Movable Type to Tumblr</title>
   <link href="https://www.mayerdan.com/2010/05/23/Converting-Movable-Type-to-Tumblr"/>
   <updated>2010-05-23T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2010/05/23/Converting-Movable-Type-to-Tumblr</id>
   <content type="html">
&lt;p&gt;Continuing my work on converting this blog to be a dev blog, I moved over all non development and computer related posts to my personal blog. I just converted about 1,000 posts and 3,000 comments from MovableType to Tumblr and Disqus.    I decided I would host my personal blog on Tumblr, which has been interesting so far. Sucking the old posts and comments out of MovableType and pushing them to Tumblr wasn’t as easy as I had hoped. Although it gave me a chance to enjoy on of my favorite parts of being a developer. If something doesn’t exist and I want it, I can built it. So I set off to write a script that would take a Movable Type blog export and import all of the posts into Tumblr and push all of the comments to Disqus. While it wasn’t the easiest thing to do, it wasn’t that difficult either. I have slowly built up a collection of various utilities to help port from Movable Type to Wordpress, or from wordpress.org to wordpress.com. Things that help clean up formatting or remove javascript and replace it with something allowed on wordpress.org. I added my conversion script to the inconveniently named &lt;a href=&quot;http://github.com/danmayer/MT_WP_Converter&quot;&gt;MT_WP_Converter&lt;/a&gt; git repo.    &lt;a href=&quot;http://github.com/danmayer/MT_WP_Converter/blob/master/mt_tumblr_converter.rb&quot;&gt;View MovableType to Tumblr Converter Script&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;Quick Gotchas:
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;There are lots of older or bad example code on how to use the disqus API out there, I had a problem posting comments because all of the POST endpoints must end with a ‘/’, which I didn’t know. This forum post helped me &lt;a href=&quot;http://www.ruby-forum.com/topic/180723&quot;&gt;fix an issue posting to disqus with rest client&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;Remember to turn off the facebook publishing for tumblr, before doing the import or testing it. I didn’t remember this and published the same test post on facebook about 40 times. Oops!&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>Improving my Emacs Configurations</title>
   <link href="https://www.mayerdan.com/2010/05/14/Improving-my-Emacs-Configurations"/>
   <updated>2010-05-14T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2010/05/14/Improving-my-Emacs-Configurations</id>
   <content type="html">
&lt;p&gt;Emacs, Aquamacs, emacs starter kit, developmentI have been using Emacs as my default editor for awhile now. My emacs setup was a hodge podge of various plugins, code from friends, blogs, etc… It wasn’t organized and was hard to maintain and fine tune. I decided it was time to get my emacs config under control, and under git so it was portable and easy to revert update etc.    I looked at a couple options for existing Emacs configuration setups for Ruby.    &lt;a href=&quot;http://github.com/technomancy/emacs-starter-kit&quot;&gt;Emacs Starter Kit&lt;/a&gt;&lt;a href=&quot;http://github.com/topfunky/emacs-starter-kit&quot;&gt;Topfunky’s Emacs Starter Kit&lt;/a&gt;&lt;a href=&quot;http://github.com/walter/aquamacs-emacs-starter-kit&quot;&gt;Aquamacs Starter Kit&lt;/a&gt;    In the end I ended up going with &lt;a href=&quot;http://github.com/walter/aquamacs-emacs-starter-kit&quot;&gt;Aquamacs Starter Kit&lt;/a&gt;, namely because I have been a fan of Aquamacs for awhile, and already new my way around it’s system pretty well. The original Starter kit, from Technomancy, doesn’t play nicely with Aquamacs and leads to some issues. Some people have forks that are supposed to work better with Aquamacs, but it seems the kit built from the ground up for Aquamacs works best. Topfunky’s project is based on the original emacs starter kit, but has been customized to work well with &lt;a href=&quot;http://www.apple.com/downloads/macosx/unix_open_source/carbonemacspackage.html&quot;&gt;Carbon Emacs&lt;/a&gt;, which is more cross compatible than Aquamacs, so your emacs config is more likely to be portable to all unix systems as well as OS X with Carbon emacs. This looks like a really interesting option, but I decided I liked some of the nice extra sugar that Aquamacs provides.     First install &lt;a href=&quot;http://github.com/walter/aquamacs-emacs-starter-kit&quot;&gt;Aquamacs starter kit&lt;/a&gt;    There were a few things that I couldn’t stand from the defaults in the Aquamacs Starter Kit and removed also some modifications that I added.     ### Disabling/Removing some Starter Kit features
    You likely installed your starter kit to a location like, ‘/Users/danmayer/Library/Preferences/Aquamacs Emacs/aquamacs-emacs-starter-kit’, go there to find the files for modification.     Disabling the emacs Twitter client, sorry I like my twitter to be entirely separate from my dev environment. Then edit ‘init.el’:&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;;;(autoload &apos;twitter-get-friends-timeline &quot;twitter&quot; nil t);;(autoload &apos;twitter-status-edit &quot;twitter&quot; nil t);;(global-set-key &quot;\C-xt&quot; &apos;twitter-get-friends-timeline);;(add-hook &apos;twitter-status-edit-mode-hook &apos;longlines-mode)&lt;/code&gt;    Disable Ruby-electric, which tries to complete various matched chars and statements as you type like ‘, {}. (), begin/end, if/end, etc… It drives me nuts and breaks my thought process. Edit ‘misc-mode-tweaks.el’:&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;;;(require &apos;ruby-electric);;(add-hook &apos;ruby-mode-hook;;          (lambda nil;;            (require &apos;ruby-electric);;            (ruby-electric-mode);;            (flymake-mode-on)))&lt;/code&gt;    ### Additions to Starter Kit
I saw a Emacs package to add flog score inline to ruby method scores and thought that sounded like a great addition to help keep my methods concise, so I added that, to my .emacs:    &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;;; ruby-complexity flog scores for methods http://github.com/topfunky/emacs-starter-kit/tree/master/vendor/ruby-complexity/(add-to-list &apos;load-path &quot;~/.emacs.d/vendor/ruby-complexity/&quot;)    (require &apos;linum)(require &apos;ruby-complexity)(add-hook &apos;ruby-mode-hook  (function (lambda ()      (flymake-mode)      (linum-mode)      (ruby-complexity-mode))))&lt;/code&gt;    I also liked some of the options from &lt;a href=&quot;http://github.com/defunkt/textmate.el&quot;&gt;defunkt’s Textmate minor mode&lt;/a&gt;. First ‘cd ~/.emacs.d/vendor; git clone git://github.com/defunkt/textmate.el.git’ Again added to my .emacs file:    &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;;; textmate bindings for some nicer key combos for common actions(add-to-list &apos;load-path &quot;~/.emacs.d/vendor/textmate.el&quot;)(require &apos;textmate)(textmate-mode)&lt;/code&gt;    I am just starting to get going with improving my Emacs setup, so I am sure I will likely be making additional changes. The current changes above are checked into &lt;a href=&quot;http://github.com/danmayer/aquamacs-emacs-starter-kit&quot;&gt;my fork&lt;/a&gt; of the project, feel free to pull the above changes if you’d like. If you have any great Emacs tips, tricks, or plugins I should add let me know as I am sure it can just keep getting better, but I am pretty happy for now.    &lt;a href=&quot;/assets/custom_aquamacs.jpg&quot;&gt;&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Introducing Ruby Resume, a project to create and host your resume</title>
   <link href="https://www.mayerdan.com/2010/05/10/Introducing-Ruby-Resume,-a-project-to-create-and-host-your-resume"/>
   <updated>2010-05-10T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2010/05/10/Introducing-Ruby-Resume,-a-project-to-create-and-host-your-resume</id>
   <content type="html">
&lt;p&gt;Ruby, Github, Open source, Programming, Heroku, Markdown, Tools, ResumeThe &lt;a href=&quot;http://github.com/danmayer/Resume&quot;&gt;Ruby Resume project&lt;/a&gt;, is a project I started to make it easier for Ruby developers to create, host, share their resume, and keep it up to date. It is an open source project, that anyone can use to help manage their resume online. It offers a variety of options and you can take or leave any part you wish. Basically, you fork the project, alter some things for your needs, and can contribute interesting additions back to the shared Ruby Resume project.    The project uses Sinatra, Markdown, and a collection of Rake tasks to get the job done.     What does the Ruby Resume project do?- Supports deploying your resume to &lt;a href=&quot;http://heroku.com&quot;&gt;Heroku&lt;/a&gt; in a variety of formats.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Easily deploy your app to any Sinatra compatible host&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Allows simple publishing of your resume to your github personal page.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;It makes it simple to publish your resume as a gem. I got the idea for a &lt;a href=&quot;http://groups.google.com/group/rails-business/msg/68cf8a890c0d4fc8?pli=1&quot;&gt;personal resume gem&lt;/a&gt; from &lt;a href=&quot;http://twitter.com/edavis10&quot;&gt;Eric Davis&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;It uses Markdown, which integrates well with &lt;a href=&quot;http://github.com/blog/553-looking-for-a-job-let-github-help&quot;&gt;Github Jobs&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;It currently suppots HTML, LaTeX, and Markdown. Soon it will support PDF, RTF, etc…
  I built this because I had to publish and start updating my resume again after not dealing with it for 3 years. I wanted something that would simplify the whole process. I wanted my resume under git, and I wanted to be able to quickly deploy any changes online and support a large number of formats.    Anyways check out the &lt;a href=&quot;http://github.com/danmayer/Resume&quot;&gt;source on Github&lt;/a&gt; and the Readme which gives simple instructions on how to use the project. Or what the video below which demonstrates how to use this project for your own resume.    Live Examples:- My &lt;a href=&quot;http://resume.mayerdan.com&quot;&gt;resume on Heroku&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;My &lt;a href=&quot;http://danmayer.github.com&quot;&gt;Github personal page&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;My personal resume gem is, &lt;a href=&quot;http://rubygems.org/gems/danmayer-resume&quot;&gt;danmayer-resume&lt;/a&gt;, which can be installed using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gem install danmayer-resume&lt;/code&gt;, then execute danmayer-resume&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>This blog will be changing soon</title>
   <link href="https://www.mayerdan.com/2010/05/04/This-blog-will-be-changing-soon"/>
   <updated>2010-05-04T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2010/05/04/This-blog-will-be-changing-soon</id>
   <content type="html">
&lt;p&gt;thoughts BlogIt will becoming my development blog. I am currently working on updating and reconfiguring the system, but you can look forward to many changes around here.    If you want to follow and read my more personal blog posts, head over to &lt;a href=&quot;http://wastedbrains.com&quot;&gt;WastedBrains&lt;/a&gt;, which will host my non development blogging and thoughts.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>A archive of my last project</title>
   <link href="https://www.mayerdan.com/2010/05/01/A-archive-of-my-last-project"/>
   <updated>2010-05-01T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2010/05/01/A-archive-of-my-last-project</id>
   <content type="html">
&lt;p&gt;Since Caliper is shutting down on April 30th, I made a little video to show what it did, and explain it’s purpose. It isn’t really promotional, but if people ever want a reference of something I worked on this should help.    &lt;object height=&quot;300&quot; width=&quot;400&quot;&gt;&lt;param name=&quot;allowfullscreen&quot; value=&quot;true&quot; /&gt;&lt;param name=&quot;allowscriptaccess&quot; value=&quot;always&quot; /&gt;&lt;param name=&quot;movie&quot; value=&quot;http://vimeo.com/moogaloop.swf?clip_id=11358149&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=&amp;amp;fullscreen=1&quot; /&gt;&lt;embed allowfullscreen=&quot;true&quot; src=&quot;http://vimeo.com/moogaloop.swf?clip_id=11358149&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=&amp;amp;fullscreen=1&quot; allowscriptaccess=&quot;always&quot; type=&quot;application/x-shockwave-flash&quot; height=&quot;300&quot; width=&quot;400&quot; /&gt;&amp;lt;/embed&amp;gt;&lt;/object&gt;
&lt;a href=&quot;http://vimeo.com/11358149&quot;&gt;Caliper Screencast&lt;/a&gt; from &lt;a href=&quot;http://vimeo.com/user2008106&quot;&gt;dan mayer&lt;/a&gt; on &lt;a href=&quot;http://vimeo.com&quot;&gt;Vimeo&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Dan Mayer Resume</title>
   <link href="https://www.mayerdan.com/2009/04/18/Dan-Mayer-Resume"/>
   <updated>2009-04-18T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2009/04/18/Dan-Mayer-Resume</id>
   <content type="html">
&lt;p&gt;Daniel Mayer dan@mayerdan.com&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Refactoring makes me happy</title>
   <link href="https://www.mayerdan.com/2008/07/29/Refactoring-makes-me-happy"/>
   <updated>2008-07-29T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2008/07/29/Refactoring-makes-me-happy</id>
   <content type="html">
&lt;p&gt;I still get such a kick out of refactoring and shrinking down code. I try not to shrink it to the point that it is harder to read. I think some people get a little overzealous with refactoring and obscure so much of the code that it is no longer that readable, or everything is 20 levels deeper than it should be when you are in a debugger trying to trace a programs execution.    Anyways I was cleaning up a bunch of little TODOs or ugly sections of code that had been building up in some of our files. It felt good, and should make things even simpler moving forward.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Quicksort in Processing.js</title>
   <link href="https://www.mayerdan.com/2008/05/28/Quicksort-in-Processingjs"/>
   <updated>2008-05-28T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2008/05/28/Quicksort-in-Processingjs</id>
   <content type="html">
&lt;p&gt;I always liked computer visualization. I think it is fun and interesting, and drawing Mandelbrot for the first time brought a smile to my face. I always thought I understood sorting, AI, search, and other algorithms best when you could really see them in action. I always enjoyed little videos or applets of a bunch of different sorting algorithms comparing their differences and speeds. I don’t really have time right now to do a whole bunch, but when I saw &lt;a href=&quot;http://ejohn.org/blog/processingjs/&quot;&gt;processing ported to javascript&lt;/a&gt; I thought I really would have to write a visualization for at least one sort just for the fun of playing around with a cool project.    So here is a link to my &lt;a href=&quot;http://mayerdan.com/quicksort.html&quot;&gt;Quicksort in Processing.js&lt;/a&gt;, it will only work in the very newest browsers (FF3, Opera 9.5, and other beta browsers… feel free to try it out on anything worst thing that will happen is just an empty box… but it will not likely do anything on older browsers). Anyways, just some fun coding to play around with… If anyone can figure out how to make the double buffering / frame rate look good I would love a fix to make the animation run smoothly.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>iTunes Stalling on Windows XP</title>
   <link href="https://www.mayerdan.com/2008/02/25/iTunes-Stalling-on-Windows-XP"/>
   <updated>2008-02-25T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2008/02/25/iTunes-Stalling-on-Windows-XP</id>
   <content type="html">
&lt;p&gt;I have been meaning to write this little tutorial ever since I fixed my iTunes on windows XP. I have a pretty good computer (3.4 ghz 1Gb ram), so I was confused when switching from WinAmp to iTunes to find my music randomly stalling. If i was running something process intensive like compiling or even just loading a new program my music would stutter on XP. I search around the web and found that others were having this problem, but not many had a good solution. After browsing around the web, I found that the stuttering in my music was caused when iTunes didn’t get enough of the CPU to process the music file. So when something would jump up and use 90% of my CPU iTunes would happily stop processing the music because it didn’t have a high enough process priority to keep a couple percent of CPU for itself. When playing music in the background iTunes only needs 3%-4% to play the music.    So now knowing the problem I searched for solutions and many just said that every time they started iTunes they would open the Windows Task Manager go to the processes tab and right click on iTunes to set the priority of the process to high. This did in fact work, but now I had to do that every time that I openned iTunes which was really annoying. So I continued looking for a better solution. There are special windows programs out there, most of them cost money, that will monitor which processes are running on the system and automatically change the process priority on any app you tell the system to increase the priority to whatever settings you choose. This seemed like and OK solution but now having an app running all the time checking my processes and just some other app that could cause problems, seemed bad. It also seems that processes aren’t as stable if you switch their priority after they begin, it is best to set the priority as they are started. So off searching for a solution I found that you can start processes in does and pass what priority you want to start the process. So I decided to just hack up a little script which would launch iTunes from dos with a process priority of ‘High’.    to run a windows app at a different process priority than the default you can create a file named “appName.bat” (in this case iTunes.bat), place this file in the same directory as the executable you want to run with a higher priority. Then edit the bat file (you can edit it in notepad). Add this line to the file    start /high iTunes.exe    (or start /priority_setting application.exe)    Save your bat file… Now all you have to do is create shortcuts to the new bat file instead of the shortcuts pointing directly the application.    To create a shortcut right click on your bat file and select ‘Create Shortcut’. If you want the shortcut to have the same icon as the original app instead of the dos icon, right click the shortcut and got to properties, then the ‘Shortcut’ tab, and click Change Icon. Select browse an application to search for an icon in, then select the application you are creating a shortcut for (in this case iTunes.exe), it will display the available icons select the one your want.    Anyways, I hope this helps anyone else having trouble getting audio to play smooth on windows XP with iTunes. I have also used this method to speed up some other processes that I always want running full speed on windows, with pretty good success.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>online resources for computer science students</title>
   <link href="https://www.mayerdan.com/2008/01/20/online-resources-for-computer-science-students"/>
   <updated>2008-01-20T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2008/01/20/online-resources-for-computer-science-students</id>
   <content type="html">
&lt;p&gt;&lt;a href=&quot;http://www.virtualhosting.com/blog/2008/50-killer-online-resources-for-computer-science-students/&quot;&gt;50 killer online resources for computer science students&lt;/a&gt;. I thought it was a decent list and figured I would share it.    Dave sorry their is stuff about computers on my blog, I know it bothers you… You never called me when you were in Denver, so um… were even or something.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Ruby on Rails built in string shortcuts</title>
   <link href="https://www.mayerdan.com/2007/10/16/Ruby-on-Rails-built-in-string-shortcuts"/>
   <updated>2007-10-16T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2007/10/16/Ruby-on-Rails-built-in-string-shortcuts</id>
   <content type="html">
&lt;p&gt;There are some great string munipulation shortcuts in rails. I thought this was a good write up and little collection of some of the most useful functions.    &lt;a href=&quot;http://thinkingrails.blogspot.com/2007/01/stringy-shortcuts-string-methods-ruby.html&quot;&gt;Rails string shortcuts&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Always use lenth on ruby collections</title>
   <link href="https://www.mayerdan.com/2007/09/07/Always-use-lenth-on-ruby-collections"/>
   <updated>2007-09-07T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2007/09/07/Always-use-lenth-on-ruby-collections</id>
   <content type="html">
&lt;p&gt;This bug never got me, but it is a good little ruby best practice to follow. So remember when ever using ruby collections use .length call not the .size call…    &lt;a href=&quot;http://blog.floehopper.org/articles/2007/08/07/why-array-length-is-better-than-array-size&quot;&gt;Why use length on all ruby collections&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Amazing Image Resizing</title>
   <link href="https://www.mayerdan.com/2007/08/21/Amazing-Image-Resizing"/>
   <updated>2007-08-21T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2007/08/21/Amazing-Image-Resizing</id>
   <content type="html">
&lt;p&gt;This is the coolest image resizing I have ever seen, I love that your can resize to erase objects out of photos, awesome…    &lt;a href=&quot;http://www.youtube.com/watch?v=vIFCV2spKtg&amp;amp;eurl=http%3A%2F%2Fwww%2Emilkandcookies%2Ecom%2Flink%2F66481%2Fdetail%2F&quot;&gt;Amazing Image Resizing&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Finished Rails Book</title>
   <link href="https://www.mayerdan.com/2007/08/12/Finished-Rails-Book"/>
   <updated>2007-08-12T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2007/08/12/Finished-Rails-Book</id>
   <content type="html">
&lt;p&gt;I finally finished a reading the rails book all the way through. I keep putting it off and reading other books because reading straight through a programming book is boring. Yippie for finishing that up, and I learn some more little rails tricks that should be helpful.    Next up Linux System Administration and Backup and Recovery!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Using Rails link_to_if</title>
   <link href="https://www.mayerdan.com/2007/07/31/Using-Rails-link_to_if"/>
   <updated>2007-07-31T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2007/07/31/Using-Rails-link_to_if</id>
   <content type="html">
&lt;p&gt;There are a lot of great helper methods in rails, and I need to remember to use more of them, one I over looked for a long time that I have found useful is link_to_if    &amp;lt;%= link_to_if logged_in?, “#{item.name}”, :controller =&amp;gt; ‘items’, :action =&amp;gt; ‘show’, :id =&amp;gt; item %&amp;gt;    replaces the much longer and more verbose:    &amp;lt;%- if logged_in? %&amp;gt;&amp;lt;%= link_to , “#{item.name}”, :controller =&amp;gt; ‘items’, :action =&amp;gt; ‘show’, :id =&amp;gt; item %&amp;gt;&amp;lt;%- else %&amp;gt;&amp;lt;%= item.name %&amp;gt;&amp;lt;%- end %&amp;gt;    I like being able to replace five confusing lines with one single simple solution. The link_to_if helper writes a link if the condition is met, otherwise it just outputs the text that would have been in the link. Very cool.    Also if you want to use &lt;a href=&quot;http://www.rails.cz/articles/2007/07/13/ajax-forms-with-multiple-submit-buttons-bug&quot;&gt;Ajax forms with multiple submit buttons in Rails, here is the solution&lt;/a&gt;.    Lastly if you are trying to write a selector and want to make sure the test is returning the html you expect you can add puts @response.body into the test after the page call and see the response. This has been useful to me a few times.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Highly useful assert_select</title>
   <link href="https://www.mayerdan.com/2007/06/26/Highly-useful-assert_select"/>
   <updated>2007-06-26T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2007/06/26/Highly-useful-assert_select</id>
   <content type="html">
&lt;p&gt;I find myself using the assert_select a lot while doing testing and rails. I have had to keep looking up different examples depending on the situation. I have found that this page seems to have most everything I need when I can’t remember it off the top of my head.    &lt;a href=&quot;http://railsmanual.com/module/ActionController::Assertions::SelectorAssertions&quot;&gt;Rails assert select examples&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Ruby rails testing</title>
   <link href="https://www.mayerdan.com/2007/06/12/Ruby-rails-testing"/>
   <updated>2007-06-12T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2007/06/12/Ruby-rails-testing</id>
   <content type="html">
&lt;p&gt;My roommate made a great post on rails testing so I thought I would share that here.    &lt;a href=&quot;http://blog.pretheory.com/arch/2007/06/adventures_in_testing_part_i_r.php&quot;&gt;Testing faster in ruby on rails&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Ruby email to rss</title>
   <link href="https://www.mayerdan.com/2007/05/20/Ruby-email-to-rss"/>
   <updated>2007-05-20T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2007/05/20/Ruby-email-to-rss</id>
   <content type="html">
&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;A simple ruby email2rss example    When I was searching for some basic ruby scripts to download pop email I came across the &lt;a href=&quot;http://rubyforge.org/projects/email2rss/&quot;&gt;email 2 Rss&lt;/a&gt; project on Ruby forge. Since I noticed there was no code available for the project yet I figured I would make a quick version up and make the code available, so anyone else searching for similar examples would have a starting point.    This code is just a functional example, but doesn’t have any real options or error checking so use at your own risk.    Download the source to &lt;a href=&quot;http://WWW.bandddesigns.com/ml/arch/RubyEmail.zip&quot;&gt;Ruby Email to RSS Feed&lt;/a&gt;    &amp;lt;pre&amp;gt;require ‘net/pop’require ‘rss/maker’    version = “2.0” # [“0.9”, “1.0”, “2.0”]destination = “/archive/emailFeed.xml” # local file to write    #create a new email Feedcontent = RSS::Maker.make(version) do&lt;/td&gt;
      &lt;td&gt;rss&lt;/td&gt;
      &lt;td&gt;rss.channel.title = “Dan Email Feed”  rss.channel.link = “http://www.pretheory.com”   rss.channel.description = “My newest emails”   rss.items.do_sort = true # sort items by date    Net::POP3.start(‘pop.yourDomain.com’, 110,                    ‘userName’, ‘myPass’) do&lt;/td&gt;
      &lt;td&gt;pop&lt;/td&gt;
      &lt;td&gt;if pop.mails.empty?      puts ‘No mail.’    else      i = 0      pop.each_mail do&lt;/td&gt;
      &lt;td&gt;m&lt;/td&gt;
      &lt;td&gt;# or “pop.mails.each …”        subject = m.header.split(“\r\n”).grep(/^Subject: /)[0]        subject = subject.gsub(“Subject: “,””)        subject = subject.gsub(“:”,””)        subject = subject[0,15] if(subject.length &amp;gt; 15)        link = ‘http://www.gmail.com’        body = m.pop        body = body[0,25] if(body.length &amp;gt; 25)                #create feed item        item = rss.items.new_item        item.title = subject        item.link = link        item.date = Time.now        item.description = body            i += 1      end      puts “#{pop.mails.size} mails popped.”    end  endend    File.open(destination,”w”) do&lt;/td&gt;
      &lt;td&gt;f&lt;/td&gt;
      &lt;td&gt;f.write(content)end    puts ‘Done. Thanks.’&amp;lt;/pre&amp;gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
</content>
 </entry>
 
 <entry>
   <title>Ruby Email Archiver</title>
   <link href="https://www.mayerdan.com/2007/05/20/Ruby-Email-Archiver"/>
   <updated>2007-05-20T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2007/05/20/Ruby-Email-Archiver</id>
   <content type="html">
&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Checking and receiving email via ruby.    If you need to get emails and check emails via a ruby script this should get you started in the right direction. I needed to copy down about six thousand emails from my pop server, so I wrote up this little script.  Ruby makes email simple, you just need to know where to look and unfortunately most searches result in finding a 3rd party library that costs a pretty penny for email actions in ruby. What you really need is the &lt;a href=&quot;http://www.ruby-doc.org/stdlib/libdoc/net/pop/rdoc/classes/Net/POP3.html&quot;&gt;Net::POP3&lt;/a&gt; or the &lt;a href=&quot;http://www.ruby-doc.org/stdlib/libdoc/net/imap/rdoc/classes/Net/IMAP.html&quot;&gt;Net::IMAP&lt;/a&gt;. After going through the quick documentation and seeing some of the available code examples, writing the code is straightforward. This is just a very simple example, with hardly any error checking.    Download the source to &lt;a href=&quot;http://WWW.bandddesigns.com/ml/arch/RubyEmail.zip&quot;&gt;Ruby Email Archiver&lt;/a&gt;    &amp;lt;pre&amp;gt;require ‘net/pop’    Net::POP3.start(‘pop.yourdomain.com’, 110,                    ‘userName’, ‘myPass’) do&lt;/td&gt;
      &lt;td&gt;pop&lt;/td&gt;
      &lt;td&gt;if pop.mails.empty?    puts ‘No mail.’  else    i = 0    pop.each_mail do&lt;/td&gt;
      &lt;td&gt;m&lt;/td&gt;
      &lt;td&gt;# or “pop.mails.each …”      subject = m.header.split(“\r\n”).grep(/^Subject: /)[0]      subject = subject.gsub(“Subject: “,””)      subject = subject.gsub(“:”,””)      subject = subject[0,10] if(subject.length &amp;gt; 10)      File.open(“/archive/#{i}-#{subject}.txt”, ‘w’) do&lt;/td&gt;
      &lt;td&gt;f&lt;/td&gt;
      &lt;td&gt;f.write m.pop      end      #if you want to delete msg after archive      #m.delete      i += 1    end    puts “#{pop.mails.size} mails popped.”  endend&amp;lt;/pre&amp;gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
</content>
 </entry>
 
 <entry>
   <title>Rails casts</title>
   <link href="https://www.mayerdan.com/2007/05/18/Rails-casts"/>
   <updated>2007-05-18T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2007/05/18/Rails-casts</id>
   <content type="html">
&lt;p&gt;A great way to keep up with some rails tips and ideas. I have started watching rails casts when I am eating a snack, working out, or just have a few minutes. It is a great and simple way to keep rails ideas fresh in your head. Most of the tips are fairly basic for now, but I am hoping they start dipping into more complex rails solutions. Or how to break out of the framework when it makes sense.    &lt;a href=&quot;http://railscasts.com/&quot;&gt;Railscasts&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Really good post on regular expresions</title>
   <link href="https://www.mayerdan.com/2007/05/17/Really-good-post-on-regular-expresions"/>
   <updated>2007-05-17T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2007/05/17/Really-good-post-on-regular-expresions</id>
   <content type="html">
&lt;p&gt;I found that this was a really good post on regular expressions. Just a good beginner walk through that will have you more comfortable looking at any regex you run into while debugging someone else’s code, or if your trying to add your own regular expressions yourself.    &lt;a href=&quot;http://immike.net/blog/2007/04/06/the-absolute-bare-minimum-every-programmer-should-know-about-regular-expressions/&quot;&gt;Regular expression walk through&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>I am now at Pretheory</title>
   <link href="https://www.mayerdan.com/2007/05/16/I-am-now-at-Pretheory"/>
   <updated>2007-05-16T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2007/05/16/I-am-now-at-Pretheory</id>
   <content type="html">
&lt;p&gt;I know I don’t actually post to this blog much at all, but I still occasionally add some stuff on it and I might slowly be bringing it back to life. So with that in mind I thought I should announce that I am now the co founder of a start up. &lt;a href=&quot;http://www.pretheory.com&quot;&gt;Pretheory&lt;/a&gt;. If you want to keep up with the various projects we are working on the best place to check is over at the &lt;a href=&quot;http://blog.pretheory.com&quot;&gt;Pretheory blog&lt;/a&gt;.     Currently we are programming in Ruby on Rails, and we are hoping to have our first project launched by early August. So if you have been following this blog at all, keep and eye on &lt;a href=&quot;http://www.pretheory.com&quot;&gt;Pretheory&lt;/a&gt; now as well.     &lt;a href=&quot;http://www.pretheory.com&quot;&gt;&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Good Startup Resources</title>
   <link href="https://www.mayerdan.com/2007/05/14/Good-Startup-Resources"/>
   <updated>2007-05-14T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2007/05/14/Good-Startup-Resources</id>
   <content type="html">
&lt;p&gt;Here is a list of some good start up resources that I have begun to check out on the web.    &lt;a href=&quot;http://www.paulgraham.com/articles.html&quot;&gt;Paul Graham’s essays&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;http://shirky.com/&quot;&gt;Clay Shirky’s essays&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;http://onstartups.com/&quot;&gt;OnStartups&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;http://www.smallbusinesshub.com/&quot;&gt;Small Business Hub&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;http://news.ycombinator.com/&quot;&gt;Startup news&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;http://programming.reddit.com/&quot;&gt;programming.reddit.com&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;http://blog.pretheory.com/arch/000362.php&quot;&gt;Good start up books to read&lt;/a&gt;&lt;br /&gt;    Hopefully I will grow this list a bit more shortly, but for now that is some good information to get your started.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Eclipse Plugin Callisto</title>
   <link href="https://www.mayerdan.com/2007/01/31/Eclipse-Plugin-Callisto"/>
   <updated>2007-01-31T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2007/01/31/Eclipse-Plugin-Callisto</id>
   <content type="html">
&lt;p&gt;This is a pretty cool project you can use via the update manager you just tell it the type of development you want to do (java swing/j2ee/java webapps/c++/ and others). Anyways it is pretty good and gets a bunch of plugins together and manages them for you, so if newer versions of one thing still require an older version of another plug in it deals with any incompatibility issues. You select what you want and then it will say you need other dependencies click the button that says get required. That seems to be it. &lt;a href=&quot;http://www.eclipse.org/callisto/&quot;&gt;Callisto&lt;/a&gt; peace,Dan&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Spam bots try anything</title>
   <link href="https://www.mayerdan.com/2007/01/14/Spam-bots-try-anything"/>
   <updated>2007-01-14T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2007/01/14/Spam-bots-try-anything</id>
   <content type="html">
&lt;p&gt;Well I have on many of my sites had to deal with and fight the onslaught of spam bots, but I still find them amusing some times. After a bug occurred on a production machine, I was looking at the logs and ran into many errors looking 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;  2007-01-11 18:15:05,938 [TP-Processor4395]
  ERROR com.realestate.search.core.SearchController - java.lang.NumberFormatException: For input string: &quot;http://valtrex-gs.****.com&quot;2007-01-11 18:15:05,980 [TP-Processor4395]
  ERROR org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/].[jsp] - Servlet.service() for servlet jspthrew exceptionjava.lang.NumberFormatException: For input string: &quot;http://valtrex-gs.***.com&quot;       at java.lang.NumberFormatException.forInputString(NumberFormatException.java:48)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It seems this was a pretty dumb, but probably mildly successful bot. It started some search and found tons of web pages, every time it encountered a form, it would attempt to fill out the majority of the fields with the URL hoping to make it into generated pages, most common search terms, comments, user names, or anything that would possibly link to the URL and increase the sites page rank. I found it hilarious, because somewhere on our site it was trying to fill out number, date, and other fields with the URL which we threw errors on format exceptions and probably returned them to the form with an error about that field. The bot hasn’t given up, but hasn’t slammed us very hard. Either way it is another amusing tale, of spam bots running amok on the web.    At least they can make me laugh sometimes…&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Mysql null date</title>
   <link href="https://www.mayerdan.com/2006/12/18/Mysql-null-date"/>
   <updated>2006-12-18T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2006/12/18/Mysql-null-date</id>
   <content type="html">
&lt;p&gt;I had an issue with java and hibernate returning a null date.    java hibernate java.sql.SQLException: Value ‘0000-00-00’ can not be represented as java.sql.Date    This just took a little longer to find the fix on a google search than I thought it should have so I post what I ended up doing to resolve the situation here:    ?zeroDateTimeBehavior=convertToNull    you add this to your jdbc connection url and it will convert the 0000-00-00 to the proper type for java to work with.    More info here:    http://dev.mysql.com/doc/mysql/en/cj-upgrading.html#cj-upgrading-3-0-to-3-1    thanks to this thread, which had many other suggestions, but most of them were not helpful, but the one above saved my day.    http://forum.java.sun.com/thread.jspa?threadID=649520&amp;amp;messageID=3824019    you can add this on the end of your hibernate connection file, like so…&lt;property name=&quot;hibernate.connection.url&quot;&gt;jdbc:mysql://{DB_IPADDRESS}:{DB_PORT}/{DB_NAME}?zeroDateTimeBehavior=convertToNull&amp;amp;profileSql=true&lt;/property&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Soap Sucks</title>
   <link href="https://www.mayerdan.com/2006/11/19/Soap-Sucks"/>
   <updated>2006-11-19T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2006/11/19/Soap-Sucks</id>
   <content type="html">
&lt;p&gt;I have said it for a long time now that XML and SOAP are bloated and most of the time the wrong way to do anything. Well it seems lead api developers are agreeing with me, it might have been the decade of XML but I hope to slowly see it fade to a hardly used only when needed standard, instead of the if it doesn’t use XML it obviously isn’t designed well.    Soap i wasted so much time and clock cycle for projects that you never did well and were always replaced.    &lt;a href=&quot;http://www.somebits.com/weblog/tech/bad/whySoapSucks.html&quot;&gt;let me say now I’d never choose to use SOAP and WSDL again. I was wrong&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Java String isEmpty()</title>
   <link href="https://www.mayerdan.com/2006/11/19/Java-String-isEmpty"/>
   <updated>2006-11-19T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2006/11/19/Java-String-isEmpty</id>
   <content type="html">
&lt;p&gt;I have heard the java string class will finally get the isEmpty method in an upcoming release. Lets hope it is in 1.6, because I can’t wait much longer, I can’t for the life of me see why this was ignored for so long. Why have I had to put this in so many freakin if statements when this could have been fixed by adding a very easy and simple but amazingly easy to read and useful method.    String randWord;    /&lt;em&gt;random code or back in forth between many different classes */    String someFunction() {if(randWord!=null &amp;amp;&amp;amp; !randWord.equals(“”)) {  /&lt;/em&gt;do something&lt;em&gt;/}    ….}    all of this could just be replaced and make the code so much more readable with the isEmpty function, becoming the following…    if( !randWord.isEmpty() ) {  /&lt;/em&gt;do something*/}    I want this functionality and I want it now… I guess since java is now opensource I should just go override the String class to add one simple function! So much value and so simple.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Nerds are great</title>
   <link href="https://www.mayerdan.com/2006/10/12/Nerds-are-great"/>
   <updated>2006-10-12T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2006/10/12/Nerds-are-great</id>
   <content type="html">
&lt;p&gt;Some days they make me proud to be a nerd…    This just cracks me up,&lt;a href=&quot;https://bugzilla.mozilla.org/show_bug.cgi?id=95849#c0&quot;&gt;Mozilla Bug Report&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Ubuntu Server 6.06</title>
   <link href="https://www.mayerdan.com/2006/07/10/Ubuntu-Server-606"/>
   <updated>2006-07-10T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2006/07/10/Ubuntu-Server-606</id>
   <content type="html">
&lt;p&gt;I installed Ubuntu server on one of my machines, one of the nice things about it, is that it from install it configures a working LAMP (Linux, Apache, MySQL and PHP) set up. Which is nice and saves lots of configuration. Unfortunately, I still need to get Subversion, Ruby, Tomcat, and other things installed and running on the system. Anyways since, I was going to have to set up a bunch of things and planned on using the system as a development machine I then installed Ubuntu desktop on the system as one of the aptitude scripts, which installs all the packages require for the desktop configuration. After that though, I realized that I was not able to complete any install with out causing an error in clvm, anything you install would cause a clvm error and make your install fail. So here is the fix if you install the Ubuntu server from CD and then install the desktop:    Ultimately this post by npcomplete2000, was the solution for me:&lt;a href=&quot;http://www.ubuntuforums.org/archive/index.php/t-186356.html&quot;&gt;http://www.ubuntuforums.org/archive/index.php/t-186356.html&lt;/a&gt;    removing clvm caused no problems for me and was something I never thought I would need. After that I could install other applications with out problems.    &lt;b&gt;More error information so those searching for error codes will find this post:&lt;/b&gt;Starting Cluster LVM Daemon clvmd could not connect to cluster managerConsult syslog for more informationinvoke-rc.d: initscript clvm, action “start” failed.dpkg: error processing clvm (–configure): subprocess post-installation script returned error exit status 3Errors were encountered while processing: clvmE: Sub-process /usr/bin/dpkg returned an error code (1)&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Ruby on Rails Gem error could not find gem</title>
   <link href="https://www.mayerdan.com/2006/07/10/Ruby-on-Rails-Gem-error-could-not-find-gem"/>
   <updated>2006-07-10T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2006/07/10/Ruby-on-Rails-Gem-error-could-not-find-gem</id>
   <content type="html">
&lt;p&gt;I got this gem error on Ubuntu 6.06 over and over then finally I followed these instructions to install Ruby on Rails,    &lt;a href=&quot;https://help.ubuntu.com/community/RubyOnRails&quot;&gt;Install Ruby on Rails on Ubuntu 6.06&lt;/a&gt;    and I changed one line:    sudo gem update –system IS WRONG!!!!    run:    sudo gem update    and thing all the sudo gem installs work perfectly fine for me. Why I have no idea. I hate computers sometimes, but at least ruby and rails now work for Ubuntu 6.06 on my system.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Old Computer Science Links</title>
   <link href="https://www.mayerdan.com/2006/07/03/Old-Computer-Science-Links"/>
   <updated>2006-07-03T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2006/07/03/Old-Computer-Science-Links</id>
   <content type="html">
&lt;p&gt;General Links    &lt;a href=&quot;http://www.colorado.edu&quot;&gt;University of Colorado&lt;/a&gt;&lt;a href=&quot;http://www.web2gether.org&quot;&gt;Web2gether Project&lt;/a&gt;&lt;a href=&quot;http://www.cs.colorado.edu/~l3d/&quot;&gt;L3D labs at CU&lt;/a&gt;&lt;a href=&quot;http://programming.wastedbrains.com/&quot;&gt;My code online&lt;/a&gt;    Machine Learning    &lt;a href=&quot;http://www.mit.edu/~9.520/&quot;&gt;MITs class page on Machine Learning&lt;/a&gt;&lt;a href=&quot;http://www.developer.com/java/other/article.php/10936_1559871_1&quot;&gt;The Use of Java in Machine Learning&lt;/a&gt;&lt;a href=&quot;http://aima.cs.berkeley.edu/&quot;&gt;AI: a modern approach&lt;/a&gt;&lt;a href=&quot;http://staff.aist.go.jp/utsugi-a/Lab/Links.html&quot;&gt;Java Neural Networks&lt;/a&gt;    Text Categorization Links    &lt;a href=&quot;http://www.cs.utexas.edu/users/mooney/ir-course/&quot;&gt;Intelligent Information Retrieval &lt;/a&gt;&lt;a href=&quot;http://www.esi.uem.es/~jmgomez/tutorials/ecmlpkdd02/&quot;&gt;Tutorial on Text Mining&lt;/a&gt;&lt;a href=&quot;http://sourceforge.net/projects/spay/&quot;&gt;Open source spam filtering project&lt;/a&gt;&lt;a href=&quot;http://cl.aist-nara.ac.jp/~taku-ku/software/yamcha/&quot;&gt;Yet Another Multipurpose Chunk Annotator&lt;/a&gt;    Weka Links    &lt;a href=&quot;http://www.cs.waikato.ac.nz/~ml/index.html&quot;&gt;WEKA&lt;/a&gt;&lt;a href=&quot;http://www.cs.iastate.edu/~andymenz/573Project.html&quot;&gt;Spam analysis&lt;/a&gt;    LSA/LSI Links    &lt;a href=&quot;http://lsa.colorado.edu&quot;&gt;LSA @ CU&lt;/a&gt;&lt;a href=&quot;http://www.cs.utk.edu/~lsi/&quot;&gt;LSI @ UTK&lt;/a&gt;    SVM Links    &lt;a href=&quot;http://www.ecs.soton.ac.uk/~srg/publications/pdf/SVM.pdf&quot;&gt;Technical Paper Describing SVM classification&lt;/a&gt;&lt;a href=&quot;http://www.cs.colorado.edu/~grudic/teaching/CSCI4202_2004/&quot;&gt;SVM Info / Lessons&lt;/a&gt;&lt;a href=&quot;http://www.kernel-machines.org/publications.html&quot;&gt;Comprehensive List of SVM publications&lt;/a&gt;&lt;a href=&quot;http://www.cs.cornell.edu/People/tj/publications/joachims_99a.pdf&quot;&gt;Large Scale SVM Learning&lt;/a&gt;&lt;a href=&quot;http://www.site.uottawa.ca/~nat/Courses/NLP-Course/itnn_1999_09_1048.pdf&quot;&gt;SVM Spam Filter Research paper&lt;/a&gt;    SVM Tools:    &lt;a href=&quot;http://tedlab.mit.edu/~dr/SVDLIBC/&quot;&gt;SVDLIBC&lt;/a&gt;&lt;a href=&quot;http://www.isi.edu/~hdaume/SVMseq/&quot;&gt;SVMseq&lt;/a&gt;&lt;a href=&quot;http://cl.aist-nara.ac.jp/~taku-ku/software/TinySVM/&quot;&gt;Tiny SVM&lt;/a&gt;&lt;a href=&quot;http://www.cs.cornell.edu/People/tj/svm_light/&quot;&gt;SVM-Light&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Coding tan lines</title>
   <link href="https://www.mayerdan.com/2006/05/21/Coding-tan-lines"/>
   <updated>2006-05-21T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2006/05/21/Coding-tan-lines</id>
   <content type="html">
&lt;p&gt;This is funny, and probably far to true.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Java skills wanted in the market</title>
   <link href="https://www.mayerdan.com/2006/05/03/Java-skills-wanted-in-the-market"/>
   <updated>2006-05-03T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2006/05/03/Java-skills-wanted-in-the-market</id>
   <content type="html">
&lt;p&gt;I have been looking around at what companies seem to want currently from a mi level Java programmer, it seems to be all over the map. I also have notice a big rise in the demand for C#/ASP programmers as well. So if your starting to hunt for a new Java developer position here is a list of things you might want to study up on a bit before going out on the job hunt:&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;  [Java RMI](http://java.sun.com/j2se/1.4.2/docs/guide/rmi/javarmiproperties.html)[Java Swing Basics / tutorials](http://java.sun.com/docs/books/tutorial/uiswing/TOC.html)[EJB basics / tutorials](http://java.sun.com/developer/onlineTraining/EJBIntro/EJBIntro.html)[Some basic internationalization](http://java.sun.com/developer/technicalArticles/Intl/IntlIntro/)[Java Date formatting](http://home.tiscali.nl/~bmc88/java/sbook/016.html) or more [Date timezones](http://java.sun.com/j2se/1.5.0/docs/api/java/util/SimpleTimeZone.html)[Java Applet security](http://www.securingjava.com/chapter-two/)[Compressing serialized objects or other output files in Java](http://java.sun.com/developer/technicalArticles/Programming/compression/)&amp;lt;a href=&quot;http://www.ccs.neu.edu/home/kenb/com3337/rmi_tut.html&amp;gt;more about Java RMI[Threading issues of Array List](http://java.sun.com/j2se/1.4.2/docs/api/java/util/ArrayList.html)[I know we all extend classes and work with interfaces, but they always ask questions about it at interviews](http://www.javacoffeebreak.com/java104/java104.html)There are also many [testing](http://www.webopedia.com/TERM/W/White_Box_Testing.html) positions [available](http://www.faqs.org/faqs/software-eng/testing-faq/section-13.html) which want experience with [JUnit](http://supportweb.cs.bham.ac.uk/documentation/tutorials/docsystem/build/tutorials/junit/junit.html).
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So if your currently on a Java position and looking around a bit, here are some areas you should really brush up on a bit before starting your interview rounds.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Ant options</title>
   <link href="https://www.mayerdan.com/2006/01/30/Ant-options"/>
   <updated>2006-01-30T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2006/01/30/Ant-options</id>
   <content type="html">
&lt;p&gt;if your running out of Java Heap space memory while running ant you probably just need to increase the memory size for ant when it is running to do this you set the enviroment variable ANT_OPTS.     ANT_OPTS=-Xmx128M    in windows you right click my computer go to properties, and then click advanced then set new enviroment variable name ANT_OPTS value -Xmx128M. If your running cygwin you must close current instances and reopen them to get the new settings to take effect, after opening up cygwin again try &amp;gt;echo $ANT_OPTS which should output your setting. If you still receive the error you can go higher with your memory. I have never had to go higher than 256.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Junit testing server states</title>
   <link href="https://www.mayerdan.com/2006/01/19/Junit-testing-server-states"/>
   <updated>2006-01-19T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2006/01/19/Junit-testing-server-states</id>
   <content type="html">
&lt;p&gt;Alot of Junit docs and faq tells you that have bad design if your trying to extend test suite so that there is only one setUp() and testDown() for a whole sweet of tests, but that just isnt true when your trying to tests proper order of state changes each transaction should change the state of the server dpending on its current state, if the server is started up new for each test your only testing the start up state… my single unit tests have dependencies on previous tests that has cuased the server to be in its current state… So no where gives good documentation on how to do this the faq is un usefully short showing how to extend testSuite but not how to use it… so here is the best page for how to actually use it just ignore hte Turbine2 stuff:    &lt;a href=&quot;http://wiki.apache.org/jakarta-turbine/Turbine2/FAQ&quot;&gt;16. Q: How can I setup Turbine once for my JUnit Test cases?&lt;/a&gt;    And remember that Junit executes your tests in order alphabetically and numerically after the intial keyword test.    ie    TestA will run before TestB so if you running tests in order for state make sure that you have your tests named appropriately to be sure they will run in the expected order.    hope this helps someone running into problems running Juint with a single setUp across a suite of tests.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Apache2 integrating Tomcat 5.5 on windows</title>
   <link href="https://www.mayerdan.com/2006/01/19/Apache2-integrating-Tomcat-55-on-windows"/>
   <updated>2006-01-19T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2006/01/19/Apache2-integrating-Tomcat-55-on-windows</id>
   <content type="html">
&lt;p&gt;I have been trying to do this off and on for over a year and a half and every time i run into problems and I get pissed and quit. I have found hundreds of pages that all say different ways of how to solve the problem of integrating tomcat 5.5 with apache2. Non of them worked for me, but after combining tips from various install guides I can show you what finally worked for me. After wasting over all the time hours and hours… here is all that I did small changes to the apache configure file and small ones to tomcats… restart both of there servers and WHOOPIE it just works. Apache2 with Tomcat 5.5 on a windows server.&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;  httpdconf changes:    LoadModule proxy_module modules/mod_proxy.soLoadModule proxy_connect_module modules/mod_proxy_connect.soLoadModule proxy_http_module modules/mod_proxy_http.so    &amp;lt;VirtualHost *:80&amp;gt;ServerName special.myname.comDocumentRoot &quot;C:/Program Files/Apache Group/Apache2/htdocs&quot;    &amp;lt;ifmodule mod_proxy.c&amp;gt;ProxyRequests OnProxyPass /cool http://66.***.***.**3:8082/your/webapp/rootdir/## these are comments on the dir#C:\Program Files\Apache\Tomcat 5.5\webapps\you\webapp\rootdir\# if anyone was confused to how that would map to windows tomcat stuff#ProxyPass /*.jsp http://66.***.***.**3:8082&amp;lt;/ifmodule&amp;gt;    tomcat&apos;s server.xml:        &amp;lt;connector acceptcount=&quot;100&quot; disableuploadtimeout=&quot;true&quot; enablelookups=&quot;false&quot; connectiontimeout=&quot;20000&quot; port=&quot;8082&quot; maxsparethreads=&quot;75&quot; minsparethreads=&quot;25&quot; proxyport=&quot;80&quot; maxthreads=&quot;150&quot; /&amp;gt;

thats it... you might have to change some of your jsps for how they find local directories and get all the lookups for the local directories to match your mapping, but you got it... it should all work now. Congradulations... if that didnt work for you here are some sites I was looking at for help:

  [Apache proxy forwarding](http://www.drewnoakes.com/snippets/ConfiguringApacheToRedirectASingleHostToMultiplePorts/)    [Apache 2 tomcat 5.5](http://forum.sun.com/thread.jspa?threadID=22381&amp;amp;tstart=210)    best of luck post comments here and i can try to help you if they are problems i have run into before.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Broken MailEnable</title>
   <link href="https://www.mayerdan.com/2005/12/20/Broken-MailEnable"/>
   <updated>2005-12-20T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2005/12/20/Broken-MailEnable</id>
   <content type="html">
&lt;p&gt;Problem: Mail Enable Bad Mail folder became HUGE! Or MailEnable Bad Mail folder filled up!    MailEnable was stuffing my hard drive with badmessages and outgoing that i didnt think it was allowed to send. Some spammers obviously found a way to attack my mail server and start making it work for them… Anyways it filled an entire 80GB hard drive with bad emails… The folders where so large that explorer would crash when trying to delete the files. So I search around and found this solution to emptying the folders.    http://support.microsoft.com/default.aspx?scid=kb;en-us;555408    After you make sure it is working remove the /s from the end of the line you wont see that it is deleting one at a time, but the process goes about 10x faster because you aren’t printing between each of the deletes.    I also dissabled my entire mail server until i can figure out what the hell is going on and how to stop the spamming bastards that are trying to take over my server.    windows server 2003 too many files to delete solved.Mail Enable can open config solved.Email Spam removed from system solved.    The problem now is how slow it is going while deleting the files.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>No Fluff just Stuff</title>
   <link href="https://www.mayerdan.com/2005/11/14/No-Fluff-just-Stuff"/>
   <updated>2005-11-14T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2005/11/14/No-Fluff-just-Stuff</id>
   <content type="html">
&lt;p&gt;Well I am done with the Rocky Mountain Software Symposium (RMSS), No Fluff just Stuff. Which was excellent. I learned a ton I wish I could go to one of these ever 3 to 6 months… Anyways I learned alot and have some great stuff that I can put into use on my projects at work. I also learned a ton of just really cool and interesting stuff. That will make me a better programmer all the time, as well as get me moving on new technologies faster than others. Anyways, it was really cool and interesting. Hopefully I will get to use and play with some of the code before i forget all of the cool stuff that i just learned… check the new RSS feed reader that is on the front of wastedbrains to see something I coded up real quick…    I highly recommend the &lt;a href=&quot;http://www.nofluffjuststuff.com/&quot;&gt;no fluff just stuff&lt;/a&gt; tour to everyone.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Ruby on Rails</title>
   <link href="https://www.mayerdan.com/2005/11/10/Ruby-on-Rails"/>
   <updated>2005-11-10T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2005/11/10/Ruby-on-Rails</id>
   <content type="html">
&lt;p&gt;I have been learning the language ruby, and have begun playing around with ruby on rails. Which is a development framework for web based applications. I have been really impressed with ruby on rails so far, I was worried about how extendable it would be to less general problems, but it seems to be fairly extendable. Which is cool. I highly recommend giving it a shot and I think this was the best tutorial I have gone through while learning ruby.    &lt;a href=&quot;http://www.onlamp.com/pub/a/onlamp/2005/01/20/rails.html?page=1&quot;&gt;Ruby on rails tutorial, building a cookbook&lt;/a&gt;    I also recommend &lt;a href=&quot;http://www.pragmaticprogrammer.com/titles/rails/index.html&quot;&gt;Agile web development with Rails, from the Pragmatic Programmers&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>CSS columns guides</title>
   <link href="https://www.mayerdan.com/2005/09/08/CSS-columns-guides"/>
   <updated>2005-09-08T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2005/09/08/CSS-columns-guides</id>
   <content type="html">
&lt;p&gt;I needed some good CSS layouts so i looked around tinkered a bit and got what i want, and I thought i should share some of the best resources i found. I would like to give thanks to everyone that has made their CSS and XHTML layouts available as it is a very cool thing to do and help out with other web developers.    &lt;a href=&quot;http://www.manisheriar.com/holygrail/&quot;&gt;Great threecolum css with header and footer&lt;/a&gt;    &lt;a href=&quot;http://www.redmelon.net/tstme/3colfixed/&quot;&gt;Center floating three column&lt;/a&gt;    &lt;a href=&quot;http://www.alistapart.com/stories/flexiblelayouts/&quot;&gt;a list apart&lt;/a&gt;    &lt;a href=&quot;http://www.fu2k.org/alex/css/layouts/3Col_NN4_RWS_A.mhtml&quot;&gt;liquid header&lt;/a&gt;    &lt;a href=&quot;http://www.fu2k.org/alex/css/layouts/3Col_NN4_FFFF.mhtml&quot;&gt;Ordered columns float model&lt;/a&gt;    &lt;a href=&quot;http://webhost.bridgew.edu/etribou/layouts/skidoo/&quot;&gt;Skidoo layouts&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Apache XML RPC Asych</title>
   <link href="https://www.mayerdan.com/2005/08/08/Apache-XML-RPC-Asych"/>
   <updated>2005-08-08T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2005/08/08/Apache-XML-RPC-Asych</id>
   <content type="html">
&lt;p&gt;Exception in thread “Thread-0” java.lang.NullPointerException at org.apache.xmlrpc.XmlRpcClient$XmlRpcClientAsyncThread.run(XmlRpcClient.java:271)    If your getting any similar errors while trying to run executeAsync from the XmlRpcClient (org.apache.xmlrpc.XmlRpcClient) class. The problem is this &lt;a href=&quot;http://issues.apache.org/jira/browse/XMLRPC-62&quot;&gt;bug&lt;/a&gt;, which has been fixed by apache, but hasn’t made it into the binaries or the src releases because they havent been updated fro some time. To get the fix use the anonymous cvs, or apply this &lt;a href=&quot;http://issues.apache.org/jira/secure/attachment/12310763/executeAsync.patch&quot;&gt;patch &lt;/a&gt; to your src. It took me a few hours to figure out the problem with this and fix it after finding the patch.    So if you build the jars and use the xmlrpc-2.0-beta that is built as the jar you include in your project that should fix everything.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>java in cygwin</title>
   <link href="https://www.mayerdan.com/2005/07/21/java-in-cygwin"/>
   <updated>2005-07-21T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2005/07/21/java-in-cygwin</id>
   <content type="html">
&lt;p&gt;man I had some serious problems running java in cygwin with a shell script (sh script)… So I am going to post my script to help you out… there are a couple things of note first…    java a windows program expects windows paths your cygwin script proably uses unix paths.    cygwin sh scripts treat ‘;’ as a special character so when using multiple classpaths you must escape it ‘\;’ to make it work as expected…    # test.shJAVAEXE=$JAVA_HOME/bin/java.execd ../IEP_HOME=C:\dev\dsmayer\sandbox\iepIEP_BIN=C:\dev\dsmayer\sandbox\iep\binIEP_LIB=C:\dev\dsmayer\sandbox\iep\libIEP_JAR=C:\dev\dsmayer\sandbox\iep\lib\iepio.jarIEP_IST=C:\dev\dsmayer\sandbox\iep\lib\istcustom.jarIEPB_JAR=C:\dev\dsmayer\sandbox\iep\lib\backport-util-concurrent.jarIEP_PROPS=C:\dev\dsmayer\sandbox\iep\properties    echo java -cp $IEP_JAR\;$IEP_IST\;$IEPB_JAR RCS.example.RCSregechojava -cp $IEP_JAR\;$IEP_IST\;$IEPB_JAR RCS.example.RCSreg &amp;amp;java -cp $IEP_PROPS\;$IEP_JAR\;$IEPB_JAR\;$IEP_IST RCS.example.clients.ResourceClient &amp;amp;java -cp $IEP_PROPS\;$IEP_JAR\;$IEPB_JAR\;$IEP_IST RCS.example.clients.UserClient &amp;amp;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Java Source Code Examples</title>
   <link href="https://www.mayerdan.com/2005/06/28/Java-Source-Code-Examples"/>
   <updated>2005-06-28T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2005/06/28/Java-Source-Code-Examples</id>
   <content type="html">
&lt;p&gt;A great collection of simple java source exmples. These are organized well and provide a wealth of information if your having problems doing something you know has been done before.    &lt;a href=&quot;http://www.java2s.com/&quot;&gt;http://www.java2s.com/ Java Source Examples&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Computer Science Research Papers</title>
   <link href="https://www.mayerdan.com/2005/06/13/Computer-Science-Research-Papers"/>
   <updated>2005-06-13T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2005/06/13/Computer-Science-Research-Papers</id>
   <content type="html">
&lt;p&gt;I have definately violated some of these and I think it would be good for people that are trying to do some serious analysis to follow these I do hate nothing more when marketing makes grapsh that essentially lie. Anyways if you have a computer science or any science for that matter paper, these are great rules to go by to remain creditable.    &lt;a href=&quot;http://www.zedshaw.com/blog/philosophy/it_analysis_rubric.html&quot;&gt;A Rubic for IT Analysis Papers&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Programming Links</title>
   <link href="https://www.mayerdan.com/2005/06/02/Programming-Links"/>
   <updated>2005-06-02T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2005/06/02/Programming-Links</id>
   <content type="html">
&lt;p&gt;Since I haven’t been putting much interesting up here of my own code lately I figure I could point you to some things of note and interest:    &lt;a href=&quot;http://www.broken-notebook.com/spell.php&quot;&gt;Java Form SpellChecking&lt;/a&gt; This is a great tool to allow users of your site or webapp to have access to spell checking much like from MS word.    &lt;a href=&quot;http://cocoongallery.sourceforge.net/&quot;&gt;Java Image Galleries&lt;/a&gt; Ever wanted a great image gallery solution?    &lt;a href=&quot;http://aoi.sourceforge.net/&quot;&gt;Art of Illusion&lt;/a&gt; A cool 3d rendering studio entirely in Java.    &lt;a href=&quot;http://java.sun.com/webservices/docs/1.3/tutorial/doc/&quot;&gt;A good webservices and JAVA XML tutorial with lots of basic information&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Converting XML with XSLT</title>
   <link href="https://www.mayerdan.com/2005/05/31/Converting-XML-with-XSLT"/>
   <updated>2005-05-31T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2005/05/31/Converting-XML-with-XSLT</id>
   <content type="html">
&lt;p&gt;Problem: Converting XML to Excel    I had to convert some XML to sort and display as we wished in excel. After reading a bunch of articles about XML-FO and other stuff about cacoon and other Java solutions I decided to go with just using a fairly simple XSLT stylesheet and convert the XML to HTML tables with the excel extension. This is even the &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnexcl2k2/html/odc_xlflatnr.asp&quot;&gt;recommended solution by microsoft&lt;/a&gt; (Which is probably because excel has a bloated and often changing format.) So converting this way leads to a well and easy and nice looking excel file and has the benifeit that anyone without excel can also view the file in a standard web browser. The microsoft tutorial on &lt;a href=&quot;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnexcl2k2/html/odc_xlflatnr.asp&quot;&gt;converting XML with XSTL&lt;/a&gt; is very good. If you have any problems or need more advanced XSTL this is also another very usefull &lt;a href=&quot;http://www.zvon.org/xxl/XSLTutorial/Books/Output/example1_ch1.html&quot;&gt;set of XSTL examples&lt;/a&gt;.     The basic idea is to make the stylesheet so that it can sort and create a easy to view html table. Then you can either let excel open the XML which can find the stylesheet and apply it for the user, or you can use a program to do the conversion for your user and output excel or html files. I needed to generate the files for my users so I went with Xalan from the apache foundation.     Xalan is a XSL stylesheet processors in Java &amp;amp; C++    The example is simple to modify for whatever you need. I used the example file SimpleTransform, which can be found in the installation directory at    &lt;install&gt;\xalan-j_2_6_0\samples\SimpleTransform    The result is simple and easy, below I will include my XSLT file to show how easy it is toe create simple large tables from a nice little XSLT file.    I couldnt include it as text cause it tried to render it so here is a link to it...[Download file](http://WWW.bandddesigns.com/ml/arch/trans.xsl)&lt;/install&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Java string to inputstream</title>
   <link href="https://www.mayerdan.com/2005/04/06/Java-string-to-inputstream"/>
   <updated>2005-04-06T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2005/04/06/Java-string-to-inputstream</id>
   <content type="html">
&lt;p&gt;If you need to make a string into an input stream it is really easy. Many people say you shouldnt convert a string into an input stream and that it is bad code design though. Since I was using someone elses library that only took input streams and all of my input was created as strings i really had no choice but to do the conversion (which is wastefull of memory cause you essentially have exact copies of the same data). Making a string into an inputstream can be done like this:    ByteArrayInputStream bs = new ByteArrayInputStream(site.getBytes());    If you have any problems leave a comment.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>CVS stuff</title>
   <link href="https://www.mayerdan.com/2005/04/05/CVS-stuff"/>
   <updated>2005-04-05T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2005/04/05/CVS-stuff</id>
   <content type="html">
&lt;p&gt;I always am forgettting this and that about CVS and this is just a really good resource on all the CVS stuff you would need to know when working with a regular CVS repository.    &lt;a href=&quot;http://cvsbook.red-bean.com/cvsbook.html#Invoking%20CVS&quot;&gt;Command Line CVS&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>java code tar</title>
   <link href="https://www.mayerdan.com/2005/03/30/java-code-tar"/>
   <updated>2005-03-30T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2005/03/30/java-code-tar</id>
   <content type="html">
&lt;p&gt;I have released a development version of java tar with added support for various GNU tar features like multi file, verification, and breaking the 8GB barrier. It is pretty nice so if your into learning some java code or working with tar there is alot of good info here and well documentated source code.    &lt;a href=&quot;http://www.bandddesigns.com/ml/arch/002132.html&quot;&gt;Java Tar upgrades&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Java Tar</title>
   <link href="https://www.mayerdan.com/2005/03/30/Java-Tar"/>
   <updated>2005-03-30T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2005/03/30/Java-Tar</id>
   <content type="html">
&lt;p&gt;Java Tar source. This might not be the final draft, but it is getting pretty close. Most features seem to be working well and the source is very well documentated. So if you want or need the new GNU features you can get them from java tar here. The main java tar build hasnt incoporated these yet but all of the modifications have been sent off to them, so hopefully they will incorporate them soon.    &lt;a href=&quot;http://www.trustice.com/java/tar/&quot;&gt;Java Tar homepage&lt;/a&gt;    Until then feel free to download the java tar source from me. You can edit and modify as much as the orginal liscense allows, which says it is public domain use it as you wish. I would like to give a big thanks to Timothy Gerard Endres, who is the orginal author of Java Tar.  If you have any questions comments or problems let me know.    &lt;a href=&quot;http://WWW.bandddesigns.com/ml/arch/javatar-2.5.zip&quot;&gt;Download Java Tar&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Java Tar and problems</title>
   <link href="https://www.mayerdan.com/2005/03/30/Java-Tar-and-problems"/>
   <updated>2005-03-30T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2005/03/30/Java-Tar-and-problems</id>
   <content type="html">
&lt;p&gt;I have been working on adding a bunch of GNU tar features to java tar. I will release the source soon, but I just wanted to comment on the lack of good documentation on the format that makes it very hard to work with the GNU tar format… Simple things like regular tar takes char 32 as a null and ignored character… and so does GNUtar for the regular tar fields but in the offset field needed for multi volume tars 32 crashes the program giving you header errors. It only accepts char 48 as a null which in the other size field it accepts either 32 or 48… things like this are commented no where and i only discoved by writting and editing the GNU tar source. Which is also far under documentated in the code and very hard to follow.     GNU Tar size field: If your working with GNU tar the size field is just like the standard tar field  in ustar… except which is no where to be found in their documentation when you support unlimited size files. If you have a file larger than 8GB to support it you must write the number as bits in twos compliment notation. Also after doing the you have to flip the sign bit (the very most left 0) to a 1. Which would normally mean you have a negative number and now if you decode this as a twos compliment you end up with a huge negative number… but not encoded it normally as a positive number and flip that bit. I only figured this out after manually reading and decoding gnu tar and stanttard ustar headers for a long time. The is nothing that talks about how the support for unlimited files was added to gnu tar… well hopefully if you are having the same problem you found this page. So upgrading GNU tar or other programs to support GNU tar should be easier.    I have added support for these gnu features to javatar:Multi volumeverificationunlimited filesizefast single file extractionTar Table of contents (xml of the files and there offsets with in the tar)    If your interested in this or have any questions about GNU tar or JAva tar feel free to send me an email and ask.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Java copy file code</title>
   <link href="https://www.mayerdan.com/2005/03/10/Java-copy-file-code"/>
   <updated>2005-03-10T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2005/03/10/Java-copy-file-code</id>
   <content type="html">
&lt;p&gt;just cause i didnt find code on this fast enough here is code to copy a file using java. It reads the file in while writting it out using buffered readers and writters:&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;   public static void copy(String from, String to) throws IOException{   InputStream in = null;    OutputStream out = null;    try {        in = new FileInputStream(from);        out = new FileOutputStream(to);        int length = 128*10240; // danger!        byte[] bytes = new byte[length];        int read=0;        for(;;){        read=in.read(bytes,0,length);        if(read==-1){            break;        }        out.write(bytes,0,read);        }        } finally {            if (in != null) {                in.close();            }            if (out != null) {                out.close();            }        }    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>my neglected blog</title>
   <link href="https://www.mayerdan.com/2005/02/28/my-neglected-blog"/>
   <updated>2005-02-28T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2005/02/28/my-neglected-blog</id>
   <content type="html">
&lt;p&gt;alright here is a post to my neglected blog. It always falls behind… Anyways i have been doing a ton of programming, but sadly none of it is very interesting or new right now. Alot of the stuff I am doing I have done before, but I am trying to build up a system which I can use to explore some new ideas and I need a nice simple toolset built up before doing anything else all the interesting. So it might be awhile, before I have any really interesting things to post on here, but i will try and we will see when I get a chance to get back to my AI interests.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>GNU tar errors on windows</title>
   <link href="https://www.mayerdan.com/2005/02/08/GNU-tar-errors-on-windows"/>
   <updated>2005-02-08T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2005/02/08/GNU-tar-errors-on-windows</id>
   <content type="html">
&lt;p&gt;I ran into a very odd error while running GNU tar on windows XP under dos or the cmd.exe (dos emulator). The error must be incredibly rare since i only found one other post with similiar problems. I will first explain the error and then how it was fixed or resolved.    here is the one link that I found with the similiar GNU windows tar error &lt;a href=&quot;http://tolstoy.newcastle.edu.au/R/help/98b/0492.htm&quot;&gt;tar error&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;  my error:C:\dev\dsmayer\sandbox\exe&amp;gt;tar cvf tester.tar newstar: Cannot add file news: No such file or directory (ENOENT)tar: Error exit delayed from previous errors
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;news was a standard directory. the error we found was occuring because we were in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;C:\dev\*.*&lt;/code&gt; directory apparently dev and aux (as the other user was having that error) are some sort of key words that screw up the program, I dont know why. I do know that moving tar.ext and my directory to a folder &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;c:\temp&lt;/code&gt; or any other &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;c:\*.*.\dsmayer\sandbox\exe&lt;/code&gt; and it would work. So if your having any erros similiar with GNU tar I suggest changing directories and seeing if that fixes your problems. Taring under cygwin in this directory worked fine it was only a problem when taring in dos. Good luck post any questions, comments, or other issues relating to GNU tar on windows here.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Jakarta org.apache.commons.fileupload</title>
   <link href="https://www.mayerdan.com/2005/02/07/Jakarta-orgapachecommonsfileupload"/>
   <updated>2005-02-07T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2005/02/07/Jakarta-orgapachecommonsfileupload</id>
   <content type="html">
&lt;p&gt;Using this package caused a little more problems than one would have believed, but it was by far still the best option out there. I found some other beens, but they only would successfully upload Ascii files and were not writen properly to write out image files or other binary data. After having a problem with a few other packages I went to use Jakarta’s java fileupload and after figuring out multiple issues and reading alot of documentation I got all of it working as I wanted. I thought I should share th links I ended up using that were usefull and helped me get everything working.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.theserverside.com/articles/article.tss?l=HttpClient_FileUpload&quot;&gt;File upload wrapper&lt;/a&gt; take a look at ProcessFileUpload.jsp&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://jakarta.apache.org/commons/fileupload/using.html&quot;&gt;Jakarta’s how to use the fileupload package&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you have any problems let me know and I can probably help you out.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Again Sorry</title>
   <link href="https://www.mayerdan.com/2004/11/03/Again-Sorry"/>
   <updated>2004-11-03T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2004/11/03/Again-Sorry</id>
   <content type="html">
&lt;p&gt;Again I am truly sorry that i haven’t been posting anything here. School, My job hunt, and other things have b een taking my time. I have written quite a bit of code and hopefully will post it all soon. I have alot of little pocket pc applications and I also have done decision trees, neural networks, and probabilistic agents.    I might have a pretty cool final project for my AI course to post but that will really depend on what I have time to end up doing for a project.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>sorry busy</title>
   <link href="https://www.mayerdan.com/2004/10/12/sorry-busy"/>
   <updated>2004-10-12T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2004/10/12/sorry-busy</id>
   <content type="html">
&lt;p&gt;Sorry there has been no updates and no new code posted for awhile. In fact comments were broken for the last couple months. Well I have fixed the comments and will slowly be getting back into some of the coding stuff. I have been busy with my job hunt, which seems to be going pretty well, but I dont want to jinx it.     I have been doing some interesting stuff in my AI course and learning alot, we have been working with Agents and Probablistic Inference… We are now doing Utility funtions and feedback learning systems, which is pretty cool. I wish i knew more about knowlege representation. Since coding up the problems takes me so long in part because i dont know how to represent all of the data for the problems. Oh well all is going well and I am learning, and I guess that is what that matters. The concepts I get and the coding I can figure out with time.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Intelligent Agents</title>
   <link href="https://www.mayerdan.com/2004/09/09/Intelligent-Agents"/>
   <updated>2004-09-09T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2004/09/09/Intelligent-Agents</id>
   <content type="html">
&lt;p&gt;Well I had to write a little one page paper for AI class about agents so here it is incase anyone else is ever thinking about a simple little agent that plays a wierd cave game called Wumpus world. Also, I guess some of the Ideas are a little interesting. The code to run and test everything is provided in my extended entry if you want it. (The code isn’t the prettiest or well commented seeing as I had other work to do and had to finish the entire assignement in less than two days, but it works and the smart agent does do better thant he simple one.)&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;Agents
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I created the two agents, the dumb agent that was mostly random and the more intelligent agent. The dumb agent got an avg of -450 which was close to the naive agent the professor had coded up, which averaged -390. So everything with my world and agent seemed pretty close. I then began work on the intelligent agent. I found it significantly harder than expected to add features that improved performance. Many times adding rules that I thought would help improve the agents performance and these rules would actually result in far lower scores. After trying a few pretty simple ideas I developed what my final agent became. My final agent had an average score of -60 which wasn’t great, but still significantly better than the dumb agent. I added one extra constraint to the game for fun, since I am a vegetarian I decided that I would never kill the Wumpus. So my algorithm would simple avoided the Wumpus in attempts to navigate to the gold. This lead to more impossible maps and therefor a lower overall score. It will still be interesting to see how my animal friendly agent performed in comparison to others.My more intelligent agent worked on the right hand on the maze wall idea. I would go forward until i found a area that could present a problem. A problem being either a stench or a breeze, if this problem was found i would turn around and walk the other direction and then try a different route, with my right hand facing the problem. This worked well at avoiding pits since I also had a higher percent of the time the choice of moving forward, and always would move forward if there was no chance of danger. This quickly lead to the problem of certain pits providing an infinite loop. Lowering my score by getting in a safe, but useless route. To fix this if I encountered the same pit problem multiple times i would then just walk threw the sensed breeze in hopes that the pit was not the direction I was going. This could have been improved by first trying alternate routes around the pit, but could have then left the problem of many different infinite loops.The improvement of the agent was significant and noticeable, but also illustrated the difficulties of simple relying on a simple set of rules. I think a more effective route would have been to have the agent slowly walk along any known safe route while mapping problem areas to his known portion of the map and only after exhausting all safe possibilities (and trying to create safe possibilities by killing the Wumpus) picking at random a unsafe point of passage that would lead to the most possible options for a next move.&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/2014472.js?file=wumpus.java&quot;&gt;&lt;/script&gt;

</content>
 </entry>
 
 <entry>
   <title>fighting back</title>
   <link href="https://www.mayerdan.com/2004/08/08/fighting-back"/>
   <updated>2004-08-08T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2004/08/08/fighting-back</id>
   <content type="html">
&lt;p&gt;Well lately all the spam on my various websites had been getting out of control. They have machines that go to blogs and post comments with links to ads for words to raise their rating at google. It sucks it has been annoying me and the others that i host for. So i coded up a little thing i am hoping will help. We will see if it actually does. The good news is that i learned where code resides for the comments and i can add in more code and such if i need to to start blocking all the spammers. Fun huh. In the next couple days we will see if it works.    It is a good use of learning time since today i was reading learning perl and working on perl programming and the entire blogging system was written in perl. Yeaaa I am learn a language that people told me to learn better before applying to google.    if you have any problems leaving comments anywhere tell me with email…&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>cleaning and moving</title>
   <link href="https://www.mayerdan.com/2004/07/19/cleaning-and-moving"/>
   <updated>2004-07-19T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2004/07/19/cleaning-and-moving</id>
   <content type="html">
&lt;p&gt;Today I am collecting all of the source and all of the data from News Shaker. I am preparing to move everything to a new machine that is on a live connection that everyone from the outside can get to. I will then continue to add the rest of the features mentioned below. The last week has been used to add these features, while making little to no process on improving the results of the learning. I am going to talk with a few people about how to improve the learning before working with that part of the project to much more. Overall most of the code seems quite stable enough but alot needs to be done on the user interface to make it useable by normal people that aren’t accustom to odd design and testing set ups. It must be cleaned up before it could ever really be used.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Feature requests</title>
   <link href="https://www.mayerdan.com/2004/07/06/Feature-requests"/>
   <updated>2004-07-06T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2004/07/06/Feature-requests</id>
   <content type="html">
&lt;p&gt;I need to add the following features into news shaker to make it more usefull:    Done (has an X if finished):  X    * Delete category and all related sites  X    * Delete category place all remaining sites in another category  X    * Ability to have one category be a sub category of another        * 2 level categorization (related to the sub category idea above)  X    * Automated “real world” testing with accuracy for all categories after a new model build. Should consist of 20 unseen and unmodeled sites that are hand categorized and then have them categorized.  X    * A way to save the results from the real world testing in the database and display them.        * A way to post articles that aren’t links but are actually html files into the system. (This also allows visitors to view this file.)  X    * ability for people to vote for a file that is in the wrong category to be recategorized  X    * Increased categorization speed        * Start a test from a new UID and then track where all the results go and view each result individually.        *  Making sure that two of the same sites are never added to the database        *  Checking and updating sites and getting rid of no longer existant ones.  X    *  Ability for users to report errors and admins to view them and delete  X    *  Ability for users to request categories and admins to view them and delete  X    *  Ability for administrator to recategorize based on users votes to recategorizeOn another note i have increased accuracy on testing to the 94% overall accuracy on known documents and i am getting and average of 25% for unknown documents, which isn’t horrible but i would like to do much better. I have now began to study and look into a transductive approach that i might begin to use, depending on the results of the next bits of testing.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>News Shaker</title>
   <link href="https://www.mayerdan.com/2004/06/29/News-Shaker"/>
   <updated>2004-06-29T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2004/06/29/News-Shaker</id>
   <content type="html">
&lt;p&gt;The last couple of weeks i have done alot of work on news shaker. I have done lots of testing. I all of the categories (about 12) to an approximated error average of 88%. For 12 categories this is really good. First i began by adding more and more data to the categories and rebuilding the models. This initially was increasing the percents but it ceased to help after all of the categories had about 90 documents in them. I then began to play with the weight of the positive terms. This was highly successful after increasing the weighting on all of the positive training vectors I could successfully take all of the training data and recategorize it with 88% accuracy with the remaining documents not wrongly categorized but declared to be of an unknown category. I then started real world testing giving all of my category unseen documents that were hand categorized. The results for the few real world tests i have done so far have been fairly poor, showing only 15-20% accuracy. I am not sure why that varying how the model is made dramatically increases percent of categorization of known documents but seems to have no effect on unseen documents. This currently is the problem i am working on. It is possitive to get known values accuracy for my models to range from 85% to 93%. After a little more real world testing and some other discussion i might be able to come to a conclusion as to what is going on between known and unknown examples.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>New NewsShaker Feature</title>
   <link href="https://www.mayerdan.com/2004/06/18/New-NewsShaker-Feature"/>
   <updated>2004-06-18T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2004/06/18/New-NewsShaker-Feature</id>
   <content type="html">
&lt;p&gt;After waiting weeks of meaning to add this feature I finally did it. It actually took me less than an hour when I thought i was going to have to write all sorts of new code and that everthing would somehow end up being far more complex than I wanted it to be.     Simple feature added, now instead of telling the system to crawl an entire site, you can tell the system to add a single page to the database. This makes it easier when finding an article, that links to entirely useless data, but should be added. So I am glad i finally took the time to add this simple feature. It was also good to see that I still remember alot more of the code structure on the spidering system than i would have thought I remembered.    Starting next week I am going to finish making the system entirely automated. I should be able to finish that in a couple days. Then I am going to make the system very general so it doesn’t have to remain so specific to special education and then the same code base for newsshaker would be adoptable to other systems such as the HAMCOD project (which is a horrible name, but since I am more interested in just working on the idea for now I am not going to spend any time working on a name until success full. Man I could make some amazing progress on the system if i could get about 3 people coding on these machine learning systems. Oh well it is good to be back and making some progress again.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Initial Description of HAMCOD</title>
   <link href="https://www.mayerdan.com/2004/06/17/Initial-Description-of-HAMCOD"/>
   <updated>2004-06-17T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2004/06/17/Initial-Description-of-HAMCOD</id>
   <content type="html">
&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;This is a description of a idea that may be the next phase of my project as I continue my work with text categorization. It right now is just a initial idea and outline of an idea so that I have some thought to begin working with when I get to the next point.    HAMCODHuman Assisted Machine Categorized Open Directory    A collaborative human assisted machine categorized directory. That will extend the functionality of the ODP (http:www.dmoz.com) project. This project will combine a already large and extensive base of human knowledge, with text categorization and social collaboration techniques to increase the amount of well categorized and defined websites, without the need of such high levels of human interaction that are required for DMOZ. This projects goal is to eventually use machine learning techniques to replace the slow and time consuming process of human categorization.    This will have humans interact with the machine learning process as it contributes to and works with the categorized directory. This will have humans say when something is miss categorized and they will be able to recommend new re-categorizations. Or recommend deletion from the directory. The spider would crawl for new pages that aren’t listed in the category, if a new page is discover by the spider, the system will attempt to categorize it to the lowest level of a category that it can.    So if there is a main category like “shoes” it might have a hierarchy like this:    1.&lt;strong&gt;____Shoes&lt;/strong&gt;&lt;strong&gt;__&lt;/strong&gt;&lt;strong&gt;__&lt;/strong&gt;&lt;strong&gt;____Car&lt;/strong&gt;&lt;strong&gt;__&lt;/strong&gt;&lt;strong&gt;__&lt;/strong&gt;&lt;strong&gt;__&lt;/strong&gt;&lt;strong&gt;Cheese2.&lt;/strong&gt;&lt;strong&gt;__&lt;/strong&gt;_&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;__&lt;/strong&gt;&lt;strong&gt;__&lt;/strong&gt;&lt;strong&gt;__&lt;/strong&gt;&lt;strong&gt;__&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;__&lt;/strong&gt;&lt;strong&gt;__&lt;/strong&gt;&lt;strong&gt;__&lt;/strong&gt;&lt;strong&gt;__&lt;/strong&gt;_&lt;/td&gt;
      &lt;td&gt;3.Nike__&lt;em&gt;Reebok__Vans&lt;/em&gt;&lt;strong&gt;__&lt;/strong&gt;&lt;em&gt;Ford&lt;/em&gt;&lt;strong&gt;&lt;em&gt;Toyota&lt;/em&gt;&lt;/strong&gt;&lt;strong&gt;____American&lt;/strong&gt;__Cheddar    The system would first do categorization on the level of is this page about shoes. If it is determined to be about shoes it would them use different models knowing that it is a shoe to try to determine what shoe company the page is about.    In its early stages it will only work through the Computers category of the DMOZ project. Which is already contains 149,512 websites. To first determine if a site belongs in the computers category, I will need to get about 400,000 websites at random from other parts of the DMOZ directory. Alternatively, I could assume that if I can’t find an appropriate sub category in computers that the site doesn’t belong in the computers category at all.    Initially I will design the system with no log in or registration required. Since use will be practically non existent. Once use begins I will require an email address and to have that email address to be confirmed. This will make it rather hard for companies or individuals to artificially increase their rankings with in the directory. With in each category the sites will need some sort of ranking system initially I won’t worry about ranking the sites. I will just assume that as you get specific enough there will only be a small number of sites in each category. (This seems to be true for DMOZ)&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
</content>
 </entry>
 
 <entry>
   <title>Done testing and work on SVMMail</title>
   <link href="https://www.mayerdan.com/2004/06/16/Done-testing-and-work-on-SVMMail"/>
   <updated>2004-06-16T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2004/06/16/Done-testing-and-work-on-SVMMail</id>
   <content type="html">
&lt;p&gt;After going through a little less than 3000 emails. I have finished testing nad doing any work on SVMMail. It still is sitting at a 97.5% accuracy. I am sure this could be increased, but I need to move all of my focus back to my primary project, News Shaker.     On the News Shaker front. I have added about 350 new manually categorized sites across the database. I am going to rebuild all of the models and see if the increased training data brings my percents up to a more reasonable level. Then once I have a little better percent accuracy I will begin all of the auto categorization code and just start to let the system go crazy and see how many sites it can categorize correctly when left to its own devices. Should be an interesting time next week. That is if the machine boots up. Someone was working on my system and now it freezes on boot up. I am sure all my data is still there and I have a fairly recent back up but hopefully this can be sorted out before the begining of next week.      I also have began reading Managing Gigabytes which is about compressing and indexing documents and images. I also ordered a new book about machine learning and artifical intellegence that i will begin reading soon. Perhaps they will provide me with some new ideas on how to improve my system.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>SVMMail testing update</title>
   <link href="https://www.mayerdan.com/2004/05/24/SVMMail-testing-update"/>
   <updated>2004-05-24T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2004/05/24/SVMMail-testing-update</id>
   <content type="html">
&lt;p&gt;I have now gone through about 800 emails with my SVM / SMO mail filter. I am still getting about 97.5% accuracy. I have blocked over 700 spams from my mail account. I am taking off (to europe) and I am sure my mail will fill up with about 600 emails while I am gone so I will have a much larger test results when I return. Good luck to anyone else out there working with or thinking about SVMs / SMOs for spam filters. It looks like it is a winning combination. I hope my various code and articles can help you on your way.    peace,Dan Mayer&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>SVM Mail feature vector</title>
   <link href="https://www.mayerdan.com/2004/05/20/SVM-Mail-feature-vector"/>
   <updated>2004-05-20T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2004/05/20/SVM-Mail-feature-vector</id>
   <content type="html">
&lt;p&gt;I have gotten a few emails and questions from others researchers in the community and I decided that I would begin to answer questions on my site rather than through email so any others could also benefit from the answer. So here is the first response I am posting on the web. Feel free to contact me if you have any other questions and I can try to respond.    I found your project while googling for various alternatives to spamfiltering; I’ve been thinking about trying SVM for mail filtering myself,but I’m slightly at loss as to what features to use.    A bag-of-words model comes naturally to mind, but it is not the mostefficient computationally; are you perhaps using it or something similar?First the text content is run through a common words stop list. This removes all very common and useless words when it comes to categorization. Removing words such as �the�, �is�, �that�, and so on. Then the text is converted to the SVM format of feature vectors. The feature vectors are created using a relative frequency to the total word space.     &lt;b&gt;Feature # : frequency in document / frequency in word space&lt;/b&gt;    So let�s say you have 3 documents in your space. All of the words in all of the documents would end up in your word space with a value of the total occurrences in all of the documents. Then a ratio for each document with the same word would be used for that feature. Each feature represents one word in the document. Lets do a tiny example:    &lt;b&gt;Documents&lt;/b&gt;1: �There is a little cat�2: �Where is the little cat�3:�There is a little dog, very little�    &lt;b&gt;Space&lt;/b&gt;There:2Where:1Is:2A:2Very:1The:1Little:4Cat:2Dog:1    So each of the words would be assigned a feature number in order of appearance, for instance �There� would be 1 and �Is� would be 2. Then to make the vector you would take each document and calculate the frequency ratio. For example we will do this for the 3rd document.    There(1) : �       Is : �       a : �       Little: 2/4         Dog : 1/1         Very : 1/1     Replacing the words with their feature numbers and the fractions with values would result in the final document vector based on frequency.    1:.5                2:.5               3:.5               4:.5                  5:1                  6:1     You mentioned bag of words which is another way you can create vectors to represent the text. I thought about this method and decided that the frequency would provide a better representation of each document. Neither way is that computationally affective I guess. You can leave out all features with 0 since they will be ignored in SVM anyways. I also leave out all ratios that result in less than 0.000001 because I figure that means the feature is really worthless in comparison to the space. What makes this fairly efficient is that the dictionary with the word counts is saved. So while building the model is time consuming getting new text and making comparisons is really pretty fast. Then you don�t have to rebuild the model each time you place something in a category you can only rebuild as needed or on a set time like once a week.    If you would like to see all of the libraries and math that I created to create these vectors download the source code to Text2SVM which I use to do these conversions. This also allows you to save a dictionary since each word must relate to the same feature value in the model and with any future files you plan to test against the model. This allows you to create new vectors that will match up with your old model.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>SVMMail vs Apple Mail</title>
   <link href="https://www.mayerdan.com/2004/05/19/SVMMail-vs-Apple-Mail"/>
   <updated>2004-05-19T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2004/05/19/SVMMail-vs-Apple-Mail</id>
   <content type="html">
&lt;p&gt;Well it looks like my SVM mail is very similar to apple’s Mail. Apples mail also uses a vector representation and training to achieve 98%+ accuracy (the claim). After reading through this, it makes sense why my filter is achieving such good accuracy. They are using a little more complicated vector analysis tool than I. I use SVMlight, while Apple is using LSA (Latentic Semantic Analysis), which i used to work with but I found the tools far less developed and harder to work with. It was causing me all sorts of problems to tell LSA to do the simple clustering I was doing very simple with SVMlight. The main reason it looks like they are using LSA is they first reduce the space vector and then using LSA on the reduced vector they are claiming a major performance increase. This is really believable, especially since LSA offers quick tools for folding new information into a model instead of recreating it. Anyways, I am happy to learn that the approach I took with my mail filter appears to be very similar to one of the best computer companies out there. It gives me even more reason to believe that I am on the right trail with all of my various Vector based learning algorithms.    &lt;a href=&quot;http://www.macdevcenter.com/pub/a/mac/2004/05/18/spam_pt2.html&quot;&gt;Explaining the Apple Mail Filter&lt;/a&gt;    P.S. This means that my filter that has barely been developed and has many enhancements possible is achieving about .5% less than apples filter!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>News Shaker Update</title>
   <link href="https://www.mayerdan.com/2004/05/18/News-Shaker-Update"/>
   <updated>2004-05-18T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2004/05/18/News-Shaker-Update</id>
   <content type="html">
&lt;p&gt;After doing some initial work with the 8 category problem, I have run into some problems. Nothing that can�t be solved but just some initial hiccups as expected. The very first run through I was getting approximately 30% accuracy on my categorizations. Better than random guessing but still pretty worthless. After changing to a different layout of the model, I am now getting around 43%. Which 43% (on average of the 8 models some are higher) also sucks. I now have about 5 different ideas after talking with a professor at CU at how to improve my overall percents. I am trying to get over 75% accuracy once I have about that level (which isn�t that high) I am hoping with some user feedback on the site that the model will train and improve itself. Which would be really cool, and possible since pretty much the whole process is automated now.     I first was taking all of the categories and creating a positive and a negative vector. The positive was all of the categorized data in the model. The negative was all of the other data in all the other categories. This wasn�t doing so well, so I removed the general category from the negative vector. I also removed the uncategorized data from the negative vector since it is possible these could fit in the category. Doing this increased my model accuracy from the 30% to the 43%.    I am now considering other things I could do to improve the accuracy. One of the things I am considering is a two level model. The first would only say if the model relates to special education the second level would then categorize within the special education category. This would allow me to quickly dump anything I know isn�t related to special education at all. It would also allow me on the final site to have users help with the categorization process. Anything that couldn�t be categorized better than just special education related could be placed in a general category. The general category users could view and then place in the proper category which would in turn help train the system.    I am also now considering a move from SVMlight to libSVM. Apparently libSVM offers some better options and optimizations, but still uses the same input format. This is important because text2SVM, took awhile and was written with SVMlight in mind. I have done some other optimizations on text2SVM which isn�t included in the released source because the project has begun to become less general and more specific to my project. It has improved and become far faster though. If I move to libSVM this would allow me to get results of a categorization attempt as a percent. If I had percents I could compare the results to different models better which would be useful since the value comparison between models isn�t scaled the same.    One of the problems I am running into is testing time. It takes about 2 1/2 hours or so to create and run a new test. It requires a few different steps. If I run them all at once my machine runs out of memory and crashes. So I have to run the steps one at time even though the code is completely automated, it can�t run as such without time to dump the memory. Perhaps I will have to start looking around CU for a gigantic machine that I can use to do testing much faster.     The spam filter has gone through over 500 emails now and has an accuracy of 97.5% on unseen new email. This is great, if it wasn�t so specialized to my mail I would make the filter available to everyone.    That�s it for now. The good news is I think I am still headed in the right direction and I think I will end up with a capable system. The bad news is that I think it is going to be harder and more time consuming than originally planned. I will be busy with some other stuff and out of town over the next 3 weeks so there will probably be little updated information available on the project.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>SVM Mail testing</title>
   <link href="https://www.mayerdan.com/2004/05/11/SVM-Mail-testing"/>
   <updated>2004-05-11T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2004/05/11/SVM-Mail-testing</id>
   <content type="html">
&lt;p&gt;Today I got a chance to let SVM mail sort 252 messages that it has never seen before. The messages were moved sucessfully fromt he inbox to either my real folder, or my spam folder. Out of the 252 messages the system incorrectly categorized 8 emails. Three of the 8 were actually the same message sent out from the frienster network. I have added those messages to real training model. This means on the first really good real world test my filter achieved an 96.8% accuracy. This comparess well to the 98% estimated acuuracy of the model, by categorizing the training data 98% correctly.    The system has been through about 400 emails, and my accuracy is now at 97.25 %. I have only retrained the model once including little bits of the new info I have collected. I places all of the incorrectly identified emails in the proper categories and retrained the system. Then just to see if that would make it correctly identify those emails I recategoried them, it cut the misclassifications in half, but some where still missclassified. It seems that forwarded messages with attachments are what it will missclassify still. Other emails seem good.    I am currently not working on or extending this project because it was just a testing project for some of my code, which I am now focusing back on my News Shaker project, which is using SVM classification to create a google news like site.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>SVMMail</title>
   <link href="https://www.mayerdan.com/2004/05/07/SVMMail"/>
   <updated>2004-05-07T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2004/05/07/SVMMail</id>
   <content type="html">
&lt;p&gt;I reached a great milestone with SVMMail today. I will be doing more test and releasing more information next week. The initial results are a 97% accuracy on the filter. Also with real world testing (so far a low number of 65 mails), there was only 2 errors (1 false positive) in prediction. I had training data of 550 real emails and 713 spam emails (all of which i collected in the 3 weeks or so that I ahve been working on this project.) I am really excited that I have past the stumbling blocks that I was on the last 3 days where I was actually getting a 0% accuracy because a bug the was generating a pretty much random model.    There is currently no web interface and it is all just run directly from java (jbuilder in my case). I will add features like that and the ability to track how many of each type of error my system makes later.    This is a great I am really happy with how this is working out.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>SVM Spam Filtering</title>
   <link href="https://www.mayerdan.com/2004/04/18/SVM-Spam-Filtering"/>
   <updated>2004-04-18T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2004/04/18/SVM-Spam-Filtering</id>
   <content type="html">
&lt;p&gt;Support Vector Machine (SVM) Spam filtering. After working more with SVM and my software to utilize it moving along nicely, I am begining to do work on a spam filter for my email. I have been getting a much larger amount of email in the last couple months.  The spam is highly recognizable and simple to see patterns in, 90% of it is related to buying prescription drugs. I figured that SVM should be able to detected this very easily. Since i have already written, a few java email applications, I thought it would be easy enough to write a program that will let me log on and list things as spam and then use that to train and SVM model, which takes far less training data than many other machine learning methods. For now i have done a very simplistic design, the first step will, be building a simple webmail system, it will really only be used to mark as spam, and check the spam folder to make sure there was no false positives. Then I will use my normal mail system to read and respond to the mail. When i first log into my simple webmail all new mail will be download and categorized. I will then be able to submit any missed items as spam. Anything newly categorized as spam or not will be places in either the spam, or real mail section of the database. The database will store who the mail was from, the subject, and the body of the message.      I have now created two folders in my mail system. old and spam. I have moved all my old real mail to the old folder and all spam to the spam folder. I have about 300 spam messages and about 520 real messages. I have finished hte code for the mail checking and sorting using java. I now imported my Text2SVM software and used the to create the orginal model file. Now similiarly to my NewShaker software all that is left to do is have autocategorization. For spam this should be a little simpler since there are only two classification levels. This should let me learn the process that I will need to use for Newsshaker, but at a little simpler level. I created a model using 170 of real mail, vs spam. With it I was getting an 80% recognition on my test data, I think with some simple fixes in the way I am creating my text from my mail, stop listing, and stemming I should be able to increase this rate. I should also be able to increase the rate once I add more training data.     (This was created as a sub project to create and test code that will be used for newsshaker)    &lt;br /&gt;Here is my initial design.&lt;br /&gt;&lt;br /&gt;&lt;a href=&quot;./gif_1.gif&quot;&gt;&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Programming progress</title>
   <link href="https://www.mayerdan.com/2004/04/13/Programming-progress"/>
   <updated>2004-04-13T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2004/04/13/Programming-progress</id>
   <content type="html">
&lt;p&gt;Today I accomplished alot on many different programming projects. 3 to be correct. Anyways, I got my comp org program almost done which is nice. I made some really good progress on my text categorization project for work. Then finally for my 3d graphics class i made some good progress on my very simple 3d shooter.&lt;/p&gt;

&lt;p&gt;I am happy to have accomplished so much, but on the downside it is 10:30 and i have been programming the entire day on one project or another. Argg…&lt;/p&gt;

&lt;p&gt;I swear i will have to do something fun and exciting for all of you soon. Then i will have something worth writting about again, but until then here is a picture of my 3d shooter in progress&lt;/p&gt;

&lt;p&gt;BROKEN image&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>A step closer to fully automated</title>
   <link href="https://www.mayerdan.com/2004/04/13/A-step-closer-to-fully-automated"/>
   <updated>2004-04-13T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2004/04/13/A-step-closer-to-fully-automated</id>
   <content type="html">
&lt;p&gt;After recent success with my models i wanted to do some much more involved and usefull tests. The only problem was half of the stuff i was doing by hand. I had written some java software Text2SVM toat would help witht he conversions and such, but i had to give it the names of the files and everything myself. I now integrated Text2SVM more into newsshaker. It uses the database to find out the names of all the categories. It recursively sorts through all the text files in given directories. It is quite nice. I ran into some problems where i was running the entire system out of memory even with a full gig given to java, which would cause way to much swapping and slow the hold system down. I rewrote my code to break up the steps into smaller parts using much less memory and it now doesnt crash and runs 2 or 3 times faster.     I now can go straight from the database to 9 SVMlight formated text training datasets for models. The next step will be to write some code the generates the models using an interface between java and SVMlight. After that i will be writting some code to keep all the results of the testing organized and worthwhile, which i will be storing in the database. The final step will then be writting code that takes many random documents from my database and trys to categorize them, and stores if they were correctly told to be place in the category from which they came. I will then be storing a matrix of attempts to categorize a category and where it was actually categorized. This should be highly exciting because it will really let me see the info I need to know to make my system worka dn to know how reliably it works, and where the problems are occuring. It would let me for instants see if the space between special education parenting is to similiar and close to special education school to be determined, but if combined would serve as a valid and seperate categorization from everything else. School will probably keep me busy for awhile, but it is nice to be making some good progress and see a first beta version coming into very close view.  Then i will have a wonderfull testing and development application for text categorization. Hopefully by the end of summer i will have everything generalized to the point that anyone could add categories and begin maintaining a category and seeing how they can get different results with different information retrival schemes, or using diffferent categorization algorythms besides SVMs.    Anyone have some other algorythms worth looking into for text categorization, besides LSA/LSI and SVMs?&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Busy</title>
   <link href="https://www.mayerdan.com/2004/04/09/Busy"/>
   <updated>2004-04-09T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2004/04/09/Busy</id>
   <content type="html">
&lt;p&gt;Alright i have been really busy on my senior project for the last couple weeks so I haven’t had time to really get any work done. I did however have a meeting with a CU AI professor who is familiar witht he use of SVMs. He thought i was on the right track and was approaching everything properly. He answered the one last question i had before i can really set the system up to start working on its own for categorization. So that was exciting. Now all i really need is a week or two to really spend some time coding. If i had the time i really think i could make a very impressive first run beta of the entire system. Then with some time i think it oculd turn into a pretty cool application. I am hoping to tweak some of the hard coded values to allow for much more flexability allowing hte system to be set up anywhere and work on categorizing any categories the user wants. The categories are currently hard coded into a few of the functions and in the end that wont be the best way to do things.    Seperate good news is that my senior project just passed a 1 and 1/2 hour live test with 80 law students and a professor with no errors and no problems. That is the longest time the system has been used continously and by far the most users on the system at once. It is really kind of cool to think about the fact that their was 80 law students actively using something that i was a large part of creating. We are going to run a larger more extensive test on monday where the professor will be braodcasting a bunch of questions in class. We will see how that works out in the end, I am excited about it. The site will move soon, but if anyone is interested in looking at what my senior project can do take a look at    &lt;a href=&quot;http://blackout.cs.colorado.edu/mtroom3/jsp&quot;&gt;http://blackout.cs.colorado.edu/mtroom3/jsp&lt;/a&gt;    It is an interactive classroom for the law school, so that professors can more easily get feedback and quiz large classes of students.    Finally a good friend of mine from my research lab was notice by google and they called her up to talk to her and ask for her resume. So that is some pretty exciting news, that they are actively searching out talent. Perhaps as my project matures they will stumble apon what i am doing.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>FIRST GOOD SUCCESS!!</title>
   <link href="https://www.mayerdan.com/2004/03/30/FIRST-GOOD-SUCCESS"/>
   <updated>2004-03-30T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2004/03/30/FIRST-GOOD-SUCCESS</id>
   <content type="html">
&lt;p&gt;Using my Text2SVM after learning how to increase java memory size so that i could do a large test was very successful. I used about 2,700 documents and then put them into SVMlight. I used 90% training data and 10% testing data leaving around 270 tests. I achieved 95% accuracy in training SVM to recognize one text category from another. I am highly excited! Tomorrow i will be testing with several of my smaller categories today i tested with my largest category. If these trends contrinue…. eeehhhh. I was really only hoping to achieve over 80% correct. So lets hope this is the start of something truely wonderful. I know that the results continuing this high are very unlikely, but i am still really excited about the first good results. I am pumped about doing more testing tomorrow!!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Current Percent levels</title>
   <link href="https://www.mayerdan.com/2004/03/30/Current-Percent-levels"/>
   <updated>2004-03-30T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2004/03/30/Current-Percent-levels</id>
   <content type="html">
&lt;p&gt;These are the current success levels for models based on different categories. These models were built with the current data in the News Shaker database. They were converted to SVMlight models with the use of Text2SVM without stemming enabled.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>NewsShaker: Features needed / bug report V.001</title>
   <link href="https://www.mayerdan.com/2004/03/29/NewsShaker-Features-needed-bug-report-V001"/>
   <updated>2004-03-29T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2004/03/29/NewsShaker-Features-needed--bug-report-V001</id>
   <content type="html">
&lt;p&gt;Features:* from empty DB to fully functional (no manual data entry)* No code should contain hard coded value refrencing categories* Easy way to define where search should put results default (in a category or in the uncategorized)* Ability for user to say they think the categorization is incorrect (if enough users say this it should be moved to the new category)*  Store all SVM results int he database* a way to visualize the distances between the categories..* how many categorizations per category have been correct or incorrect* a way to start over the correct and incorrect after generating new SVM models* SVM weighting with C towards the positive examples* a way to let a user create their own account and their own categories to manage themselves* When administrator is adding a URL to crawl. Should be able to pick depth to crawl and default category that all results will be placed in. * Search entire database or by category using Lucecne* Ability to add single page to a category* More administration features* Ability to start or stop auto categorization* caching the front page* Text2SVM integration* Text2SVM configuration file* ability to create the SVM categories from the web as administrator* create only the dictionary and store it as one function* use stored dictionary to create all needed spaces for SVM models* ability to distinguish between multiple categories instead of single boolean.    Bugs:* If the user chooses to move a page from one category to another but doesn’t choose a new category it should do nothing and give them an error.* Crawling from the web doesn’t work anymore* SVM first word spacing?? with the first character removal??? is this still a bug?* Counts for the categories should be switch to be autocounted* Text2SVM runs out of memory on large examples* front page loads to slowly* categories are manually counted let MYSQL do the counting!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Text2SVM</title>
   <link href="https://www.mayerdan.com/2004/03/09/Text2SVM"/>
   <updated>2004-03-09T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2004/03/09/Text2SVM</id>
   <content type="html">
&lt;p&gt;This is the main page for “Text2SVM” a java program that converts text documents to the format require for SVMlight. The project is currently in beta. I figured that i should release a early working version for anyone else that has been finding it very difficult to find software to help them work with SVM’s. This project is distibuted as is with no garuntees. So use it try it out. Right now it is written very specifically to handle text files conversion to a single format for SVM light. This should be capable of being edited to support many other formats though. To read more about &lt;a href=&quot;http://svmlight.joachims.org/&quot;&gt;SVMlight &lt;/a&gt;visit their page. If you make any good modifications or changes of this program I would love to hear about it please contact me at ddmayer (at) colorado (dot) edu (spam pertection). You can also leave comments on this page so other users could help solve your problems.    To download the &lt;a href=&quot;/computer_science/files/Text2SVM.zip&quot;&gt;code click here&lt;/a&gt;.    This first release 0.001 include these features:-Import entire directory of files-Save dictionary to generate other word vectors based on same SVMmodel-export files in SVMlight format-Stemming to increase the occurance of words like (stopping and stopped)-The ability to import saved dictionaries and continue adding to the model-debugging code left in    What is SVM, and why would i want to convert text to a word vector?Support Vector Machines. They are a method or clustering and machine learning. In this examples we are working with Text categorization using SVM. This programs purpose is to take a bunch of documents in a category and a bunch of documents unrelated to that category. It then creates word vectors out of all the documents. The word vectors then are converted to SVMlights format and given positve weight and negative weight. Anytime the document vector has a possitive weight that means we consider it part of our category. A negative weight means it is outside of our category. After the text is written in this format you can use SVMlight to compare unknown text and see if it belongs in this category.    KNOWN ISSUES:- occassionally cuts the first letter off of a document.- uses high amount of memory to run the program add -Xmx256x then the program your trying to run. wc.java is known to run out of memory with large amounts of text.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Week of stuff</title>
   <link href="https://www.mayerdan.com/2004/03/08/Week-of-stuff"/>
   <updated>2004-03-08T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2004/03/08/Week-of-stuff</id>
   <content type="html">
&lt;p&gt;Well this is going to be a busy week. Instead of doing homework on saturday. I went to ft. Collins because my friend Matt wanted me to come up. Also since steve is in town i have been hanging out with him and doing stuff and there for not getting my work done. Today oddly enough instead of spending my time working on my various school projects, I worked for 6 hours on my work project. I accomplished quite a bit and I am now ready to introduce my &lt;a href=&quot;http://www.deadawakemovie.com/ml&quot;&gt;Machine Learning blog&lt;/a&gt;. It follows my work and progress on some projects that i am working on and serves to be a center for most of my coding and source code.    I am sure that doesn’t interest 90% of the people that read my blog, but oh well. I am planning to be really busy until spring break. Which is kind of annoying. I wanted to ask this girl out, but i really dont have any time to go out on a date before spring break. Also, the whole spring break thing is still up in the air. I seriously have no clue what I will be doing. Scott and Dom still dont know when they are off work, how much time they will have and such. Also, I am pretty sure they both would rather just spend it with thier girlfriends. So I am really thinking about just going to vegas with some of my other friends. Also Megan, one of my good friends from highschool is coming to colorado the same week as my break for her springbreak. I would really like to see her, but it is likely that no matter what I do i will be out of town.  Maybe I should just flip a coin a couple of times and figure it out.    Well now I am off to a statistics review session.. yeaaa school from 7-9! You got to love being inside the entire day when it is beautiful outside.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Text to vectors progress</title>
   <link href="https://www.mayerdan.com/2004/03/04/Text-to-vectors-progress"/>
   <updated>2004-03-04T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2004/03/04/Text-to-vectors-progress</id>
   <content type="html">
&lt;p&gt;I finished the basic text to vector, but in its current form it is kinda useless. It only outputs to system.out in a format that isn’t usefull yet. Also it doesn’t save the dictionary that it uses to generate the vectors. So you can’t generate any new word vectors to run comparisons with. I will be adding in features to save the generation dictionary, to import a saved dictionary, and to output the feature word vectors in the SVM light format. I also have recently found some cool java stemming software that I will add in to my project after I get the first usable version out. If your interested in the stemming software here are the links:    &lt;a href=&quot;http://www.ils.unc.edu/keyes/java/porter/&quot;&gt;Porter Stemming Algorythm&lt;/a&gt;    &lt;a href=&quot;http://www.comp.lancs.ac.uk/computing/research/stemming/paice/paicejava.htm&quot;&gt;Lancaster Stemmer&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Converting documents to word vectors</title>
   <link href="https://www.mayerdan.com/2004/03/04/Converting-documents-to-word-vectors"/>
   <updated>2004-03-04T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2004/03/04/Converting-documents-to-word-vectors</id>
   <content type="html">
&lt;p&gt;After unsuccessfully searching all over the web for source that would do this, I began writting my own code to convert all of my documents to word vectors based on a overall word space. I have a program that came with LSA called Pindex that does this but for LSA and it seems to give some odd results that I am not sure if they are compatable with SVM’s. (I know that the formatting is different, which I will be converting to SVMlight formatting, but I am not sure if the data is valid.) I am now making a word vector for every document that will just be its total ratio to a total spaces count of those words. I will then have a seperate java app that allows you to give it two sets of documents and it will generate the proper SVM light input file. Giving one set of documents the positive value and the other set the negative. Then I should be able to create take any new text create its word vector based on the same dictionary and then run comparisons in SVMlight. I am hoping to have the text to vector done this weekend. It shouldn’t be to difficult. Then I will release it on the web since i had a next to impossible time trying to find anything like this on the web.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Space Altering</title>
   <link href="https://www.mayerdan.com/2004/03/02/Space-Altering"/>
   <updated>2004-03-02T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2004/03/02/Space-Altering</id>
   <content type="html">
&lt;p&gt;I ran some initial results that would tell me how closely related all the documents within a category were related. we were looking for numbers in the .75 range. The first attempt was actually returning the .339 for one category and .328 for another. These were rather low. To improve the mean of the categories we thought creating a larger space with an overall larger vocabulary would help the documents to be more related. We increased the space from 987 total documents to 1798 documents. I didn’t add any documents to either of the test categories, only to the overall space (documents that were really not related to either catgory). I also added a simple stoplist filtering out some common but irrelavent words. This didn’t seem to help the relational mean between the two categories. The first category lowered from .339 to .320 while the second increased from .338 to .349. Now I have to decide wether to greatly increase the space, or greatly increase the documents within the categories (which i have to do by hand so it is a slow and time consuming process.)&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Very cool clustering</title>
   <link href="https://www.mayerdan.com/2004/03/01/Very-cool-clustering"/>
   <updated>2004-03-01T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2004/03/01/Very-cool-clustering</id>
   <content type="html">
&lt;p&gt;This isn’t quite related to my own machine learning research but it is cool to see what others out there are doing with machine learning. This is a very cool search engine that using text clustering.    &lt;a href=&quot;http://vivisimo.com&quot;&gt;http://vivisimo.com&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Setting Up</title>
   <link href="https://www.mayerdan.com/2004/03/01/Setting-Up"/>
   <updated>2004-03-01T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2004/03/01/Setting-Up</id>
   <content type="html">
&lt;p&gt;This is going to be a research blog kept by Dan Mayer to show his progress on his computer science research into machine learning.    I will be setting up a collection of all the work and research that I have done on machine learning. I am working on a project for the L3D labs at the University of Colorado. I am working specifically with text categorization, currently focused on using LSA and SVM to categorize. I will post all the links, projects, examples, and resources that I have used and learned from during my progress on this project.    This is documenting the work on my project &lt;a href=&quot;http://clever.cs.colorado.edu:8080/newsShaker/jsp/index.jsp&quot;&gt;News Shaker&lt;/a&gt;, which will be similiar to &lt;a href=&quot;http://news.google.com/&quot;&gt;google news&lt;/a&gt;. It uses a web spider and text categorization to organize the data into particular categories. The categories I am currently working with are special education related. The topics of the categories were choosen so that this project would become a sub project of &lt;a href=&quot;http://www.web2gether.org&quot;&gt;Web2gether&lt;/a&gt;, which is a site to help the special education community.    Currently all of the categories seen on the News Shaker site are manually sorted and loaded into the system after the spider crawls specific topics on the web. I am now working with LSA and SVMs to begin the text categorization of newly crawled websites and results. This project is also setting up a easy to use web managed content management system. This would allow for the quick set up and design of any topic based categorization using the methods developed while creating the original News Shaker which is focused on special education.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>open gl progress</title>
   <link href="https://www.mayerdan.com/2004/02/16/open-gl-progress"/>
   <updated>2004-02-16T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2004/02/16/open-gl-progress</id>
   <content type="html">
&lt;p&gt;Well first sorry about the site being down for awhile, my hosting service had some problems, but they have it fixed.    Second, i finished my openGL project for fractal terrains today. This project took me entirely way to long, but it has been hard to move back to c++ after having all the nice things java provides you for so long. Anyways i am slowly getting back on track. So take a look and i hope you all like my little fractals. It is alot cooler if you actually have the program since you can rotate and do all sorts of stuff with the terrain then. Oh well back to laying and now moving.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>finished opengl</title>
   <link href="https://www.mayerdan.com/2004/02/02/finished-opengl"/>
   <updated>2004-02-02T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2004/02/02/finished-opengl</id>
   <content type="html">
&lt;p&gt;I just finished my first opengl progam. I know this one isn’t 3d but it could be. I just had a hard time moving back to c++ at first since i am so used to java and remembering all the wierd ways to pass things in c++ sucked. Anyways simple but wonderfull since i am done with my first working and well documentated opengl program. I think i have a good understanding of how to work with opengl now as well. Anyways it is a fractal look and enjoy. i know it is simple.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Well today was a big</title>
   <link href="https://www.mayerdan.com/2004/02/02/Well-today-was-a-big"/>
   <updated>2004-02-02T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2004/02/02/Well-today-was-a-big</id>
   <content type="html">
&lt;p&gt;Well today was a big relief… Since my server at work died twice on friday, I have been nervous all weekend that i might have lost all my work. today we recovered the server and i made sure to make backups of all of my files. So now even if the server dies (since we really dont know what the problem was) i wont loose any work. My project at work it moving along nicely. Their are about 900 sites in the database currently and we should be categorizing on the fly by the end of this week. Which will be a huge step. If all goes well i might have a highly successfully project right away. If not with some refinement over a few weeks we should be able to make it fairly successful.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Cool project</title>
   <link href="https://www.mayerdan.com/2004/01/15/Cool-project"/>
   <updated>2004-01-15T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2004/01/15/Cool-project</id>
   <content type="html">
&lt;p&gt;I just learned that the final project in my computer graphics course is to make a simple 3d Doom like game. I am excited. Depending on the models and if we can make them or we are just using stock models I will try to put a bunch of my friends in the game so that I can kill them and they can all kill each other. Yeaaaa violence. Either way it is going to be my first few steps in the direction of 3D graphics which is really cool and exciting.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>OpenGL Resources</title>
   <link href="https://www.mayerdan.com/2003/11/08/OpenGL-Resources"/>
   <updated>2003-11-08T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2003/11/08/OpenGL-Resources</id>
   <content type="html">
&lt;p&gt;This is a collection of various tips and things I use or have done in OpenGL. This is actually up on the web more as a refrence to myself than anything else. This is a collection of good links, programs, tutorials, and source code. That I have found usefull, created, or thought would be usefull to others. So enjoy any and all of my OpenGL info.    &lt;b&gt;Projects:&lt;/b&gt;    3D shooter I wrote, it could obviously be improved alot but it is a good easy start.&lt;br /&gt;&lt;a href=&quot;http://WWW.deadawakemovie.COM/ml/archives/3dshoot.jpg&quot;&gt;&lt;/a&gt;    You can download the source and the exe and everything and mess with it yourself. This was written in Visual Studio.net   &lt;a href=&quot;http://www.deadawakemovie.com/ml/files/Mayer_GL.zip&quot;&gt;Get it&lt;/a&gt;&lt;br /&gt;I also made a fractal program into a openGL winamp plug in.  If your interested in doing conversions to a winamp plug in yourself, you can download the source and the DLL for winamp and mess around with this.&lt;br /&gt;&lt;a href=&quot;http://www.deadawakemovie.com/ml/files/winamp.zip&quot;&gt;Get the winamp code&lt;/a&gt;        &lt;b&gt;Links:&lt;/b&gt;    &lt;a href=&quot;http://graphics.stanford.edu/data/3Dscanrep/&quot;&gt;A group of scanned 3D object models, and how to read and write them.&lt;/a&gt;    &lt;a href=&quot;http://nehe.gamedev.net/&quot;&gt; NEHE the best place to go for open GL resources.&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>C++ resources</title>
   <link href="https://www.mayerdan.com/2003/11/04/C-resources"/>
   <updated>2003-11-04T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2003/11/04/C-resources</id>
   <content type="html">
&lt;p&gt;This is a collection of various tips and things I use or have done in C+. This is actually up on the web more as a refrence to myself than anything else. I keep learning this stuff, but since I dont use it all that frequently I tend to forget how to do something exactly the next time i need to. So i will just keep a bunch of files that i use for refrence when coding. I don’t use C++ as much now since I have been working mostly with Java at my job. Anyways here is some c++ stuff:&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>MySQL resources</title>
   <link href="https://www.mayerdan.com/2003/11/02/MySQL-resources"/>
   <updated>2003-11-02T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2003/11/02/MySQL-resources</id>
   <content type="html">
&lt;p&gt;This is a collection of various tips and things I use in MySQL. This is actually up on the web more as a reference to myself than anything else. I keep learning this stuff, but since I don’t use it all that frequently I tend to forget how to do something exactly the next time i need to. So here are little commands I use and some tiny descriptions. If you have some tips tricks or think i am doing something the hard way please feel free to comment and share. If you have any MySQL questions please feel free to post them if you think it is something I might be able to help you with. That said here is mysql information…&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;&amp;lt;b&amp;gt;MySQL Commands:&amp;lt;/b&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;//to start mysql on the command line
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Mysql �u root �p   (It will then ask for your password)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;//To start mysql server
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Sudo bash�bin/safe-mysqld� from the mysql root directory&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;//How to select data using rules involving more than one table from MYSQL:
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Select * from user leftjoin link on user.id = from Left join msg on to=msg.id where user.name = �john�;    Database:mysql -u root �p ***&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;//snowmass    //to create a text dump of a database
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mysqldump �-user [user name] �-password=[password] [database name] &amp;gt; [dump file] &lt;/code&gt;&lt;/p&gt;

&lt;p&gt;//to create a new databasemy
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sql&amp;gt; create database somedb;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;//granting privileges to a user
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grant all privileges on *.* to NewsShaker@�localhost&quot; &amp;gt; identified by &apos;passwrd&apos;;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;//shows all the tables of a database
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;show tables;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;//creates a new table in the database your currently using
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;create table &quot;tablename&quot; (&quot;column1&quot; &quot;data type&quot;, &quot;column2&quot; &quot;data type&quot;, &quot;column3&quot; &quot;data type&quot;);&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;//creates a link table (which is really the same as other tables but just//a way to associate data between tablescreate table link (catID bigint, pageID bigint);&lt;/p&gt;

&lt;p&gt;//deletes an entire tabledrop table Pages;&lt;/p&gt;

&lt;p&gt;//deletes an entry from table idgen where the item has a uid of 12delete from idgen where uid=12;&lt;/p&gt;

&lt;p&gt;//show only the colums of a table not the datashow columns from tablename;&lt;/p&gt;

&lt;p&gt;//To change the data of an already existing entry
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;update tablename set columname = &quot;whatever&quot; where columnname = &quot;something&quot;;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;//to alter a table if you need to add a new field
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;alter table tablename add column_name column_type after column_name2;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;//creates a dump of database called newsshaker
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;./mysqldump �u root �p newsshaker &amp;gt; ../../../ddmayer/newsshakerdb.txt//at command line in the mysql/bin directory&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;//to recreate the database from the dump file
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;./mysql -u root -pPASSWORD newsshaker &amp;lt; ./newsshakerdb.txt//at the command line in the mysql/bin directory&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;//to get the count in a category since a date:
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;select count(*) as co from Pages as p left join link as l on p.UID = l.PageID left join categories as c on c.UID = l.CatID where c.name like &apos;autism&apos; and p.date &amp;gt; 20031201000000 group by c.UID;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;//to get only 5 in order as the very newest:
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;select p.UID,p.date,c.UID from Pages as p left join link as l on p.UID = l.PageID left join categories as c on c.UID = l.CatID where c.name like &apos;autism&apos; and p.date &amp;gt; 20031201000000 order by p.date DESC limit 5;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;//to get a whole category:
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;select p.UID,p.date,c.UID from Pages as p left join link as l on p.UID = l.PageID left join categories as c on c.UID = l.CatID where c.name like &apos;autism&apos;;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;//add a new user to your mysql system
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mysql&amp;gt; GRANT ALL PRIVILEGES ON *.* TO &apos;monty&apos;@&apos;localhost&apos;    -&amp;gt;     IDENTIFIED BY &apos;some_pass&apos; WITH GRANT OPTION;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;//getting random results from a mysql query or mysql table
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mysql_query(&quot;SELECT * FROM table ORDER BY RAND() LIMIT 1&quot;)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;//renaming multiple tables at the same time.
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RENAME TABLE old_table    TO backup_table, new_table TO old_table, backup_table TO new_table;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Links:&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.mysql.org&quot;&gt;MySQL home&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://sqlcourse.com/insert.html&quot;&gt;My SQL commands tutorial&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.javacoding.net/articles/technical/java-mysql.html&quot;&gt;Java MySQL tutorial&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.developer.com/java/data/article.php/3417381&quot;&gt;Great java Mysql developer tutorial&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Linux / Unix Resources</title>
   <link href="https://www.mayerdan.com/2003/11/02/Linux-Unix-Resources"/>
   <updated>2003-11-02T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2003/11/02/Linux--Unix-Resources</id>
   <content type="html">
&lt;p&gt;This is a collection of various tips and things I use in linux. This is actually up on the web more as a refrence to myself than anything else. I keep learning this stuff, but since I dont use it all that frequently I tend to forget how to do something exactly the next time i need to. So here are little commands I use and some tiny descriptions. If you have some tips tricks or think i am doing something the hard way please feel free to comment and share. If you have any Linux / Unix questions please feel free to post them if you think it is something I might be able to help you with. That said here is what I got…    &lt;b&gt;Commands:&lt;/b&gt;// secure copy. This allows you to copy a file to another computer over SSHscp filename compname:~/.  (exactly like this no spaces may occur anywhere in this)    //if something is in the background or you accidently suspended it you//can bring it back to the front by typingfg (in the command line)    //if you have sudo access you can do about anything on the systemsudo cmd (any command you want, then it will ask for your password)    //to quickly view a fileless filename.txt    //to open and edit a fileemacs filename    //to add to your path and edit many other settings this in your home directoryemacs .cshrc    //to get to your home directorycd (or sometimes cd $HOME)    //FTP stuffftp domain.comftp&amp;gt; cd directoryftp&amp;gt; put filenameftp&amp;gt; get otherfileftp&amp;gt; quit    //To add users to the systemsudo adduser    //edit a users password if your root or can sudosudo passwd username    //to change your users password while logged on the systempasswd    //to give users sudo accessudo visudo//to run a program and keep the console availableprogram &amp;amp;    //if you dont have the proper permissions for a filechmod 666 filename (this gives read and write access to everyone, so it isn’t secure, man chmod if you need a specific file rights)    &lt;b&gt;TomCat:&lt;/b&gt;To start tomcat run �bin/start-tomcat� from tomcats home directoryTomcat installs /var/tomcat/webappsTomcat really /usr/local/Jakarta-tomcat&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Java Resources</title>
   <link href="https://www.mayerdan.com/2003/11/02/Java-Resources"/>
   <updated>2003-11-02T00:00:00+00:00</updated>
   <id>https://www.mayerdan.com/2003/11/02/Java-Resources</id>
   <content type="html">
&lt;p&gt;This is a collection of various tips and things I use in Java. This is actually up on the web more as a refrence to myself than anything else. I keep learning this stuff, but since I dont use it all that frequently I tend to forget how to do something exactly the next time i need to. So here are little commands I use and some tiny descriptions. If you have some tips tricks or think i am doing something the hard way please feel free to comment and share. If you have any Java questions please feel free to post them if you think it is something I might be able to help you with. That said here is some java stuff…    First off i think the best way to work with java code and projects is using J Builder. It is available free for personal use. I highly recommend it. It is available for linux and windows. get it from &lt;a href=&quot;http://www.borland.com/jbuilder/&quot;&gt;Borland&lt;/a&gt;    If your going to develop web applications I also highly recommend apache tomcat. It is a free java (JSP) server. It is also available for both windows and linux which is great. get it from &lt;a href=&quot;http://jakarta.apache.org/tomcat/&quot;&gt;Apache Tomcat&lt;/a&gt;    If a java program keeps running out of memory, this is easy to fix. The java virtual machine is only given so much memory. So running:”java -Xmx256m PROGRAMNAME” should fix the problem by &lt;!--more--&gt; giving the virtual machine 256 mb of memory. You will see this error as a java.lang.outofmemory error. If your using J builder go to project-&amp;gt;run-&amp;gt;Java VM parameters: and add the -Xmx256m to the parameter line.    Here’s how you could rewrite that statement using the ?: operator:    System.out.println(“The character “ + aChar + “ is “ +                   (Character.isUpperCase(aChar) ? “upper” : “lower”) +                   “case.”);    The ?: operator returns the string “upper” if the isUpperCase method returns true. Otherwise, it returns the string “lower”. The result is concatenated with other parts of a message to be displayed. Using ?: makes sense here because the if statement is secondary to the call to the println method. Once you get used to this construct, it also makes the code easier to read.    &lt;b&gt;Files:&lt;/b&gt;One of the first Java projects I did the source and some info on a AOL Instant messenger (AIM) chatterbot. Built using simpleaim, megahal and some of my own work. Most of the work was in combining the programs learning how they worked and such though. I did add a nice logger to it as well. This still needs alot of work to be that impressive. download &lt;a href=&quot;./files/MegaDan.zip&quot;&gt;MegaDan.zip&lt;/a&gt;    A part of my machine learning, for project News Shaker. Text2SVM converts text documents into the proper format for use with SVMlight. It is fairly well commented and contains the full source code. This is a good easy way to learn to work with reading and writting files since it has alot of IO. Visit this projects own page, &lt;a href=&quot;http://www.deadawakemovie.com/ml/archives/000138.html&quot;&gt;Text2SVM&lt;/a&gt;.    My edited and improved version of weblech. Weblech is a web spider. I added many features and costumized it for my own  purposes. This crawler uses google to find files that are relavant to whatever topics your working on. &lt;a href=&quot;http://programming.wastedbrains.com&quot;&gt;Download the improved weblech from here&lt;/a&gt;    Links:&lt;a href=&quot;http://sourceforge.net/projects/weblech/&quot;&gt;A great starting point for building a java web spider (Weblech)&lt;/a&gt;    &lt;a href=&quot;http://programming.wastedbrains.com&quot;&gt;My edited and improved version of weblech&lt;/a&gt;    &lt;a href=&quot;http://home.clara.net/robmorton/projects/wc/&quot;&gt;A great Java Word Counter / dictionary builder&lt;/a&gt;    &lt;a href=&quot;http://www.csd.abdn.ac.uk/~pgray/teaching/CS3011/prac4.html&quot;&gt;Stop List&lt;/a&gt;    &lt;a href=&quot;http://www.javapractices.com/Topic87.cjp&quot;&gt;Parsing Text&lt;/a&gt;    &lt;a href=&quot;http://www.javacoding.net/articles/technical/java-mysql.html&quot;&gt; Java MySQL tutorial&lt;/a&gt;    &lt;a href=&quot;http://www.ils.unc.edu/keyes/java/porter/&quot;&gt;Java Porter Stemming Algorythm&lt;/a&gt;    &lt;a href=&quot;http://www.comp.lancs.ac.uk/computing/research/stemming/paice/paicejava.htm&quot;&gt;Java Lancaster Stemmer&lt;/a&gt;    &lt;a href=&quot;http://www.airporttools.com/other/programming/hashsorting/&quot;&gt; Sorting a hash table in java&lt;/a&gt;    &lt;a href=&quot;http://jigsaw.w3.org/Doc/Programmer/api/org/w3c/tools/sorter/Sorter.html&quot;&gt; Java sort class (sorts most types)&lt;/a&gt;    &lt;a href=&quot;http://neuron.eng.wayne.edu/&quot;&gt; a great java machine learning site&lt;/a&gt;    &lt;a href=&quot;http://java.sun.com/developer/onlineTraining/JavaMail/contents.html&quot;&gt; Pretty much everything you would need to work with java Email &lt;/a&gt;    &lt;a href=&quot;http://www.ftponline.com/javapro/2004_01/online/javamail_kjones_01_28_04/default_pf.aspx&quot;&gt; How to work with multipart email messages&lt;/a&gt;    &lt;a href=&quot;http://java.sun.com/docs/books/tutorial/uiswing/components/example-1dot4/index.html#TextSamplerDemo&quot;&gt;Great Java Swing Tutorials, especially the stuff on the grid bag layout&lt;/a&gt;    &lt;a href=&quot;http://java.sun.com/j2ee/1.4/docs/tutorial/doc/&quot;&gt;Great webservices and java XML info&lt;/a&gt;    &lt;a href=&quot;http://www.manageability.org/blog/stuff/top-ten-truly-obscure-java-projects/view&quot;&gt;The 10 best and unervalued least known about java libraries. some of these would be great to use in many projects without reimplementing there feautres yourself.&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 
</feed>