<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://vesavanska.com/atom.xml" rel="self" type="application/atom+xml" /><link href="https://vesavanska.com/" rel="alternate" type="text/html" /><updated>2026-03-01T17:28:26+00:00</updated><id>https://vesavanska.com/atom.xml</id><title type="html">Vesa Vänskä</title><subtitle>Blog of a thirty-something Ruby developer living in Helsinki working for Kisko Labs.</subtitle><author><name>Vesa Vänskä</name></author><entry><title type="html">Euruko 2022 talk on trunk-based-development</title><link href="https://vesavanska.com/2022/euruko-2022-talk-on-trunk-based-develoment" rel="alternate" type="text/html" title="Euruko 2022 talk on trunk-based-development" /><published>2022-10-30T00:00:00+00:00</published><updated>2022-10-30T00:00:00+00:00</updated><id>https://vesavanska.com/2022/euruko-2022-talk-on-trunk-based-develoment</id><content type="html" xml:base="https://vesavanska.com/2022/euruko-2022-talk-on-trunk-based-develoment"><![CDATA[<p>Two weeks I gave a talk at Euruko 2022 on trunk-based development with the full title of From massive pull requests to trunk-based development.</p>

<p>Here is the talk description:</p>

<blockquote>
  <p>Massive, long-lived pull requests are often such a headache. In this session we go through a move into a trunk-based development source-control workflow which eliminates the need for these “code avalanche” pull requests.</p>

  <p>After short introduction of trunk-based development and the fears people have for implementing it, we will go through concrete examples, lifted from a production system, of code, architecture, and workflows to tackle those fears when working on improving the development and deployment happiness of the team working on the application.</p>

  <p>After this talk the attendee are emboldened to start their own trunk-based development journey or excited to apply some of the techniques from the talk to their existing development processes and codebases.</p>
</blockquote>

<p>And video here:</p>

<iframe width="560" height="315" src="https://www.youtube.com/embed/M76dNDySrNc" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>

<p>And slides here:</p>

<iframe class="speakerdeck-iframe" frameborder="0" src="https://speakerdeck.com/player/50c830a995c9499cb67440d2faf06cb9" title="From massive pull requests to trunk-based development in Ruby" allowfullscreen="true" mozallowfullscreen="true" webkitallowfullscreen="true" style="border: 0px; background: padding-box padding-box rgba(0, 0, 0, 0.1); border-radius: 6px; width: 560px; height: 315px;" data-ratio="1.7777777777777777"></iframe>]]></content><author><name>Vesa Vänskä</name></author><category term="programming" /><category term="trunk-based-development" /><category term="process" /><summary type="html"><![CDATA[Two weeks I gave a talk at Euruko 2022 on trunk-based development with the full title of From massive pull requests to trunk-based development.]]></summary></entry><entry><title type="html">Automatic layout for VX Network with D3.js</title><link href="https://vesavanska.com/2018/automatic-layout-for-vx-network-with-d3" rel="alternate" type="text/html" title="Automatic layout for VX Network with D3.js" /><published>2018-09-26T00:00:00+00:00</published><updated>2018-09-26T00:00:00+00:00</updated><id>https://vesavanska.com/2018/automatic-layout-for-vx-network-with-d3</id><content type="html" xml:base="https://vesavanska.com/2018/automatic-layout-for-vx-network-with-d3"><![CDATA[<p>I’ve lately used <a href="https://vx-demo.now.sh/">vx</a> visualization components to implement different data visualizations. It provides small pieces you combine and customize to build your own custom visualizations. I’ve found it quite comprehensible and deeply customizable.</p>

<p>vx includes a <a href="https://vx-demo.now.sh/network">network graph component</a> for showing graph data which I needed for a project. Unfortunately it doesn’t automatically calculate the positions for the graph nodes, so I decided to use the <a href="https://github.com/d3/d3-force">D3.js force simulation</a> to calculate the X and Y coordinates for the nodes.</p>

<p><img src="/images/2018/vx-network-with-d3.png" alt="Screenshot of vx network graph" title="Screenshot of vx network graph" /></p>

<p>React component’s <code class="language-plaintext highlighter-rouge">componentDidMount</code> callback sets up d3 <code class="language-plaintext highlighter-rouge">forceSimulation</code> which takes care of setting the <code class="language-plaintext highlighter-rouge">x</code> and <code class="language-plaintext highlighter-rouge">y</code> properties for the passed nodes.</p>

<p>The full code for a simple network graph with automatic layout is below. You can also view how it looks and play around with it in <a href="https://codesandbox.io/s/rr8vkxy1rm">CodeSandbox</a>.</p>

<div class="language-jsx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="nx">React</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">react</span><span class="dl">"</span><span class="p">;</span>
<span class="k">import</span> <span class="nx">ReactDOM</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">react-dom</span><span class="dl">"</span><span class="p">;</span>

<span class="k">import</span> <span class="p">{</span> <span class="nx">Graph</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">@vx/network</span><span class="dl">"</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span>
  <span class="nx">forceCenter</span><span class="p">,</span>
  <span class="nx">forceLink</span><span class="p">,</span>
  <span class="nx">forceManyBody</span><span class="p">,</span>
  <span class="nx">forceSimulation</span>
<span class="p">}</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">d3-force</span><span class="dl">"</span><span class="p">;</span>

<span class="c1">// The node rendered by the graph</span>
<span class="kd">class</span> <span class="nx">NetworkNode</span> <span class="kd">extends</span> <span class="nx">React</span><span class="p">.</span><span class="nx">Component</span> <span class="p">{</span>
  <span class="nx">render</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">return</span> <span class="p">&lt;</span><span class="nt">circle</span> <span class="na">r</span><span class="p">=</span><span class="si">{</span><span class="mi">10</span><span class="si">}</span> <span class="na">fill</span><span class="p">=</span><span class="si">{</span><span class="dl">"</span><span class="s2">#9280FF</span><span class="dl">"</span><span class="si">}</span> <span class="p">/&gt;;</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="kd">class</span> <span class="nx">Network</span> <span class="kd">extends</span> <span class="nx">React</span><span class="p">.</span><span class="nx">Component</span> <span class="p">{</span>
  <span class="kd">constructor</span><span class="p">(</span><span class="nx">props</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">super</span><span class="p">(</span><span class="nx">props</span><span class="p">);</span>

    <span class="kd">const</span> <span class="nx">links</span> <span class="o">=</span> <span class="nx">props</span><span class="p">.</span><span class="nx">network</span><span class="p">.</span><span class="nx">links</span><span class="p">;</span>
    <span class="kd">const</span> <span class="nx">nodes</span> <span class="o">=</span> <span class="nx">props</span><span class="p">.</span><span class="nx">network</span><span class="p">.</span><span class="nx">nodes</span><span class="p">;</span>

    <span class="k">this</span><span class="p">.</span><span class="nx">state</span> <span class="o">=</span> <span class="p">{</span>
      <span class="na">data</span><span class="p">:</span> <span class="p">{</span>
        <span class="nx">nodes</span><span class="p">,</span>
        <span class="nx">links</span>
      <span class="p">}</span>
    <span class="p">};</span>
  <span class="p">}</span>

  <span class="c1">// Update force if the width or height of the graph changes</span>
  <span class="nx">componentDidUpdate</span><span class="p">(</span><span class="nx">newProps</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span>
      <span class="nx">newProps</span><span class="p">.</span><span class="nx">width</span> <span class="o">!==</span> <span class="k">this</span><span class="p">.</span><span class="nx">props</span><span class="p">.</span><span class="nx">width</span> <span class="o">||</span>
      <span class="nx">newProps</span><span class="p">.</span><span class="nx">height</span> <span class="o">!==</span> <span class="k">this</span><span class="p">.</span><span class="nx">props</span><span class="p">.</span><span class="nx">height</span>
    <span class="p">)</span> <span class="p">{</span>
      <span class="k">this</span><span class="p">.</span><span class="nx">force</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">force</span>
        <span class="p">.</span><span class="nx">force</span><span class="p">(</span><span class="dl">"</span><span class="s2">center</span><span class="dl">"</span><span class="p">,</span> <span class="nx">forceCenter</span><span class="p">(</span>
          <span class="nx">newProps</span><span class="p">.</span><span class="nx">width</span> <span class="o">/</span> <span class="mi">2</span><span class="p">,</span>
          <span class="nx">newProps</span><span class="p">.</span><span class="nx">height</span> <span class="o">/</span> <span class="mi">2</span>
        <span class="p">))</span>
        <span class="p">.</span><span class="nx">restart</span><span class="p">();</span>
    <span class="p">}</span>
  <span class="p">}</span>

  <span class="c1">// Setup D3 force</span>
  <span class="nx">componentDidMount</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">force</span> <span class="o">=</span> <span class="nx">forceSimulation</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">state</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">nodes</span><span class="p">)</span>
      <span class="p">.</span><span class="nx">force</span><span class="p">(</span>
        <span class="dl">"</span><span class="s2">link</span><span class="dl">"</span><span class="p">,</span>
        <span class="nx">forceLink</span><span class="p">()</span>
          <span class="p">.</span><span class="nx">id</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">d</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">return</span> <span class="nx">d</span><span class="p">.</span><span class="nx">id</span><span class="p">;</span>
          <span class="p">})</span>
          <span class="p">.</span><span class="nx">links</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">state</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">links</span><span class="p">)</span>
      <span class="p">)</span>
      <span class="p">.</span><span class="nx">force</span><span class="p">(</span><span class="dl">"</span><span class="s2">charge</span><span class="dl">"</span><span class="p">,</span> <span class="nx">forceManyBody</span><span class="p">().</span><span class="nx">strength</span><span class="p">(</span><span class="o">-</span><span class="mi">500</span><span class="p">))</span>
      <span class="p">.</span><span class="nx">force</span><span class="p">(</span>
        <span class="dl">"</span><span class="s2">center</span><span class="dl">"</span><span class="p">,</span>
        <span class="nx">forceCenter</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">props</span><span class="p">.</span><span class="nx">width</span> <span class="o">/</span> <span class="mi">2</span><span class="p">,</span> <span class="k">this</span><span class="p">.</span><span class="nx">props</span><span class="p">.</span><span class="nx">height</span> <span class="o">/</span> <span class="mi">2</span><span class="p">)</span>
      <span class="p">);</span>

    <span class="c1">// Force-update the component on each force tick</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">force</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="dl">"</span><span class="s2">tick</span><span class="dl">"</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="k">this</span><span class="p">.</span><span class="nx">forceUpdate</span><span class="p">());</span>
  <span class="p">}</span>

  <span class="nx">render</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="k">this</span><span class="p">.</span><span class="nx">force</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">return</span> <span class="kc">null</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">return</span> <span class="p">(</span>
      <span class="p">&lt;</span><span class="nt">div</span> <span class="na">style</span><span class="p">=</span><span class="si">{</span><span class="p">{</span> <span class="na">width</span><span class="p">:</span> <span class="dl">"</span><span class="s2">100%</span><span class="dl">"</span><span class="p">,</span> <span class="na">height</span><span class="p">:</span> <span class="dl">"</span><span class="s2">100%</span><span class="dl">"</span> <span class="p">}</span><span class="si">}</span><span class="p">&gt;</span>
        <span class="p">&lt;</span><span class="nt">svg</span> <span class="na">width</span><span class="p">=</span><span class="si">{</span><span class="k">this</span><span class="p">.</span><span class="nx">props</span><span class="p">.</span><span class="nx">width</span><span class="si">}</span> <span class="na">height</span><span class="p">=</span><span class="si">{</span><span class="k">this</span><span class="p">.</span><span class="nx">props</span><span class="p">.</span><span class="nx">height</span><span class="si">}</span><span class="p">&gt;</span>
          <span class="p">&lt;</span><span class="nt">rect</span>
            <span class="na">width</span><span class="p">=</span><span class="si">{</span><span class="k">this</span><span class="p">.</span><span class="nx">props</span><span class="p">.</span><span class="nx">width</span><span class="si">}</span>
            <span class="na">height</span><span class="p">=</span><span class="si">{</span><span class="k">this</span><span class="p">.</span><span class="nx">props</span><span class="p">.</span><span class="nx">height</span><span class="si">}</span>
            <span class="na">fill</span><span class="p">=</span><span class="s">"#f9fcff"</span>
          <span class="p">/&gt;</span>
          <span class="p">&lt;</span><span class="nc">Graph</span> <span class="na">graph</span><span class="p">=</span><span class="si">{</span><span class="k">this</span><span class="p">.</span><span class="nx">state</span><span class="p">.</span><span class="nx">data</span><span class="si">}</span> <span class="na">nodeComponent</span><span class="p">=</span><span class="si">{</span><span class="nx">NetworkNode</span><span class="si">}</span> <span class="p">/&gt;</span>
        <span class="p">&lt;/</span><span class="nt">svg</span><span class="p">&gt;</span>
      <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
    <span class="p">);</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="kd">function</span> <span class="nx">App</span><span class="p">()</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">nodes</span> <span class="o">=</span> <span class="p">[{</span> <span class="na">id</span><span class="p">:</span> <span class="mi">1</span> <span class="p">},</span> <span class="p">{</span> <span class="na">id</span><span class="p">:</span> <span class="mi">2</span> <span class="p">},</span> <span class="p">{</span> <span class="na">id</span><span class="p">:</span> <span class="mi">3</span> <span class="p">},</span> <span class="p">{</span> <span class="na">id</span><span class="p">:</span> <span class="mi">4</span> <span class="p">},</span> <span class="p">{</span> <span class="na">id</span><span class="p">:</span> <span class="mi">5</span> <span class="p">}];</span>
  <span class="kd">const</span> <span class="nx">links</span> <span class="o">=</span> <span class="p">[</span>
    <span class="p">{</span> <span class="na">source</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="na">target</span><span class="p">:</span> <span class="mi">2</span> <span class="p">},</span>
    <span class="p">{</span> <span class="na">source</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="na">target</span><span class="p">:</span> <span class="mi">3</span> <span class="p">},</span>
    <span class="p">{</span> <span class="na">source</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="na">target</span><span class="p">:</span> <span class="mi">4</span> <span class="p">},</span>
    <span class="p">{</span> <span class="na">source</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span> <span class="na">target</span><span class="p">:</span> <span class="mi">4</span> <span class="p">},</span>
    <span class="p">{</span> <span class="na">source</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span> <span class="na">target</span><span class="p">:</span> <span class="mi">4</span> <span class="p">},</span>
    <span class="p">{</span> <span class="na">source</span><span class="p">:</span> <span class="mi">4</span><span class="p">,</span> <span class="na">target</span><span class="p">:</span> <span class="mi">5</span> <span class="p">}</span>
  <span class="p">];</span>
  <span class="k">return</span> <span class="p">(</span>
    <span class="p">&lt;</span><span class="nt">div</span> <span class="na">className</span><span class="p">=</span><span class="s">"App"</span><span class="p">&gt;</span>
      <span class="p">&lt;</span><span class="nc">Network</span>
        <span class="na">width</span><span class="p">=</span><span class="si">{</span><span class="mi">400</span><span class="si">}</span>
        <span class="na">height</span><span class="p">=</span><span class="si">{</span><span class="mi">400</span><span class="si">}</span>
        <span class="na">network</span><span class="p">=</span><span class="si">{</span><span class="p">{</span>
          <span class="na">nodes</span><span class="p">:</span> <span class="nx">nodes</span><span class="p">,</span>
          <span class="na">links</span><span class="p">:</span> <span class="nx">links</span>
        <span class="p">}</span><span class="si">}</span>
      <span class="p">/&gt;</span>
    <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
  <span class="p">);</span>
<span class="p">}</span>

<span class="kd">const</span> <span class="nx">rootElement</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">"</span><span class="s2">root</span><span class="dl">"</span><span class="p">);</span>
<span class="nx">ReactDOM</span><span class="p">.</span><span class="nx">render</span><span class="p">(&lt;</span><span class="nc">App</span> <span class="p">/&gt;,</span> <span class="nx">rootElement</span><span class="p">);</span>
</code></pre></div></div>]]></content><author><name>Vesa Vänskä</name></author><category term="programming" /><category term="javascript" /><category term="vx" /><category term="d3" /><summary type="html"><![CDATA[I’ve lately used vx visualization components to implement different data visualizations. It provides small pieces you combine and customize to build your own custom visualizations. I’ve found it quite comprehensible and deeply customizable.]]></summary></entry><entry><title type="html">How to set Bundler to fetch Github gems using HTTPS</title><link href="https://vesavanska.com/2017/how-to-set-bundler-to-fetch-github-gems-using-https" rel="alternate" type="text/html" title="How to set Bundler to fetch Github gems using HTTPS" /><published>2017-01-22T00:00:00+00:00</published><updated>2017-01-22T00:00:00+00:00</updated><id>https://vesavanska.com/2017/how-to-set-bundler-to-fetch-github-gems-using-https</id><content type="html" xml:base="https://vesavanska.com/2017/how-to-set-bundler-to-fetch-github-gems-using-https"><![CDATA[<p>Fetching Github gems using HTTPS instead of git protocol is going to be the new default when Bundler 2.0 is released. Here are the instructions on how to do it locally and on <a href="https://www.heroku.com/">Heroku</a> and <a href="https://travis-ci.com/">Travis CI</a> before Bundler 2.0 is released.</p>

<p>Configure Bundler to use HTTPS for GitHub sources:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bundle config <span class="nt">--global</span> github.https <span class="nb">true</span>
</code></pre></div></div>

<p>And on Heroku (note the double underscore):</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>heroku config:set <span class="nv">BUNDLE_GITHUB__HTTPS</span><span class="o">=</span><span class="nb">true</span>
</code></pre></div></div>

<p>And on Travis (put it to your <code class="language-plaintext highlighter-rouge">.travis.yml</code> file):</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">before_install</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="s2">"</span><span class="s">bundle</span><span class="nv"> </span><span class="s">config</span><span class="nv"> </span><span class="s">--global</span><span class="nv"> </span><span class="s">github.https</span><span class="nv"> </span><span class="s">true"</span>
</code></pre></div></div>]]></content><author><name>Vesa Vänskä</name></author><category term="ruby" /><category term="bundler" /><category term="heroku" /><category term="travis" /><summary type="html"><![CDATA[Fetching Github gems using HTTPS instead of git protocol is going to be the new default when Bundler 2.0 is released. Here are the instructions on how to do it locally and on Heroku and Travis CI before Bundler 2.0 is released.]]></summary></entry><entry><title type="html">Year in books 2015 &amp;amp; 2016</title><link href="https://vesavanska.com/2017/year-in-books-2015-2016" rel="alternate" type="text/html" title="Year in books 2015 &amp;amp; 2016" /><published>2017-01-15T00:00:00+00:00</published><updated>2017-01-15T00:00:00+00:00</updated><id>https://vesavanska.com/2017/year-in-books-2015-2016</id><content type="html" xml:base="https://vesavanska.com/2017/year-in-books-2015-2016"><![CDATA[<p>Last year I missed the posting of the yearly reading update so this post covers both 2015 and 2016.</p>

<p>I managed to hit my reading goals and they were also good reading years in quality.</p>

<p>In 2015 I read 82 books (24 of them comics) and in 2016 read 65 books (20 of them comics). In 2016 I also started doing lot more re-reading of previously read books or parts of them.</p>

<h2 id="books-with-5-stars">Books with 5 stars</h2>

<p>I’m not a literary critic and don’t know which books are good or bad so I give my stars according to my own feeling on how much I got out of the book and how big of an impact I estimate it having on my life.</p>

<p>Here’s the list:</p>

<ul>
  <li><a href="http://www.soundstrue.com/store/leap-first-1.html">Leap First: Creating Work That Matters</a> by Seth Godin</li>
  <li><a href="http://amzn.to/2iuOhXA">Moominpappa at Sea</a> by Tove Jansson</li>
  <li><a href="https://verkkokauppa.duodecim.fi/6361.html">Joustava mieli - Vapaudu stressin, uupumuksen ja masennuksen yliotteesta</a> by Arto Pietikäinen <em>in Finnish</em></li>
  <li><a href="http://amzn.to/1WBz315">The Road Less Traveled: A New Psychology of Love, Traditional Values and Spiritual Growth</a> by M. Scott Peck</li>
  <li><a href="http://amzn.to/2iAgQWR">Shoe Dog: A Memoir by the Creator of NIKE</a> by Phil Knight</li>
  <li><a href="http://amzn.to/2jm2lXR">Siddhartha</a> by Hermann Hesse</li>
</ul>

<hr />

<p>You can view all the books I read in my Goodreads profile <a href="https://www.goodreads.com/user/year_in_books/2015/3603997">for 2015</a> and <a href="https://www.goodreads.com/user/year_in_books/2016/3603997">for 2016</a>.</p>

<p>Check out also previous Year in books posts for <a href="http://vesavanska.com/2014/reading-list-2013/">2013</a> and <a href="http://vesavanska.com/2015/year-in-books-2014/">2014</a>.</p>

<p>You can follow the books I read on <a href="https://www.goodreads.com/user/show/3603997-vesa-vanska">Goodreads</a>. It gets updated when I get reading done. I’m also interested in following people with similar taste.</p>]]></content><author><name>Vesa Vänskä</name></author><category term="reading" /><category term="books" /><summary type="html"><![CDATA[Last year I missed the posting of the yearly reading update so this post covers both 2015 and 2016.]]></summary></entry><entry><title type="html">Year in books 2014</title><link href="https://vesavanska.com/2015/year-in-books-2014" rel="alternate" type="text/html" title="Year in books 2014" /><published>2015-01-17T00:00:00+00:00</published><updated>2015-01-17T00:00:00+00:00</updated><id>https://vesavanska.com/2015/year-in-books-2014</id><content type="html" xml:base="https://vesavanska.com/2015/year-in-books-2014"><![CDATA[<p>I enjoyed a bunch of books in 2014 (46 in total). My selection process for picking books to read is rather random but this year I could lift a few themes. I feature my favorite books of the year in this post.</p>

<p><strong>Bird by Bird: Some Instructions on Writing and Life</strong> by Anne Lamott</p>

<p><a href="http://www.amazon.com/gp/product/B000SEGI8Q/ref=as_li_tl?ie=UTF8&amp;camp=1789&amp;creative=390957&amp;creativeASIN=B000SEGI8Q&amp;linkCode=as2&amp;tag=vesan-20&amp;linkId=NME7ZC3CR636ANCN"><img src="/images/2015/bird-by-bird.jpg" alt="Bird by Bird" /></a></p>

<p>This is a book about writing and writer’s life. Even though I don’t write that much I could find plenty of good advice. Encouraging and inspiring (even though some people might find the honesty off-putting) read that also reminded me of the importance of the shitty first draft.</p>

<p><strong>The Obstacle Is the Way: The Timeless Art of Turning Trials into Triumph</strong> by Ryan Holiday</p>

<p><a href="http://www.amazon.com/gp/product/B00G3L1B8K/ref=as_li_tl?ie=UTF8&amp;camp=1789&amp;creative=390957&amp;creativeASIN=B00G3L1B8K&amp;linkCode=as2&amp;tag=vesan-20&amp;linkId=XAYRCQFDQM2W4YZA"><img src="/images/2015/obstacle-is-the-way.png" alt="The Obstacle Is the Way" /></a></p>

<p><strong>Meditations</strong> by Marcus Aurelius</p>

<p><a href="http://www.amazon.com/gp/product/0679642609/ref=as_li_tl?ie=UTF8&amp;camp=1789&amp;creative=390957&amp;creativeASIN=0679642609&amp;linkCode=as2&amp;tag=vesan-20&amp;linkId=PDH2TGOWPBGOBBC4"><img src="/images/2015/meditations.jpg" alt="Meditations" /></a></p>

<p>I’ve been interested about Stoicism and read two books related to it. Great advice for the rough times and one-liners to spice up your day. I already bought couple other Stoic books so will read more during 2015.</p>

<p><strong>Foundation and Empire</strong> &amp; <strong>Second Foundation</strong> by Isaac Asimov</p>

<p><a href="http://www.amazon.com/gp/product/0553293370/ref=as_li_tl?ie=UTF8&amp;camp=1789&amp;creative=390957&amp;creativeASIN=0553293370&amp;linkCode=as2&amp;tag=vesan-20&amp;linkId=HYKLACSVWFQ55JOB"><img src="/images/2015/foundation-and-empire.jpg" alt="Foundation and Empire" /></a></p>

<p><a href="http://www.amazon.com/gp/product/B000FC1PWU/ref=as_li_tl?ie=UTF8&amp;camp=1789&amp;creative=390957&amp;creativeASIN=B000FC1PWU&amp;linkCode=as2&amp;tag=vesan-20&amp;linkId=YQ2RLZHNSLHTLATP"><img src="/images/2015/second-foundation.jpg" alt="Second Foundation" /></a></p>

<p>On the fiction front I read the second and third installment of the science fiction series Foundation. I enjoy the dry writing style of Asimov. The books are low in action and very dialogue heavy so not for everyone. I love the plots and uncovering the mysteries of psychohistory. I will continue reading the series in 2015.</p>

<p><strong>World War Z</strong> by Max Brooks</p>

<p><a href="http://www.amazon.com/gp/product/B004SSUPMS/ref=as_li_tl?ie=UTF8&amp;camp=1789&amp;creative=390957&amp;creativeASIN=B004SSUPMS&amp;linkCode=as2&amp;tag=vesan-20&amp;linkId=6Z36VBHPOAJS37KC"><img src="/images/2015/world-war-z.jpg" alt="World War Z" /></a></p>

<p>Another fiction pick is World War Z. It is a fictional collection of stories about lives of people after the Zombie war. The stories encompass various nationalities in different life situations told as dialogues with the interviewer of the book. Stories blend together social, economic, religious, political, and environmental themes and there is a clear critique and commentary on the current state of the world.</p>

<p>After reading the book I returned back to the stories in my head and marvelled on all the view points the book included. Event if you don’t like zombies, I would recommend it. It’s sometimes categorized as horror but I wouldn’t label it as such.</p>

<p><strong>Functional JavaScript: Introducing Functional Programming with Underscore.js</strong> by Michal Fogus</p>

<p><a href="http://www.amazon.com/gp/product/B00D624AQO/ref=as_li_tl?ie=UTF8&amp;camp=1789&amp;creative=390957&amp;creativeASIN=B00D624AQO&amp;linkCode=as2&amp;tag=vesan-20&amp;linkId=OODEQL6CYFGU4OKD"><img src="/images/2015/functional-javascript.png" alt="Functional JavaScript" /></a></p>

<p>I didn’t read that many technical books this year but from the ones I did, this was my favorite. I’ve being dabbling with Clojure and this book linked many of those functional programming ideas to JavaScript.</p>

<p><strong>Waking Up: A Guide to Spirituality Without Religion</strong> by Sam Harris</p>

<p><a href="http://www.amazon.com/gp/product/B00GEEB9YC/ref=as_li_tl?ie=UTF8&amp;camp=1789&amp;creative=390957&amp;creativeASIN=B00GEEB9YC&amp;linkCode=as2&amp;tag=vesan-20&amp;linkId=TUVVMUHBZMFHEHQC"><img src="/images/2015/waking-up.jpg" alt="Waking Up" /></a></p>

<p><strong>Still the Mind</strong> by Alan Watts</p>

<p><a href="http://www.amazon.com/gp/product/B003CIQ56U/ref=as_li_tl?ie=UTF8&amp;camp=1789&amp;creative=390957&amp;creativeASIN=B003CIQ56U&amp;linkCode=as2&amp;tag=vesan-20&amp;linkId=WO466PTTJLB32VDV"><img src="/images/2015/still-the-mind.jpg" alt="Still the Mind" /></a></p>

<p>On the spirituality front these books were my favorites.</p>

<p><strong>The Innovators: How a Group of Hackers, Geniuses, and Geeks Created the Digital Revolution</strong> by Walter Isaacson</p>

<p><a href="http://www.amazon.com/gp/product/B00JGAS65Q/ref=as_li_tl?ie=UTF8&amp;camp=1789&amp;creative=390957&amp;creativeASIN=B00JGAS65Q&amp;linkCode=as2&amp;tag=vesan-20&amp;linkId=BQVJ6GSY5C7PBBFV"><img src="/images/2015/innovators.jpg" alt="The Innovators" /></a></p>

<p>This is the book I have talked about the most this year, so I guess that makes it my favorite. It tells the stories of people and teams that have invented the ideas and built the technologies we rely on in today’s digital world: programming, microprocessor, personal computer, software, internet and so on. It also observes what are the commonalities of the innovators and tries to lift themes from their stories. While reading the book I also read some of the papers written by the early pioneers. Reading papers written in the 60’s and 70’s you see how so many parts of the technology puzzle took decades to go from idea to wide usage.</p>

<p>I also found it interesting that when the first interactive computers were invented, the first user interface designers were psychologists. The teams building these interactive machines thought of it as a man-machine symbiosis so it only made sense to put engineers, the experts of the machine, and psychologists, the experts of the human, to the same team to work on the machines.</p>

<p>Here is a trailer of the book:</p>

<iframe width="637" height="389" src="//www.youtube.com/embed/xnE5oZEEQeU" frameborder="0" allowfullscreen=""></iframe>

<hr />

<p>You can view all the books I reading in 2014 on <a href="https://www.goodreads.com/review/list/3603997-vesa-v-nsk?read_at=2014&amp;view=covers">my GoodReads profile</a>. I wrote similar post also a year ago so you can see <a href="http://vesavanska.com/2014/reading-list-2013/">what I enjoyed in 2013</a>.</p>

<p>My plan of writing notes or mini-reviews on the books I read failed miserably so I’m doing it properly this in 2015. See you in a year!</p>]]></content><author><name>Vesa Vänskä</name></author><category term="reading" /><category term="books" /><summary type="html"><![CDATA[I enjoyed a bunch of books in 2014 (46 in total). My selection process for picking books to read is rather random but this year I could lift a few themes. I feature my favorite books of the year in this post.]]></summary></entry><entry><title type="html">ActiveRecord migrations best practices</title><link href="https://vesavanska.com/2014/activerecord-migrations-best-practices" rel="alternate" type="text/html" title="ActiveRecord migrations best practices" /><published>2014-11-02T00:00:00+00:00</published><updated>2014-11-02T00:00:00+00:00</updated><id>https://vesavanska.com/2014/activerecord-migrations-best-practices</id><content type="html" xml:base="https://vesavanska.com/2014/activerecord-migrations-best-practices"><![CDATA[<p>There are certain guidelines I follow when writing migrations using the ActiveRecord migrations library. The guidelines have formed during the years I’ve been working with Ruby on Rails.</p>

<p>Here is an example migration class that follows all the guidelines reviewed in this post:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">AddAreaToPlots</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Migration</span>
  <span class="k">class</span> <span class="nc">Plot</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span><span class="p">;</span> <span class="k">end</span>

  <span class="k">def</span> <span class="nf">up</span>
    <span class="n">add_column</span> <span class="ss">:plots</span><span class="p">,</span> <span class="ss">:area</span><span class="p">,</span> <span class="ss">:float</span>

    <span class="no">Plot</span><span class="p">.</span><span class="nf">find_each</span> <span class="k">do</span> <span class="o">|</span><span class="n">plot</span><span class="o">|</span>
      <span class="n">plot</span><span class="p">.</span><span class="nf">area</span> <span class="o">=</span> <span class="n">plot</span><span class="p">.</span><span class="nf">width</span> <span class="o">*</span> <span class="n">plot</span><span class="p">.</span><span class="nf">height</span>
      <span class="n">plot</span><span class="p">.</span><span class="nf">save!</span>
    <span class="k">end</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">down</span>
    <span class="n">remove_column</span> <span class="ss">:plots</span><span class="p">,</span> <span class="ss">:area</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<h2 id="redefine-activerecord-classes">Redefine ActiveRecord classes</h2>

<p>If possible, ActiveRecord models should not be used in migrations. Migrations should be simple and failproof transformations of database schema and data. Using complex model objects that complicate the migration should be avoided.</p>

<p>If you have strong SQL skills, you can use it for data transformations. Well written SQL is often faster than using ActiveRecord. For schema changes I would use the ActiveRecord methods which are readable and give extra features provided by the framework (most notably the migration/rollback support talked later in this post).</p>

<p>If you want to use the familiar ActiveRecord API to do the data transformations, the ActiveRecord classes should be redefined. As shown in the example, the <code class="language-plaintext highlighter-rouge">Plot</code> class has been redefined inside the migration class. The redefinition overrides the model class from the application code and makes it a bare ActiveRecord class. This means it has no validations, associations or callbacks. If some of the callbacks should be called in the migration, those can be reimplemented in the redefined class. Same goes for methods in the model class.</p>

<p>Why go through this trouble? When the application changes, the models change with it. It’s possible that the models change in a way that break the migrations. Or the models can be renamed or removed which will also break the migrations.</p>

<p>It might feel weird but it just works, makes your migrations more robust, and protects you from trouble in the future.</p>

<h2 id="use-find_each">Use find_each</h2>

<p>When modifying records, <code class="language-plaintext highlighter-rouge">Model.find_each</code> should be used to fetch the records in batches. If <code class="language-plaintext highlighter-rouge">Model.all</code> is used on a big database table, ActiveRecord will instantiate objects for all rows in the table which will consume all the available memory and the process starts swapping memory like crazy slowing the migration.</p>

<p>By default <code class="language-plaintext highlighter-rouge">find_each</code> loads the records in batches of 1000 but that can be configured. Only drawback of <code class="language-plaintext highlighter-rouge">find_each</code> is that you can’t use <code class="language-plaintext highlighter-rouge">order</code>, <code class="language-plaintext highlighter-rouge">offset</code> or <code class="language-plaintext highlighter-rouge">limit</code>. This is because <code class="language-plaintext highlighter-rouge">find_each</code> orders the records by id and then uses offset and limit to control the loading of the batches. Other than these caveats, there are no drawbacks on using <code class="language-plaintext highlighter-rouge">find_each</code>. Filtering the records using <code class="language-plaintext highlighter-rouge">where</code> works just fine.</p>

<h2 id="dont-change-the-data-using-the-change-method">Don’t change the data using the change method</h2>

<p>The <code class="language-plaintext highlighter-rouge">change</code> method for migration classes was introduced in Rails 3.1 to make the writing of migrations simpler. Using <code class="language-plaintext highlighter-rouge">change</code> and methods that support inversing themselves, you can write migrations like this:</p>

<p>class AddAreaToPlots &lt; ActiveRecord::Migration
  def change
    add_column :plots, :area, :float
  end
end</p>

<p>When the migration are run by ActiveRecord, it executes the “up” version of the command adding the area column. On rollback it runs the code and executes the “down” version of the command for all the methods that have it. In the case of adding a column, rollback would remove it.</p>

<p>If you modify your data within the <code class="language-plaintext highlighter-rouge">change</code> method, ActiveRecord will run that code on both migration and rollback which is probably not what is wanted. Here is an example:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">AddAreaToPlots</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Migration</span>
  <span class="k">class</span> <span class="nc">Plot</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span><span class="p">;</span> <span class="k">end</span>

  <span class="k">def</span> <span class="nf">change</span>
    <span class="n">add_column</span> <span class="ss">:plots</span><span class="p">,</span> <span class="ss">:area</span><span class="p">,</span> <span class="ss">:float</span>

    <span class="no">Plot</span><span class="p">.</span><span class="nf">find_each</span> <span class="k">do</span> <span class="o">|</span><span class="n">plot</span><span class="o">|</span>
      <span class="n">plot</span><span class="p">.</span><span class="nf">area</span> <span class="o">=</span> <span class="n">plot</span><span class="p">.</span><span class="nf">width</span> <span class="o">*</span> <span class="n">plot</span><span class="p">.</span><span class="nf">height</span>
      <span class="n">plot</span><span class="p">.</span><span class="nf">save!</span>
    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>On rollback the migration would first remove the column and then try to populate the column which would of course fail due to the missing column breaking the rollback.</p>

<hr />

<p>In addition to the following these guideline it doesn’t hurt to understand how the ActiveRecord migrations work. Read the <a href="http://guides.rubyonrails.org/migrations.html">official Active Record Migrations guide</a> to get more information.</p>

<p>Do you have any best practices you follow to make your migrations better? I’d be interested hear about those in the comments.</p>

<h2 id="addition-migrations-should-not-contain-seed-data">Addition: Migrations should not contain seed data</h2>

<p>Seed data refers to the initial data the database must have for the application to function correctly at all. Rails provides a [mechanism for populating the database with seed data] which should be used. Migrations are meant only to provide the structure of the database, not the contents.</p>

<p>If migrations are used for seed data, there is a chance that a database is created without the seed data. When the incorrect migrations get older and a new database is bootstrapped using <code class="language-plaintext highlighter-rouge">rake db:schema:load</code> it uses the <code class="language-plaintext highlighter-rouge">db/schema.rb</code> file to get the right structure, but the data from the migrations won’t be created in the database.</p>

<p>Thanks for <a href="https://github.com/christianhellsten">Christian Hellsten</a> for the addition!</p>]]></content><author><name>Vesa Vänskä</name></author><category term="ruby" /><category term="rails" /><category term="programming" /><category term="activerecord" /><category term="database" /><summary type="html"><![CDATA[There are certain guidelines I follow when writing migrations using the ActiveRecord migrations library. The guidelines have formed during the years I’ve been working with Ruby on Rails.]]></summary></entry><entry><title type="html">Keep track of test suite run duration with Travis CI</title><link href="https://vesavanska.com/2014/keep-track-of-test-suite-run-duration-with-travis-ci" rel="alternate" type="text/html" title="Keep track of test suite run duration with Travis CI" /><published>2014-08-31T00:00:00+00:00</published><updated>2014-08-31T00:00:00+00:00</updated><id>https://vesavanska.com/2014/keep-track-of-test-suite-run-duration-with-travis-ci</id><content type="html" xml:base="https://vesavanska.com/2014/keep-track-of-test-suite-run-duration-with-travis-ci"><![CDATA[<p>I wanted to view how the test suite run duration has been changing over time on a few projects. The tests are run using the pro version of Travis CI. <a href="https://travis-ci.org/">Travis CI</a> is a hosted continuous integration platform which provides easy to configure way to get a continuous integration server for your projects. Luckily Travis also has an API that exposes the test build information.</p>

<h2 id="so-why-should-you-be-interested-about-test-duration">So why should you be interested about test duration?</h2>

<p>Test duration is important because if running your tests takes too much time it builds friction and you run the tests less often. When you don’t run your tests regressions get committed to version control which slows down your other team members. Also if you do test-driven development (TDD) the feedback loop provided by the tests gets too slow to be useful.</p>

<p>If you do the test suite duration comparison often enough you can pinpoint tests that slow the suite down disproportionately to the benefit the added test provides. When you know certain test is especially slow you can think of ways to improving the speed of the test or find a way to test the functionality with another, faster way.</p>

<h2 id="pulling-test-suite-durations-from-travis">Pulling test suite durations from Travis</h2>

<p>Fetching Travis data is easy using Ruby and the <a href="https://github.com/travis-ci/travis.rb">official Travis Ruby library</a>. These instructions are for the pro version of Travis which is meant for private repositories. You can also use the same instructions for the open source version of Travis but you need to change the commands and library usage to remove the references to pro.</p>

<p>First install the Travis RubyGem:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gem <span class="nb">install </span>travis
</code></pre></div></div>

<p>Then login to Travis using it:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>travis login <span class="nt">--pro</span> <span class="nt">--auto</span>
</code></pre></div></div>

<p>Here is the Ruby file for fetching the data:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">require</span> <span class="s1">'travis/pro/auto_login'</span>

<span class="n">repo_name</span> <span class="o">=</span> <span class="s2">"your-organization/your-project"</span>

<span class="n">builds</span> <span class="o">=</span> <span class="no">Travis</span><span class="o">::</span><span class="no">Pro</span><span class="p">.</span><span class="nf">repos</span><span class="p">.</span><span class="nf">find</span><span class="p">(</span><span class="n">repo_name</span><span class="p">).</span><span class="nf">first</span><span class="p">.</span><span class="nf">builds</span>
<span class="n">finished_builds</span> <span class="o">=</span> <span class="n">builds</span><span class="p">.</span><span class="nf">select</span><span class="p">(</span><span class="o">&amp;</span><span class="ss">:finished?</span><span class="p">)</span>
<span class="n">durations</span> <span class="o">=</span> <span class="n">finished_builds</span><span class="p">.</span><span class="nf">map</span> <span class="p">{</span><span class="o">|</span><span class="n">build</span><span class="o">|</span>
  <span class="p">[</span><span class="n">build</span><span class="p">.</span><span class="nf">started_at</span><span class="p">,</span> <span class="n">build</span><span class="p">.</span><span class="nf">duration</span><span class="p">,</span> <span class="n">build</span><span class="p">.</span><span class="nf">commit</span><span class="p">.</span><span class="nf">sha</span><span class="p">]</span>
<span class="p">}</span>
<span class="no">File</span><span class="p">.</span><span class="nf">open</span><span class="p">(</span><span class="s2">"stats.txt"</span><span class="p">,</span> <span class="s2">"w"</span><span class="p">)</span> <span class="p">{</span><span class="o">|</span><span class="n">f</span><span class="o">|</span>
  <span class="n">durations</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">duration</span><span class="o">|</span>
    <span class="n">f</span><span class="p">.</span><span class="nf">puts</span> <span class="n">duration</span><span class="p">.</span><span class="nf">join</span><span class="p">(</span><span class="s2">"</span><span class="se">\t</span><span class="s2">"</span><span class="p">)</span>
  <span class="k">end</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The program fetches all finished builds for a repository, gets the durations and writes those to a file stats.txt separated by tabs.</p>

<p>To make the data a bit more readable for large projects, lets skip some data points to get to less than 100:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">require</span> <span class="s1">'travis/pro/auto_login'</span>

<span class="n">repo_name</span> <span class="o">=</span> <span class="s2">"your-organization/your-project"</span>

<span class="n">builds</span> <span class="o">=</span> <span class="no">Travis</span><span class="o">::</span><span class="no">Pro</span><span class="p">.</span><span class="nf">repos</span><span class="p">.</span><span class="nf">find</span><span class="p">(</span><span class="n">repo_name</span><span class="p">).</span><span class="nf">first</span><span class="p">.</span><span class="nf">builds</span>

<span class="n">finished_builds</span> <span class="o">=</span> <span class="n">builds</span><span class="p">.</span><span class="nf">select</span><span class="p">(</span><span class="o">&amp;</span><span class="ss">:finished?</span><span class="p">)</span>
<span class="n">every_n_build</span> <span class="o">=</span> <span class="p">(</span><span class="n">finished_builds</span><span class="p">.</span><span class="nf">size</span> <span class="o">/</span> <span class="mi">100</span><span class="p">.</span><span class="nf">to_f</span><span class="p">).</span><span class="nf">ceil</span>

<span class="n">smaller_dataset</span> <span class="o">=</span> <span class="n">finished_builds</span><span class="p">.</span><span class="nf">each_slice</span><span class="p">(</span><span class="n">every_n_build</span><span class="p">).</span><span class="nf">map</span><span class="p">(</span><span class="o">&amp;</span><span class="ss">:first</span><span class="p">)</span>
<span class="n">durations</span> <span class="o">=</span> <span class="n">smaller_dataset</span><span class="p">.</span><span class="nf">map</span> <span class="p">{</span><span class="o">|</span><span class="n">build</span><span class="o">|</span>
  <span class="p">[</span><span class="n">build</span><span class="p">.</span><span class="nf">started_at</span><span class="p">,</span> <span class="n">build</span><span class="p">.</span><span class="nf">duration</span><span class="p">,</span> <span class="n">build</span><span class="p">.</span><span class="nf">commit</span><span class="p">.</span><span class="nf">sha</span><span class="p">]</span>
<span class="p">}</span>

<span class="no">File</span><span class="p">.</span><span class="nf">open</span><span class="p">(</span><span class="s2">"stats.txt"</span><span class="p">,</span> <span class="s2">"w"</span><span class="p">)</span> <span class="p">{</span><span class="o">|</span><span class="n">f</span><span class="o">|</span>
  <span class="n">durations</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">duration</span><span class="o">|</span>
    <span class="n">f</span><span class="p">.</span><span class="nf">puts</span> <span class="n">duration</span><span class="p">.</span><span class="nf">join</span><span class="p">(</span><span class="s2">"</span><span class="se">\t</span><span class="s2">"</span><span class="p">)</span>
  <span class="k">end</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Now you can use your favorite charting tool to get something visual.</p>

<p>Here is a screenshot of a data from one of my apps created using Numbers.app. It was created by opening the file in Numbers.app, selecting the data and selecting bar chart from the Chart menu.</p>

<p><a href="/images/2014/travis-ci-statistics.png"><img src="/images/2014/travis-ci-statistics.png" alt="Travis CI build statistics" /></a></p>]]></content><author><name>Vesa Vänskä</name></author><category term="testing" /><category term="programming" /><category term="travis" /><summary type="html"><![CDATA[I wanted to view how the test suite run duration has been changing over time on a few projects. The tests are run using the pro version of Travis CI. Travis CI is a hosted continuous integration platform which provides easy to configure way to get a continuous integration server for your projects. Luckily Travis also has an API that exposes the test build information.]]></summary></entry><entry><title type="html">Guest at the Frontend Friday podcast</title><link href="https://vesavanska.com/2014/guest-at-frontend-friday-podcast" rel="alternate" type="text/html" title="Guest at the Frontend Friday podcast" /><published>2014-03-30T00:00:00+00:00</published><updated>2014-03-30T00:00:00+00:00</updated><id>https://vesavanska.com/2014/guest-at-frontend-friday-podcast</id><content type="html" xml:base="https://vesavanska.com/2014/guest-at-frontend-friday-podcast"><![CDATA[<p>If you understand Finnish, you can enjoy an episode of the Frontend Friday podcast with the theme of Hypermedia APIs. I was happy to be invited to discuss about what Hypermedia APIs are, why I find them interesting, how would you start implementing them and other Hypermedia API related topics.</p>

<p>You can download the episode and view the shownotes at <a href="http://ouluweb.github.io/podcast/2014/03/16/Episodi-10.html">the Frontend Friday website</a>.</p>]]></content><author><name>Vesa Vänskä</name></author><category term="hypermedia apis" /><category term="podcast" /><category term="programming" /><category term="apis" /><summary type="html"><![CDATA[If you understand Finnish, you can enjoy an episode of the Frontend Friday podcast with the theme of Hypermedia APIs. I was happy to be invited to discuss about what Hypermedia APIs are, why I find them interesting, how would you start implementing them and other Hypermedia API related topics.]]></summary></entry><entry><title type="html">Exercise is a canary of a coal mine for the body</title><link href="https://vesavanska.com/2014/exercise-is-a-canary-in-a-colemine-for-the-body" rel="alternate" type="text/html" title="Exercise is a canary of a coal mine for the body" /><published>2014-01-06T00:00:00+00:00</published><updated>2014-01-06T00:00:00+00:00</updated><id>https://vesavanska.com/2014/exercise-is-a-canary-in-a-colemine-for-the-body</id><content type="html" xml:base="https://vesavanska.com/2014/exercise-is-a-canary-in-a-colemine-for-the-body"><![CDATA[<p>I’ve noticed that for me exercise works as a canary of a coal mine for the body. When the body is getting overstressed, it is first noticeable during exercise.</p>

<p>Things like flu, small aches and stiffness in the body are normal in human life. Urban life and stationary jobs strain the body. Usually these smaller things pass on their own and you don’t pay too much attention on them. But sometimes those are a signal of something bigger. A disease that needs treatment or overstress that you should react to.</p>

<p>For me exercise works great on separating the signals that are meaningful and need to be reacted to. Exercise stresses the body in different way than home and work life. I do CrossFit exercise twice a week and have been doing for three years. If for many workouts in succession I don’t feel normal, it is usually a sign that it is not the normal everyday things going on. I notice it from being out of breath easily and muscle pain after workouts being greater and taking longer to pass.</p>

<p>I also follow my workout results (using our own <a href="https://www.wodconnect.com">workout journal app WODconnect</a>). My memory for results is horrible so having a place for the results that makes them comparable over time is very valuable. Sometimes not being able to get the results I have been previously been capable of is also a sign of something more severe (sometimes it is just being tired from previous workouts or something else).</p>

<p>Listening to your body is never stupid but it is especially important when exercising.</p>]]></content><author><name>Vesa Vänskä</name></author><category term="excercise" /><summary type="html"><![CDATA[I’ve noticed that for me exercise works as a canary of a coal mine for the body. When the body is getting overstressed, it is first noticeable during exercise.]]></summary></entry><entry><title type="html">Reading list 2013</title><link href="https://vesavanska.com/2014/reading-list-2013" rel="alternate" type="text/html" title="Reading list 2013" /><published>2014-01-05T00:00:00+00:00</published><updated>2014-01-05T00:00:00+00:00</updated><id>https://vesavanska.com/2014/reading-list-2013</id><content type="html" xml:base="https://vesavanska.com/2014/reading-list-2013"><![CDATA[<p>2013 was a good reading year for me and I thought I’d share the books that influenced me the most during the year.</p>

<p>Overall I read 55 books (of which 10 were comic books). I use <a href="https://www.goodreads.com/">GoodReads</a> to track my reading so you can view <a href="https://www.goodreads.com/review/list/3603997-vesa-v-nsk?read_at=2013&amp;view=table">my full reading list there</a> and even follow my “reviews” if you are really interested.</p>

<h2 id="the-books">The books</h2>

<p><strong>So Good They Can’t Ignore You: Why Skills Trump Passion in the Quest for Work You Love</strong> by Cal Newport</p>

<p><a href="http://www.amazon.com/gp/product/1455509124/ref=as_li_ss_tl?ie=UTF8&amp;camp=1789&amp;creative=390957&amp;creativeASIN=1455509124&amp;linkCode=as2&amp;tag=vesan-20"><img src="/images/2014/so-good-they-cant-ignore-you.jpg" alt="So Good They Can't Ignore You" /></a></p>

<p>The title sounds rather self-helpish but reading the author’s <a href="http://calnewport.com/blog/">Study Hacks</a> blog made me interested. Newport has been studying remarkable careers and the book formulates rules based on the that.</p>

<p>Perhaps the most controversial bit is the idea that you should not follow your passion but start learning valuable skills. Newport says that passion is dangerous because having passion is so rare that it being a precursor for a great career is depressing and confusing. After you have advanced skills and knowledge on an area the passion will come automatically. Waiting for the passion to appear is not an effective strategy. Working right trumps finding the right work.</p>

<p>Book offers a models for improving your skills, steering your work choices to maximize control, and on formulating an underlying mission for your career. I had bunch of light bulb moments with this book and would recommend it to anyone pondering where she is going next with her work life.</p>

<p><strong>Buddhism Plain and Simple</strong> by Steve Hagen</p>

<p><a href="http://www.amazon.com/gp/product/0767903323/ref=as_li_ss_tl?ie=UTF8&amp;camp=1789&amp;creative=390957&amp;creativeASIN=0767903323&amp;linkCode=as2&amp;tag=vesan-20"><img src="/images/2014/buddhism_plain_and_simple.jpg" alt="Buddhism Plain and Simple" /></a></p>

<p>I’ve been reading some Buddhist material during the past few years and I found this book to be great summary of many Buddhist ideas. It is stripped from any extra layers and presents the ideas in a clear way. I would recommend it to people who are interested in Buddhism on all knowledge levels of the subject.</p>

<p><strong>Surely You’re Joking, Mr. Feynman!</strong> by Richard P. Feynman</p>

<p><a href="http://www.amazon.com/gp/product/B003V1WXKU/ref=as_li_ss_tl?ie=UTF8&amp;camp=1789&amp;creative=390957&amp;creativeASIN=B003V1WXKU&amp;linkCode=as2&amp;tag=vesan-20"><img src="/images/2014/surely_you_are_joking.jpg" alt="Surely You're Joking, Mr. Feynman!" /></a></p>

<p>Great stories from one of my favorite scientist. I’ve watched videos and read stories about Feynman earlier but this was the first book I read about him. I also read the second book What Do You Care What Other People Think? but didn’t enjoy it as much because it was more fragmented collection of random events from his life.</p>

<p><strong>Design Is a Job</strong> by Mike Monteiro</p>

<p><a href="http://www.abookapart.com/products/design-is-a-job"><img src="/images/2014/design_is_a_job.jpg" alt="Design Is a Job" /></a></p>

<p>Mike Montero has a unique speaking and writing voice which I enjoy. Book is about his learnings from running his design consultancy Mule Design. Book has good points to be applied on my own work, especially on client relationships. I would recommend it to anyone running a business that serves clients with professional services.</p>

<p><strong>Thinking Fast and Slow</strong> by Daniel Kahneman</p>

<p><a href="http://www.amazon.com/gp/product/0374533555/ref=as_li_ss_tl?ie=UTF8&amp;camp=1789&amp;creative=390957&amp;creativeASIN=0374533555&amp;linkCode=as2&amp;tag=vesan-20"><img src="/images/2014/Thinking,_Fast_and_Slow.jpg" alt="Thinking Fast and Slow" /></a></p>

<p>The book is a summary of research done by the author on the area of cognitive biases, his work on <a href="http://en.wikipedia.org/wiki/Prospect_theory">prospect theory</a> and on happiness. Underlying idea of most of Kahneman’s research is based on separation of two modes of thinking: faster, intuitive system and slower, logical system. He goes through how the systems work in unison and what kind of behavioral patterns emerge from that.</p>

<p>The book is a bit long to my taste but it is easy to follow and I think knowing about the biases of our own thinking has is beneficial to everyone.</p>

<h2 id="comics">Comics</h2>

<p><strong>Fok_It by Joonas Rinta-Kanto (books 1-4)</strong></p>

<p><a href="http://www.kreegah.net/index.php?_a=viewCat&amp;catId=13"><img src="/images/2014/fokit1-albumi.jpg" alt="Fok_It 1" /></a></p>

<p>Only available in Finnish. I enjoyed the whole available catalogue of Fok_It albums. It is just my kind of humour.</p>

<p><strong>Habibi by Craig Thompson</strong></p>

<p><a href="http://www.amazon.com/gp/product/0375424148/ref=as_li_ss_tl?ie=UTF8&amp;camp=1789&amp;creative=390957&amp;creativeASIN=0375424148&amp;linkCode=as2&amp;tag=vesan-20"><img src="/images/2014/Habibi.jpg" alt="Habibi" /></a></p>

<p>A love story set in Islamic world. The visual design is really great and with over 600 pages you get a lot to see. Anyone who enjoys great illustration and graphical storytelling should grab this one.</p>

<h2 id="extra-pick">Extra pick</h2>

<p><strong>DJ-kirja by Matti Nives &amp; Iina Esko</strong></p>

<p><a href="https://www.facebook.com/djkirja"><img src="/images/2014/dj_kirja.jpg" alt="DJ-kirja" /></a></p>

<p>I actually finished reading this on 2014 but I’ll include it anyway. This one is also only available in Finnish. The book includes essays about DJ culture, interviews of DJ’s and a section on club flyers from Finnish point of view. As a music nerd I really enjoyed all the view points and stories from familiar faces.</p>

<hr />

<p>Next year I’m going to aim for 50 read books and try to start a habit of writing notes or mini-reviews of the books I read. It takes some work but in the long run you get a quite nice library of good notes you can read to remind you on the important points. For example checkout out <a href="http://sivers.org/book">Derek Sivers’ book notes</a> with short summaries and longer notes.</p>]]></content><author><name>Vesa Vänskä</name></author><category term="reading" /><category term="books" /><summary type="html"><![CDATA[2013 was a good reading year for me and I thought I’d share the books that influenced me the most during the year.]]></summary></entry></feed>