Gray Soft / Tags / Processtag:graysoftinc.com,2014-03-20:/tags/Process2014-07-20T22:41:00ZJames Edward Gray IIDave's No Tests Challengetag:graysoftinc.com,2014-07-20:/posts/1222014-07-20T22:41:00ZAn exploration of what we can learn from the recent challenge that Dave Thomas gave to the Ruby Rogues: "Stop writing tests."<p>I've mentioned before <a href="http://graysoftinc.com/non-code/ipsc-2014-postmortem">my difficulties in the 2014 IPSC</a>. But taking one beating is no reason not to try again. The first loss just showed me that the contest still had more to teach me.</p>
<p>A buddy of mine has spent some time with <a href="http://ipsc.ksp.sk/2014/real/problems/k.html">the crossword problem</a> and told me that he enjoyed it. I didn't try this problem during the actual event, but I was a little familiar with it from my friend's description.</p>
<p>To add to the fun, I decided this would be a great excuse to take up <a href="http://rubyrogues.com/164-rr-staying-sharp-with-dave-thomas/">the recent challenge Dave Thomas gave to the Ruby Rogues</a>: "Stop writing tests."</p>
<h4>Step 1: Feedback Loops</h4>
<p>Without tests to guide me, I really want to see what's going on. One of the biggest advantages of tests, in my opinion, is the feedback loop it provides. So I set out to provide my own feedback.</p>
<p>Since the problem at hand involves filling in a crossword board, the easiest feedback loop I could think of was to see the board as it fills in. The final board is also the required output. Therefor, I decided a good first step would just be to read the board into some data structure and write it back out. Once I had that, I could insert code between those steps to fill it in. And constantly seeing the board evolve would let me eyeball things for obvious mistakes.</p>
<p>I set out to make that happen, with the minimal amount of effort. First I created a project:</p>
<pre><code>$ mkdir -p daves_no_tests_challenge/{bin,data,lib}
$ cd daves_no_tests_challenge/
$ git init
Initialized empty Git repository in /Users/jeg2/Desktop/daves_no_tests_challenge/.git/
</code></pre>
<p>Then I built a minimal data structure:</p>
<div class="highlight highlight-ruby"><pre><span class="k">module</span> <span class="nn">Crossword</span>
<span class="k">class</span> <span class="nc">Board</span>
<span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">squares</span><span class="p">)</span>
<span class="vi">@squares</span> <span class="o">=</span> <span class="n">squares</span>
<span class="k">end</span>
<span class="kp">attr_reader</span> <span class="ss">:squares</span>
<span class="kp">private</span> <span class="ss">:squares</span>
<span class="k">def</span> <span class="nf">to_s</span>
<span class="n">squares</span><span class="o">.</span><span class="n">map</span> <span class="p">{</span> <span class="o">|</span><span class="n">row</span><span class="o">|</span> <span class="n">row</span><span class="o">.</span><span class="n">join</span> <span class="p">}</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="s2">"</span><span class="se">\n</span><span class="s2">"</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
</pre></div>
<p>That code just takes in and stringifies (for output) some rows of crossword squares. Eventually, this code will need to be able to add answers, but that isn't needed for this first pass.</p>
<p>Next I wrapped my fledgling data structure in some reading and writing code:</p>
<div class="highlight highlight-ruby"><pre><span class="n">require_relative</span> <span class="s2">"board"</span>
<span class="k">module</span> <span class="nn">Crossword</span>
<span class="k">class</span> <span class="nc">Solver</span>
<span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">input</span><span class="p">,</span> <span class="n">output</span><span class="p">)</span>
<span class="vi">@input</span> <span class="o">=</span> <span class="n">input</span>
<span class="vi">@output</span> <span class="o">=</span> <span class="n">output</span>
<span class="vi">@board</span> <span class="o">=</span> <span class="kp">nil</span>
<span class="k">end</span>
<span class="kp">attr_reader</span> <span class="ss">:input</span><span class="p">,</span> <span class="ss">:output</span><span class="p">,</span> <span class="ss">:board</span>
<span class="kp">private</span> <span class="ss">:input</span><span class="p">,</span> <span class="ss">:output</span><span class="p">,</span> <span class="ss">:board</span>
<span class="k">def</span> <span class="nf">solve</span>
<span class="n">parse_board</span>
<span class="n">write_board</span>
<span class="k">end</span>
<span class="kp">private</span>
<span class="k">def</span> <span class="nf">parse_board</span>
<span class="n">rows</span><span class="p">,</span> <span class="n">columns</span> <span class="o">=</span> <span class="n">input</span><span class="o">.</span><span class="n">gets</span><span class="o">.</span><span class="n">scan</span><span class="p">(</span><span class="sr">/\d+/</span><span class="p">)</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="o">&</span><span class="ss">:to_i</span><span class="p">)</span>
<span class="vi">@board</span> <span class="o">=</span> <span class="no">Board</span><span class="o">.</span><span class="n">new</span><span class="p">(</span>
<span class="nb">Array</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">rows</span><span class="p">)</span> <span class="p">{</span> <span class="n">input</span><span class="o">.</span><span class="n">gets</span><span class="o">.</span><span class="n">chars</span><span class="o">.</span><span class="n">first</span><span class="p">(</span><span class="n">columns</span><span class="p">)</span> <span class="p">}</span>
<span class="p">)</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">write_board</span>
<span class="n">output</span><span class="o">.</span><span class="n">puts</span> <span class="n">board</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
</pre></div>
<p>That code just parses the <code>Board</code> using the dimensions in the input file and writes it back out. This class gives me a place to hang some clue solving code, when I'm ready for that.</p>
<p>In preparation for adding an executable, I collapsed this process into a simple method call:</p>
<div class="highlight highlight-ruby"><pre><span class="n">require_relative</span> <span class="s2">"crossword/solver"</span>
<span class="k">module</span> <span class="nn">Crossword</span>
<span class="kp">module_function</span>
<span class="k">def</span> <span class="nf">solve</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">)</span>
<span class="no">Solver</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">)</span><span class="o">.</span><span class="n">solve</span>
<span class="k">end</span>
<span class="k">end</span>
</pre></div>
<p>Finally, I added the minimal binary:</p>
<div class="highlight highlight-ruby"><pre><span class="c1">#!/usr/bin/env ruby -w</span>
<span class="n">require_relative</span> <span class="s2">"../lib/crossword"</span>
<span class="no">Crossword</span><span class="o">.</span><span class="n">solve</span><span class="p">(</span><span class="no">ARGF</span><span class="p">,</span> <span class="vg">$stdout</span><span class="p">)</span>
</pre></div>
<p>If we make that file executable and point it at the easy input, we can see our first empty crossword:</p>
<pre><code>$ chmod +x bin/solve
$ bin/solve data/k1.in
........#.#...#.#.#####.......
......######..#.#..#...####...
..####..#.#..#######...#......
....#.######..#.#..#.#.#......
....#...#.#...#.#.######......
....#...#.########.#.#.#####..
.#######....#...#..#.#.#......
.#..#.#.#######......#....#...
.#....#.....#.#.......#...#...
.#....#.########..#########...
.######.#.#.#.#.#.....#...#...
.#....#.#.#.#.#.#.##########..
.....########.#.#.....#...#...
.#..#.#.#.#...#.#########.#...
######...####.#.......#.#.#...
.#..#.#.#.#....############..#
.#..########.#....#...#.#....#
.#..#.#.#...#####.##########.#
.#..#.#.#..#.#....#.....#.#..#
...#########.#########..#.#..#
....#.#.#..#.#....#.#.########
......#.#..#.#########..#.#..#
.#.#..#.#..#.#..#.#.#...#.#...
.#.#.......#.#..#.#.#.#...#...
#####.#########.#.#.#.#...#.#.
.#.#..#.#..#....#..########.#.
.######.#..#######..#.#...#.#.
...#..#.#.......#...#.#.....#.
....######...######...#.....#.
......#.#.............#######.
</code></pre>
<p>Seeing this made me realize that there are two sides to crosswords: the board and the clues. I now had some visibility into one side, but what about clues?</p>
<p>For clues I wanted to know, what am I handling and what am I not. If I printed out the not yet handled stuff, I could scan such a list for similar clues and then write the code to handle them. I should then see the unhandled clue list shrink and some answers appear on the board. I could then just repeat that process until I am done.</p>
<p>This requires just two changes to my <code>Solver</code>:</p>
<div class="highlight highlight-ruby"><pre><span class="c1"># ...</span>
<span class="k">module</span> <span class="nn">Crossword</span>
<span class="k">class</span> <span class="nc">Solver</span>
<span class="c1"># ...</span>
<span class="k">def</span> <span class="nf">solve</span>
<span class="n">parse_board</span>
<span class="n">solve_clues</span>
<span class="n">write_board</span>
<span class="k">end</span>
<span class="kp">private</span>
<span class="c1"># ...</span>
<span class="k">def</span> <span class="nf">solve_clues</span>
<span class="o">%</span><span class="n">i</span><span class="o">[</span><span class="n">across</span> <span class="n">down</span><span class="o">].</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">direction</span><span class="o">|</span>
<span class="n">count</span> <span class="o">=</span> <span class="nb">gets</span><span class="o">.</span><span class="n">to_i</span>
<span class="n">count</span><span class="o">.</span><span class="n">times</span> <span class="k">do</span>
<span class="n">row</span><span class="p">,</span> <span class="n">column</span><span class="p">,</span> <span class="n">clue</span> <span class="o">=</span> <span class="nb">gets</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">" "</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span><span class="o">.</span><span class="n">map</span><span class="o">.</span><span class="n">with_index</span> <span class="p">{</span> <span class="o">|</span><span class="n">str</span><span class="p">,</span> <span class="n">i</span><span class="o">|</span>
<span class="n">i</span> <span class="o"><</span> <span class="mi">2</span> <span class="o">?</span> <span class="n">str</span><span class="o">.</span><span class="n">to_i</span> <span class="p">:</span> <span class="n">str</span><span class="o">.</span><span class="n">strip</span>
<span class="p">}</span>
<span class="k">if</span> <span class="kp">false</span> <span class="c1"># FIXME: match a clue</span>
<span class="c1"># FIXME: write the answer to the board</span>
<span class="k">else</span>
<span class="nb">warn</span> <span class="s2">"No match: </span><span class="si">#{</span><span class="n">clue</span><span class="si">}</span><span class="s2">"</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="c1"># ...</span>
<span class="k">end</span>
<span class="k">end</span>
</pre></div>
<p>I added a <code>solve_clues()</code> step to the overall process, then fleshed out that method just enough to parse clues and consider all of them failures. Notice that I got a bit hand wavy with how the solving stuff was actually going to work. It didn't matter yet. I also put the clue warnings on <code>STDERR</code> because they aren't legally part of the output and I wanted to be able to redirect them and the board independently.</p>
<p>Now I had a lot of output, but it showed me exactly what I needed to do:</p>
<pre><code>No match: Pokemon which evolves from Oddish
No match: Pokemon which evolves from Spearow
No match: Chemical element with the symbol Au
No match: Pokemon with number #63
No match: Pokemon with number #137
No match: Last name of the President of the United States who held the office in 1987
No match: Chemical element with the symbol Na
No match: Chemical element with the symbol W
No match: Pokemon which evolves into Arbok
No match: Pokemon which evolves from Machoke
…
........#.#...#.#.#####.......
......######..#.#..#...####...
..####..#.#..#######...#......
....#.######..#.#..#.#.#......
....#...#.#...#.#.######......
....#...#.########.#.#.#####..
.#######....#...#..#.#.#......
.#..#.#.#######......#....#...
.#....#.....#.#.......#...#...
.#....#.########..#########...
.######.#.#.#.#.#.....#...#...
.#....#.#.#.#.#.#.##########..
.....########.#.#.....#...#...
.#..#.#.#.#...#.#########.#...
######...####.#.......#.#.#...
.#..#.#.#.#....############..#
.#..########.#....#...#.#....#
.#..#.#.#...#####.##########.#
.#..#.#.#..#.#....#.....#.#..#
...#########.#########..#.#..#
....#.#.#..#.#....#.#.########
......#.#..#.#########..#.#..#
.#.#..#.#..#.#..#.#.#...#.#...
.#.#.......#.#..#.#.#.#...#...
#####.#########.#.#.#.#...#.#.
.#.#..#.#..#....#..########.#.
.######.#..#######..#.#...#.#.
...#..#.#.......#...#.#.....#.
....######...######...#.....#.
......#.#.............#######.
</code></pre>
<h4>Step 2: Strategies</h4>
<p>Satisfied that I could now follow my process without using a test suite, it was time to make this problem easy to solve.</p>
<p>I realized that different clues would need different strategies to solve and I might need to write several of those. This made me want to add a little bit of infrastructure for loading and matching strategies. I figured I could solve that problem now, then focus in entirely on clue specifics.</p>
<p>I sketched out a base class for this approach:</p>
<div class="highlight highlight-ruby"><pre><span class="k">module</span> <span class="nn">Crossword</span>
<span class="k">class</span> <span class="nc">Strategy</span>
<span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">strategies</span>
<span class="vi">@strategies</span> <span class="o">||=</span> <span class="o">[</span> <span class="o">]</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">inherited</span><span class="p">(</span><span class="n">subclass</span><span class="p">)</span>
<span class="n">strategies</span> <span class="o"><<</span> <span class="n">subclass</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">load</span><span class="p">(</span><span class="n">dir</span> <span class="o">=</span> <span class="no">File</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">__dir__</span><span class="p">,</span> <span class="s2">"strategies"</span><span class="p">))</span>
<span class="no">Dir</span><span class="o">.</span><span class="n">glob</span><span class="p">(</span><span class="s2">"</span><span class="si">#{</span><span class="n">dir</span><span class="si">}</span><span class="s2">/*.rb"</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">path</span><span class="o">|</span>
<span class="no">Kernel</span><span class="o">.</span><span class="n">load</span> <span class="n">path</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">match</span><span class="p">(</span><span class="n">clue</span><span class="p">)</span>
<span class="n">strategies</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">subclass</span><span class="o">|</span>
<span class="n">strategy</span> <span class="o">=</span> <span class="n">subclass</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">clue</span><span class="p">)</span>
<span class="k">return</span> <span class="n">strategy</span> <span class="k">if</span> <span class="n">strategy</span><span class="o">.</span><span class="n">match?</span>
<span class="k">end</span>
<span class="kp">nil</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">clue</span><span class="p">)</span>
<span class="vi">@clue</span> <span class="o">=</span> <span class="n">clue</span>
<span class="vi">@match_data</span> <span class="o">=</span> <span class="kp">nil</span>
<span class="k">end</span>
<span class="kp">attr_reader</span> <span class="ss">:clue</span><span class="p">,</span> <span class="ss">:match_data</span>
<span class="kp">private</span> <span class="ss">:clue</span><span class="p">,</span> <span class="ss">:match_data</span>
<span class="k">def</span> <span class="nf">match?</span>
<span class="vi">@match_data</span> <span class="o">=</span> <span class="nb">self</span><span class="o">.</span><span class="n">class</span><span class="o">.</span><span class="n">const_get</span><span class="p">(</span><span class="ss">:CLUE_REGEX</span><span class="p">)</span><span class="o">.</span><span class="n">match</span><span class="p">(</span><span class="n">clue</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">answer</span>
<span class="nb">fail</span> <span class="no">NotImplementedError</span><span class="p">,</span> <span class="s2">"Strategies must override answer()"</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
</pre></div>
<p>The four class methods all work together to support this simple process:</p>
<ol>
<li>I plan to define subclasses of <code>Strategy</code>
</li>
<li>And I'll throw all of them in the same directory</li>
<li>So dynamically load everything in there</li>
<li>Then search through all subclasses to find one that matches the current clue</li>
</ol><p>The two instance methods handle matching and generating answers for clues. Originally I considered forcing subclasses to implement both. However a glance at the clues made me feel that at least some could be matched with a simple regex. I decided to make that the default behavior. Subclasses can still override <code>match?()</code> if they need more complex logic.</p>
<p>My use of a constant for the regex is a little tricky. Had I used a bare constant, Ruby would have resolved it based on the current scope. Put simply, it would have searched for a constant in this class. (That's an over simplification, but true enough here.) It would not find a constant defined in a <code>Strategy</code> subclass, where I wanted to put them. Because of that, I look up the constant manually, using Ruby's reflection capabilities. This code will find a constant in the subclass. The truth is, it will fail with an error if the subclass does not define the constant, but I expect <code>match?()</code> to be overriden in those cases.</p>
<p>Plugging these strategies into the <code>Solver</code> is just four lines of code, explained inline:</p>
<div class="highlight highlight-ruby"><pre><span class="n">require_relative</span> <span class="s2">"board"</span>
<span class="n">require_relative</span> <span class="s2">"strategy"</span> <span class="c1"># 1: load the dependency</span>
<span class="k">module</span> <span class="nn">Crossword</span>
<span class="k">class</span> <span class="nc">Solver</span>
<span class="c1"># ...</span>
<span class="kp">private</span>
<span class="c1"># ...</span>
<span class="k">def</span> <span class="nf">solve_clues</span>
<span class="no">Strategy</span><span class="o">.</span><span class="n">load</span> <span class="c1"># 2: dynamically load all available strategies</span>
<span class="o">%</span><span class="n">i</span><span class="o">[</span><span class="n">across</span> <span class="n">down</span><span class="o">].</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">direction</span><span class="o">|</span>
<span class="n">count</span> <span class="o">=</span> <span class="nb">gets</span><span class="o">.</span><span class="n">to_i</span>
<span class="n">count</span><span class="o">.</span><span class="n">times</span> <span class="k">do</span>
<span class="n">row</span><span class="p">,</span> <span class="n">column</span><span class="p">,</span> <span class="n">clue</span> <span class="o">=</span> <span class="nb">gets</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">" "</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span><span class="o">.</span><span class="n">map</span><span class="o">.</span><span class="n">with_index</span> <span class="p">{</span> <span class="o">|</span><span class="n">str</span><span class="p">,</span> <span class="n">i</span><span class="o">|</span>
<span class="n">i</span> <span class="o"><</span> <span class="mi">2</span> <span class="o">?</span> <span class="n">str</span><span class="o">.</span><span class="n">to_i</span> <span class="p">:</span> <span class="n">str</span><span class="o">.</span><span class="n">strip</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">strategy</span> <span class="o">=</span> <span class="no">Strategy</span><span class="o">.</span><span class="n">match</span><span class="p">(</span><span class="n">clue</span><span class="p">))</span> <span class="c1"># 3: find a match for the clue</span>
<span class="c1"># 4: record the answer on the Board:</span>
<span class="n">board</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="s2">"record_</span><span class="si">#{</span><span class="n">direction</span><span class="si">}</span><span class="s2">"</span><span class="p">,</span> <span class="n">row</span><span class="p">,</span> <span class="n">column</span><span class="p">,</span> <span class="n">strategy</span><span class="o">.</span><span class="n">answer</span><span class="p">)</span>
<span class="k">else</span>
<span class="nb">warn</span> <span class="s2">"No match: </span><span class="si">#{</span><span class="n">clue</span><span class="si">}</span><span class="s2">"</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="c1"># ...</span>
<span class="k">end</span>
<span class="k">end</span>
</pre></div>
<p>As you can see, this required the <code>Board</code> to gain some new methods as well, for recording answers:</p>
<div class="highlight highlight-ruby"><pre><span class="k">module</span> <span class="nn">Crossword</span>
<span class="k">class</span> <span class="nc">Board</span>
<span class="c1"># ...</span>
<span class="k">def</span> <span class="nf">record_across</span><span class="p">(</span><span class="n">row</span><span class="p">,</span> <span class="n">column</span><span class="p">,</span> <span class="n">answer</span><span class="p">)</span>
<span class="n">answer</span><span class="o">.</span><span class="n">downcase</span><span class="o">.</span><span class="n">chars</span><span class="o">.</span><span class="n">each_with_index</span> <span class="k">do</span> <span class="o">|</span><span class="n">char</span><span class="p">,</span> <span class="n">i</span><span class="o">|</span>
<span class="n">record</span><span class="p">(</span><span class="n">row</span><span class="p">,</span> <span class="n">column</span> <span class="o">+</span> <span class="n">i</span><span class="p">,</span> <span class="n">char</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">record_down</span><span class="p">(</span><span class="n">row</span><span class="p">,</span> <span class="n">column</span><span class="p">,</span> <span class="n">answer</span><span class="p">)</span>
<span class="n">answer</span><span class="o">.</span><span class="n">downcase</span><span class="o">.</span><span class="n">chars</span><span class="o">.</span><span class="n">each_with_index</span> <span class="k">do</span> <span class="o">|</span><span class="n">char</span><span class="p">,</span> <span class="n">i</span><span class="o">|</span>
<span class="n">record</span><span class="p">(</span><span class="n">row</span> <span class="o">+</span> <span class="n">i</span><span class="p">,</span> <span class="n">column</span><span class="p">,</span> <span class="n">char</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="c1"># ...</span>
<span class="kp">private</span>
<span class="k">def</span> <span class="nf">record</span><span class="p">(</span><span class="n">row</span><span class="p">,</span> <span class="n">column</span><span class="p">,</span> <span class="n">char</span><span class="p">)</span>
<span class="n">square</span> <span class="o">=</span> <span class="n">squares</span><span class="o">[</span><span class="n">row</span><span class="o">][</span><span class="n">column</span><span class="o">]</span>
<span class="nb">fail</span> <span class="s2">"Outside of board"</span> <span class="k">if</span> <span class="n">square</span><span class="o">.</span><span class="n">nil?</span>
<span class="nb">fail</span> <span class="s2">"Outside of answer"</span> <span class="k">if</span> <span class="n">square</span> <span class="o">==</span> <span class="s2">"."</span>
<span class="nb">fail</span> <span class="s2">"Answer mismatch"</span> <span class="k">if</span> <span class="o">![</span><span class="s2">"#"</span><span class="p">,</span> <span class="n">char</span><span class="o">].</span><span class="n">include?</span><span class="p">(</span><span class="n">char</span><span class="p">)</span>
<span class="n">square</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="n">char</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
</pre></div>
<p>I did notice that <code>record_across()</code> and <code>record_down()</code> had some duplication that could be removed with some <code>yield</code> magic. This is a pretty simple case though and I decided to let it ride.</p>
<p>You'll noticed that I cranked the paranoia up a bit in <code>record()</code>. Placing letters in a crossword puzzle is a great time to notice several potential problems like overruns or answers that don't agree. I don't know if I would have added all of these guard clauses in the presence of a strong test suite, but I liked seeing them here in the end. I think the extra checks here, where they mattered most, helped me write the rest of the code with more confidence.</p>
<p>This felt like enough infrastructure to start solving actual clues.</p>
<p>I decided to start with the Pokémon questions. Glancing through the lists, I saw two different flavors of clues. So I went hunting for <a href="https://raw.githubusercontent.com/veekun/pokedex/master/pokedex/data/csv/pokemon.csv">a data file that could answer both types of questions in an easy-to-read format</a>. Having found that, I needed some code to parse the data and allow me to search through it:</p>
<div class="highlight highlight-ruby"><pre><span class="nb">require</span> <span class="s2">"csv"</span>
<span class="k">module</span> <span class="nn">Crossword</span>
<span class="k">class</span> <span class="nc">PokemonList</span>
<span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">loaded</span>
<span class="vi">@loaded</span> <span class="o">||=</span> <span class="kp">new</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">method_missing</span><span class="p">(</span><span class="nb">method</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">&</span><span class="n">block</span><span class="p">)</span>
<span class="k">if</span> <span class="n">loaded</span><span class="o">.</span><span class="n">respond_to?</span><span class="p">(</span><span class="nb">method</span><span class="p">)</span>
<span class="n">loaded</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="nb">method</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">&</span><span class="n">block</span><span class="p">)</span>
<span class="k">else</span>
<span class="k">super</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">path</span> <span class="o">=</span> <span class="no">File</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">__dir__</span><span class="p">,</span> <span class="o">*</span><span class="sx">%w[.. .. data pokemon.csv]</span><span class="p">))</span>
<span class="vi">@pokemon</span> <span class="o">=</span> <span class="no">CSV</span><span class="o">.</span><span class="n">read</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="ss">headers</span><span class="p">:</span> <span class="kp">true</span><span class="p">,</span> <span class="n">header_converters</span><span class="p">:</span> <span class="ss">:symbol</span><span class="p">)</span>
<span class="k">end</span>
<span class="kp">attr_reader</span> <span class="ss">:pokemon</span>
<span class="kp">private</span> <span class="ss">:pokemon</span>
<span class="k">def</span> <span class="nf">find_by_number</span><span class="p">(</span><span class="n">number</span><span class="p">)</span>
<span class="n">pokemon</span><span class="o">.</span><span class="n">find</span> <span class="p">{</span> <span class="o">|</span><span class="n">record</span><span class="o">|</span> <span class="n">record</span><span class="o">[</span><span class="ss">:id</span><span class="o">]</span> <span class="o">==</span> <span class="n">number</span> <span class="p">}</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
</pre></div>
<p>I do the actual parse in <code>initialize()</code>, just handing off to the standard <code>CSV</code> library. The class methods setup a system of fowarding to the instance methods for searching through a loaded data set. The only search I needed to drop the first set of clues was an ability to find a Pokémon by number.</p>
<p>With this database ready to go, the actual strategy for solving the clue is almost a non-event:</p>
<div class="highlight highlight-ruby"><pre><span class="n">require_relative</span> <span class="s2">"../pokemon_list"</span>
<span class="k">module</span> <span class="nn">Crossword</span>
<span class="k">class</span> <span class="nc">PokemonNumber</span> <span class="o"><</span> <span class="no">Strategy</span>
<span class="no">CLUE_REGEX</span> <span class="o">=</span> <span class="sr">/\APokemon with number #(?<number>\d+)\z/</span>
<span class="k">def</span> <span class="nf">answer</span>
<span class="no">PokemonList</span><span class="o">.</span><span class="n">find_by_number</span><span class="p">(</span><span class="n">match_data</span><span class="o">[</span><span class="ss">:number</span><span class="o">]</span><span class="p">)</span><span class="o">[</span><span class="ss">:identifier</span><span class="o">]</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
</pre></div>
<p>You can finally see how my stategies pay off here. I write a regex to match the clue, capturing the key bits, then use those bits to find the right answer when called upon to do so.</p>
<p>It was time to check in on those feedback loops:</p>
<pre><code>No match: Pokemon which evolves from Oddish
No match: Pokemon which evolves from Spearow
No match: Chemical element with the symbol Au
…
........#.#...#.#.#####.......
......######..#.#..#...g###...
..abra..#.#..porygon...r......
....#.######..#.#..#.d.i......
....#...#.#...#.#.###i#m......
....#...#.########.#.t.e####..
.m######....#...#..#.t.r......
.e..#.#.#######......o....#...
.w....#.....#.#.......#...#...
.t....#.##a#####..#########...
.w#####.#.r.#.#.#.....#...#...
.o....#.#.t.#.#.#.hitmonchan..
.....#####i##.#.#.....#...#...
.m..#.#.#.c...#.#########.#...
#a####...#u##.#.......#.#.#...
.c..#.#.#.n....############..#
.h..######o#.s....#...#.#....#
.o..#.#.#...#a###.charmander.#
.p..#.#.#..#.n....#.....#.#..#
...#########.dragonite..#.#..#
....#.#.#..#.s....#.#.primeape
......#.#..#.lickitung..#.#..#
.#.w..#.#..#.a..#.#.#...#.#...
.#.e.......#.s..#.#.#.#...#...
###e#.growlithe.#.#.#.#...#.k.
.#.d..#.#..#....#..########.r.
.##l###.#..#######..#.#...#.a.
...e..#.#.......#...#.#.....b.
....######...pinsir...#.....b.
......#.#.............######y.
</code></pre>
<p>There are two things to notice here:</p>
<ul>
<li>There are still plenty of unsolved clues, including another flavor of Pokémon clues</li>
<li>The board is starting to fill in and we even see some clues sharing squares</li>
</ul><h4>Step 3: The Real Problem</h4>
<p>The rest of this puzzle was just about writing strategies to solve the various clues and it went pretty smoothly. I'm not going to show all of them. Checkout <a href="https://github.com/JEG2/daves_no_test_challenge">the repository on GitHub</a>, if you're curious.</p>
<p>I'll show you the most challenging clue type though, just for fun. Check out this one:</p>
<pre><code>No match: Last name of the next President of the United States after Harry S. Truman
</code></pre>
<p>This clue is still just a lookup in a database, but names are involved. Names can be quite a curve ball. Trust the guy named James Edward Gray II and married to Dana Ann Leslie Gray. There's not a form in the world that handles both of our names gracefully. I knew enough to fear this part, because there was very little chance of the database perfectly matching the clues. Given that, I built an object just to handle name comparisons:</p>
<div class="highlight highlight-ruby"><pre><span class="k">module</span> <span class="nn">Crossword</span>
<span class="k">class</span> <span class="nc">Name</span>
<span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">full_name</span><span class="p">)</span>
<span class="vi">@full_name</span> <span class="o">=</span> <span class="n">full_name</span>
<span class="k">end</span>
<span class="kp">attr_reader</span> <span class="ss">:full_name</span>
<span class="kp">private</span> <span class="ss">:full_name</span>
<span class="k">def</span> <span class="nf">first_name</span>
<span class="n">full_name</span><span class="o">[</span><span class="sr">/\A\w+/</span><span class="o">]</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">last_name</span>
<span class="n">full_name</span><span class="o">[</span><span class="sr">/\w+\z/</span><span class="o">]</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">==</span><span class="p">(</span><span class="n">other</span><span class="p">)</span>
<span class="n">first_name</span> <span class="o">==</span> <span class="n">other</span><span class="o">.</span><span class="n">first_name</span> <span class="o">&&</span>
<span class="n">last_name</span> <span class="o">==</span> <span class="n">other</span><span class="o">.</span><span class="n">last_name</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
</pre></div>
<p>Now I'll be honest: I cheated here. A lot. I didn't need to worry about suffixes because my database didn't include them and I didn't need to consider middle names or initials in the comparisons for these easy clues. (That's not true in the hard input!) I was lucky that both lists were small enough for me to visually scan for issues like that. It saved me a fair bit of code.</p>
<p>The president database is similar to what you saw for Pokémon:</p>
<div class="highlight highlight-ruby"><pre><span class="nb">require</span> <span class="s2">"csv"</span>
<span class="n">require_relative</span> <span class="s2">"name"</span>
<span class="k">module</span> <span class="nn">Crossword</span>
<span class="k">class</span> <span class="nc">PresidentList</span>
<span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">loaded</span>
<span class="vi">@loaded</span> <span class="o">||=</span> <span class="kp">new</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">method_missing</span><span class="p">(</span><span class="nb">method</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">&</span><span class="n">block</span><span class="p">)</span>
<span class="k">if</span> <span class="n">loaded</span><span class="o">.</span><span class="n">respond_to?</span><span class="p">(</span><span class="nb">method</span><span class="p">)</span>
<span class="n">loaded</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="nb">method</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">&</span><span class="n">block</span><span class="p">)</span>
<span class="k">else</span>
<span class="k">super</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">path</span> <span class="o">=</span> <span class="no">File</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">__dir__</span><span class="p">,</span> <span class="o">*</span><span class="sx">%w[.. .. data presidents.csv]</span><span class="p">))</span>
<span class="vi">@presidents</span> <span class="o">=</span> <span class="no">CSV</span><span class="o">.</span><span class="n">read</span><span class="p">(</span>
<span class="n">path</span><span class="p">,</span>
<span class="ss">headers</span><span class="p">:</span> <span class="kp">true</span><span class="p">,</span>
<span class="n">header_converters</span><span class="p">:</span> <span class="ss">:symbol</span><span class="p">,</span>
<span class="ss">converters</span><span class="p">:</span> <span class="nb">lambda</span> <span class="p">{</span> <span class="o">|</span><span class="n">field</span><span class="p">,</span> <span class="n">field_info</span><span class="o">|</span>
<span class="k">if</span> <span class="n">field_info</span><span class="o">.</span><span class="n">header</span> <span class="o">==</span> <span class="ss">:president</span>
<span class="no">Name</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">field</span><span class="o">.</span><span class="n">sub</span><span class="p">(</span><span class="s2">" (2nd term)"</span><span class="p">,</span> <span class="s2">""</span><span class="p">))</span>
<span class="k">else</span>
<span class="n">field</span>
<span class="k">end</span>
<span class="p">}</span>
<span class="p">)</span>
<span class="k">end</span>
<span class="kp">attr_reader</span> <span class="ss">:presidents</span>
<span class="kp">private</span> <span class="ss">:presidents</span>
<span class="k">def</span> <span class="nf">find_by_name</span><span class="p">(</span><span class="nb">name</span><span class="p">)</span>
<span class="n">presidents</span><span class="o">.</span><span class="n">find</span> <span class="p">{</span> <span class="o">|</span><span class="n">record</span><span class="o">|</span> <span class="n">record</span><span class="o">[</span><span class="ss">:president</span><span class="o">]</span> <span class="o">==</span> <span class="nb">name</span> <span class="p">}</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">find_by_number</span><span class="p">(</span><span class="n">number</span><span class="p">)</span>
<span class="n">presidents</span><span class="o">.</span><span class="n">find</span> <span class="p">{</span> <span class="o">|</span><span class="n">record</span><span class="o">|</span> <span class="n">record</span><span class="o">[</span><span class="ss">:presidency</span><span class="o">]</span> <span class="o">==</span> <span class="n">number</span> <span class="p">}</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
</pre></div>
<p>You can see that I used the same class method forwarding trick here. In fact, I used it in all three databases that I built to solve this problem. Were I not just playing around here, I'm pretty sure I would have felt compelled to extract the common code into a superclass, but I knew there was almost no chance I would need to change it during this exercise. Copy and paste coding turned out to be good enough for this case.</p>
<p>The main difference in the load here is that I had <code>CSV</code> convert the names into <code>Name</code> objects as they were read in. This allows for easier comparisons later on.</p>
<p>The two instance methods are the searches I needed to find some president by name and then look for a later president. Here's the strategy that actually does that:</p>
<div class="highlight highlight-ruby"><pre><span class="n">require_relative</span> <span class="s2">"../president_list"</span>
<span class="n">require_relative</span> <span class="s2">"../name"</span>
<span class="k">module</span> <span class="nn">Crossword</span>
<span class="k">class</span> <span class="nc">PresidentAfter</span> <span class="o"><</span> <span class="no">Strategy</span>
<span class="no">CLUE_REGEX</span> <span class="o">=</span> <span class="sr">/</span>
<span class="sr"> \A (?<name>First|Last) \s+ name \s+ of \s+ the \s+</span>
<span class="sr"> next \s+ President \s+ of \s+ the \s+ United \s+ States \s+ after \s+</span>
<span class="sr"> (?<before>.+) \z</span>
<span class="sr"> /x</span>
<span class="k">def</span> <span class="nf">answer</span>
<span class="nb">id</span> <span class="o">=</span> <span class="no">PresidentList</span><span class="o">.</span><span class="n">find_by_name</span><span class="p">(</span>
<span class="no">Name</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">match_data</span><span class="o">[</span><span class="ss">:before</span><span class="o">]</span><span class="p">)</span>
<span class="p">)</span><span class="o">[</span><span class="ss">:presidency</span><span class="o">].</span><span class="n">to_i</span> <span class="o">+</span> <span class="mi">1</span>
<span class="no">PresidentList</span>
<span class="o">.</span><span class="n">find_by_number</span><span class="p">(</span><span class="nb">id</span><span class="o">.</span><span class="n">to_s</span><span class="p">)</span><span class="o">[</span><span class="ss">:president</span><span class="o">]</span>
<span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="s2">"</span><span class="si">#{</span><span class="n">match_data</span><span class="o">[</span><span class="ss">:name</span><span class="o">].</span><span class="n">downcase</span><span class="si">}</span><span class="s2">_name"</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
</pre></div>
<p>These queries pick up some oddities of their individual databases, that could definitely be cleaned up, if this wasn't contest code. That said, the strategy code is pretty short even for this trickier clue. I'm not feeling much pain.</p>
<h4>Conclusion</h4>
<p>Overall, I really liked the process I slipped into here:</p>
<ol>
<li>Setup feedback loops</li>
<li>Make the problem easy to solve</li>
<li>Solve the problem</li>
</ol><p>Testing can be a viable way to handle feedback loops, but it's not the only available solution. Sure, I had a couple of times during this experiment when I introduced a bug that I found on the next run, but there weren't many of those and they were easily dealt with. I didn't feel super hindered in this approach. I need to keep exploring this idea though, so I know what the limitations are.</p>
<p>In the end, solving just this easier portion of the problem took me roughly three and a half hours. That's not fair though, because I was able to do a little thinking about how I should handle it before I started.</p>James Edward Gray IIOne Programmer's Librarytag:graysoftinc.com,2014-07-10:/posts/1212021-08-23T01:22:16ZMy list of books that most programmers should read with some reasoning behind the choices.<p>I have always enjoyed the posts where people list out all of the books they think are important, given some subject. They vary wildly. For example, <a href="https://signalvnoise.com/posts/3375-the-five-programming-books-that-meant-most-to-me">some are very to-the-point</a> while <a href="http://www.daniellesucher.com/2014/01/05/the-best-books-i-read-in-2013/">others are rich in detail</a> (and Danielle makes one of those each year).</p>
<p>I began to wonder what my list would look like. Below I've tried to make those decisions. I was surprised by just how hard it is to restrict myself to the bare essentials. A lot of things that I read influence me in some way.</p>
<h4>The Classics</h4>
<p>I'll start with the super obvious titles you've likely heard mentioned before.</p>
<ul>
<li>
<a href="https://martinfowler.com/books/refactoring.html">Refactoring: Improving the Design of Existing Code</a> is probably the classically great programming text that had the biggest effect on me. This book teaches you how to turn the code you have into the code you want. It doesn't get much more essential than that.</li>
<li>
<a href="https://www.oreilly.com/library/view/smalltalk-best-practice/9780132852098/">Smalltalk Best Practice Patterns</a> is the book I learned a new language just to read. It's worth that. This is <em>The Field Manual of Object-Oriented Tactics</em> and it helps you know what to do line-by-line.</li>
<li>
<a href="https://martinfowler.com/books/eaa.html">Patterns of Enterprise Application Architecture</a> is the book you should read so you can see how much great knowledge we've had about building programs for over a decade. Odds are that this book can teach you multiple strategies for problems you face regularly.</li>
<li>
<a href="https://pragprog.com/titles/tpp20/the-pragmatic-programmer-20th-anniversary-edition/">The Pragmatic Programmer: From Journeyman to Master</a> is one of those rare books that can make you a better person, in addition to a better programmer. Advice like "Fix Broken Windows" and "Make Stone Soup" have universal scope. This is a must read.</li>
<li>
<a href="https://www.oreilly.com/library/view/programming-pearls-second/9780134498058/">Programming Pearls (2nd Edition)</a> is a book about algorithms and, eventually, all programmers need to learn some algorithms. The upside of this title is that it's fun from page one. That helps to keep you interested in what can be a dry topic.</li>
<li>
<a href="http://www.growing-object-oriented-software.com/">Growing Object-Oriented Software, Guided by Tests</a> is almost surely the single biggest influence on how I do Test-Driven Development. There are other schools of thought but pick one and dig deep enough into TDD until you're confident about when and how to use it. This is a tool everyone needs in their toolbox.</li>
</ul><h4>Focused Knowledge</h4>
<p>Most of my favorite programming books are hyper focused on some topic.</p>
<ul>
<li>
<a href="https://pragprog.com/titles/ruby4/programming-ruby-1-9-2-0-4th-edition/">Programming Ruby 1.9 & 2.0: The Pragmatic Programmers' Guide (The Facets of Ruby)</a> is still my favorite book to learn Ruby from, despite the surplus of choices available today. I prefer language books like this that teach you the basics and give you enough of a foundational reference to start building on. Oh, and you should definitely learn Ruby, or an equivalent dynamic language.</li>
<li>
<a href="https://www.oreilly.com/library/view/mastering-regular-expressions/0596528124/">Mastering Regular Expressions</a> is a book about some black magic that is supported in many text editors, programming languages, and command-line tools. Strangely, many programmers are afraid of them. That was all the reason I needed to read this one and it really did turn me into a wizard.</li>
<li>
<a href="https://www.apress.com/us/book/9781590593769">From Bash to Z Shell: Conquering the Command Line</a> has been <a href="http://graysoftinc.com/book-reviews/from-bash-to-z-shell">reviewed in detail</a> on this blog before. All you need to know though is that this one can teach you a lot about modern shells. All programmers can benefit from knowing how shells work. Plus, you won't be able to escape learning a bit about how computers manage processes and share information.</li>
<li>
<a href="https://www.oreilly.com/library/view/network-programming-with/0201615711/">Network Programming with Perl</a> was a major part of the reason I became a programmer. This taught me how to build servers and got me coding up MUDs in my spare time. I'm sure you have different inspirations, but it's critical to have them. You need pet projects to guide your learning.</li>
<li>
<a href="https://computationbook.com/">Understanding Computation: From Simple Machines to Impossible Programs</a> could be called <em>Computer Science: The Handy Parts</em>. If you didn't go to school for computers, this excellent book can teach you some of what you missed and do need to know. I won't kid you, it's a challenging read. It's totally delivers though.</li>
<li>
<a href="https://smile.amazon.com/gp/product/0672315858/?sa-no-redirect=1">Java Thread Programming</a> is the book that taught me how to do thread programming and I'm super glad that I learned it in Java. Each language has things they do well and things they don't. You'll need to shop for some topics in other lands.</li>
</ul><h4>My Area</h4>
<p>At some point I assume most of us find areas to call our own. If you read this blog, you know that I'm a Rubyist. These are my favorites from that world. Some of them may be worth a side trip for non-Rubyists while others likely have equivalents in other worlds.</p>
<ul>
<li>
<a href="https://www.poodr.com/">Practical Object-Oriented Design in Ruby: An Agile Primer (Addison-Wesley Professional Ruby Series)</a> is a vital book that teaches you how to think about Object Design. Java may be the right place to learn threading, but Ruby lives and breaths objects and I doubt this book would be so great in another language.</li>
<li>
<a href="http://patshaughnessy.net/ruby-under-a-microscope">Ruby Under a Microscope: An Illustrated Guide to Ruby Internals</a> is a super cool <em>How VM's Work</em> book. You don't have to read this one for Ruby, but do find and read one like it. Such books help bridge the significant gap between what you type and what computers actually do.</li>
<li>
<a href="http://exceptionalruby.com/">Exceptional Ruby</a> teaches you that there's a huge difference between learning a language's syntax for trapping errors and writing code that properly addresses error states. There's a lot of value in spending some time learning about the latter. Read this book if you can't find an equivalent for your language of choice.</li>
<li>
<a href="https://pragprog.com/titles/agcr/confident-ruby/">Confident Ruby: 32 Patterns for Joyful Coding</a>, or a similar title, is essential to learning how to structure code. You don't want to be wasting energy worrying about all the problems a given section of code might have. This book teaches you where to apply the right amount of paranoia, so you can trust the rest of your code.</li>
</ul><h4>Understanding Your Brain</h4>
<p>Learning how to learn is one of the biggest challenges of mastering any skill. A large part of that is figuring out exactly how your brain works.</p>
<ul>
<li>
<a href="https://pragprog.com/titles/ahptl/pragmatic-thinking-and-learning/">Pragmatic Thinking and Learning: Refactor Your Wetware (Pragmatic Programmers)</a> could be called <em>You're Brain: The Owner's Manual</em>. This title got me started learning a vital set of facts about what you can do well, what you can't, and how to compensate for those weaknesses. I've <a href="http://graysoftinc.com/book-reviews/pragmatic-thinking-and-learning">reviewed it in detail</a> here before.</li>
<li>
<a href="https://www.calnewport.com/books/so-good/">So Good They Can't Ignore You: Why Skills Trump Passion in the Quest for Work You Love</a> is the book you need to read when you begin to realize that the traditional advice, "Follow your passion," doesn't actually tell you what to do. This book does. It tells you how to grow, improve, and make plans for career.</li>
</ul><h5>Understanding Motivation</h5>
<p>One aspect of brain mechanics than has been the most helpful to get a handle on is motivation. This has really helped me use my time better and to understand the needs of those I am working with.</p>
<ul>
<li>
<a href="https://www.alfiekohn.org/punished-rewards/">Punished by Rewards: The Trouble with Gold Stars, Incentive Plans, A's, Praise, and Other Bribes</a> is a great place to start decoding motivation if you don't yet know how rewards and punishments affect us. I didn't and I really needed this powerful data dump about what we've observed for over 40 years.</li>
<li>
<a href="https://www.danpink.com/books/drive/">Drive: The Surprising Truth About What Motivates Us</a> is the book you read once you've been convinced that you need to understand this stuff. This covers how motivation really works and gives practical actions we can take to use this knowledge to our advantage.</li>
<li>
<em>Added 10/21/2014</em>: <a href="https://en.wikipedia.org/wiki/Maverick_(book)">Maverick: The Success Story Behind the World's Most Unusual Workplace</a> can be a tough book to find a copy of these days, but it's worth the hunt. Aside from being the ultimate guide to autonomy, it includes managing tips, a nice example of the perils of waterfall development, and some social justice. I love how the author doesn't have all the answers and you get to see him make plenty of mistakes.</li>
<li>
<a href="https://en.wikipedia.org/wiki/Mastery_(book)">Mastery</a> focuses in on just one of the pillars of pure intrinsic motivation. This covers the whole process of achieving mastery in something, starting all the way back at selecting the right something. It even goes into emotional responses likely to trip you up at each stage. It's thorough.</li>
</ul><h4>Communication</h4>
<p>Many of us like to think that we just talk to machines all day long, but that's silly. We pair program, plan features with stakeholders, etc. If you don't know how to communicate, you don't know how to do part of your job. It's that simple.</p>
<ul>
<li>
<a href="https://www.nonviolentcommunication.com/about-marshall-rosenberg/books-and-products/">Nonviolent Communication: A Language of Life</a> honestly contains plenty of material I don't like. It's filled with anecdotal evidence, what it says about praise is half-right at best, and it would have us all talking via a magic formula. That said, everyone should read this book. It's just hard to be aware of how judgmental our language can be, how to talk about emotions, or how to get the intended message across until you do.</li>
<li>
<a href="https://samharris.org/books/lying/">Lying</a> is a book I debated leaving off this list. However, I think it's important to think about concepts like "Lies of Omission" for our communication and even our code. This book will get you asking the right questions.</li>
</ul><h5>Parenting</h5>
<p>If you want to develop a communication A-game, parenting is the big league show. Sadly many, many parenting books are just junk. Here are a couple that aren't.</p>
<ul>
<li>
<a href="https://brainrules.net/brain-rules-for-baby/">Brain Rules for Baby (Updated and Expanded): How to Raise a Smart and Happy Child from Zero to Five</a> has so much to teach. This book could be called <em>The Science of Parenting</em>. It'll tell you how brains develop and what the research says. That's nice, but the real win is how it gets you practicing empathy or understanding praise. Again, this book can make you a better person.</li>
<li>
<a href="https://www.freerangekids.com/book/">Free-Range Kids, How to Raise Safe, Self-Reliant Children (Without Going Nuts with Worry)</a> shows that our natural tendencies are to panic over most things and it's so not worth it. It actually does harm. Reading this helps you decide when you need to worry and when you don't. That lesson helps with programming and life in general.</li>
</ul><p>I reserve the right to add to this list when I discover new tomes of power. I'll note the changes as I make them.</p>James Edward Gray IIIPSC 2014 Postmortemtag:graysoftinc.com,2014-06-26:/posts/1202014-06-28T15:10:28ZA "Thank You" note to the IPSC for all that they retaught me this year.<p>I decided to give the <a href="http://ipsc.ksp.sk/">Internet Problem Solving Contest (IPSC)</a> a go this year, with a couple of friends. I've done it in the past and enjoyed it. I like how it only eats a few hours one day and I like how the variety in the problems they give you keeps things interesting.</p>
<p>That said, my performance in the IPSC this year is probably best described as, "Three strikes and you're out!" I did terrible.</p>
<p>I solved one very simple problem. I spent the rest of the contest chasing after a much harder challenge that I couldn't complete in the time allowed.</p>
<p>The worst part is that I made some silly mistakes that I've learned to avoid in the past. As penance, I offer up this article, mainly as a reminder to myself, but hopefully also as a tool that could save others from some of my folly.</p>
<p>Let's start with a simple mistake I made…</p>
<h4>Not All Problems are Programming Problems</h4>
<p>The IPSC does a great job each year of reminding us that <strong>some problems are trivial to solve without programming</strong>. It's a good thing they do too, because I seem to need a lot of reminding.</p>
<p>A great example of that in this year's contest was <a href="http://ipsc.ksp.sk/2014/real/problems/l.html">the problem that had you listening to sound files</a>. The goal was to get a simple number out of what you heard. In <a href="http://ipsc.ksp.sk/2014/real/problems/l1.mp3">the easier case</a>, a digitized voice is reading ones and zeros for the listener to transcribe.</p>
<p>While you <em>could</em> solve this problem with programming, that's a lot of work. You would need to process the sound data and search for patterns.</p>
<p>Of course, the obvious non-programming solution also has problems. You can just listen to the file, writing down what you hear. But the file goes on for almost 15 minutes and I don't trust my ability to maintain focus and accuracy for that long. It's too error prone and takes too long.</p>
<p>What I really wanted to be able to do was to <em>read the sound</em>. Sadly, I didn't realize until after the contest that I could do exactly that. See for yourself:</p>
<p><img src="http://graysoftinc.com/images/l1_sound_wave.png" alt="L1 Sound Wave"></p>
<p>That's right, open the sound file in most any sound editor and it will show you a graphical representation of what you are hearing. You can play it for a few seconds to spot the shape of the ones and zeros, then stop the sound and literally <em>read</em> the number out of what you see.</p>
<p>I'm super sad that I forgot this because <a href="http://www.devreps.com/">Mandy Moore</a> talks about how she can spot an, "Um," visually, while editing <a href="http://rubyrogues.com/">the Ruby Rogues podcast</a>. I knew better.</p>
<p>The point I want to remember here is that I should be using every tool in my arsenal, not just programming, to solve problems.</p>
<h4>Quitting is a Valid Move</h4>
<p>As I said earlier, I blew most of my problem solving time on <a href="http://ipsc.ksp.sk/2014/real/problems/f.html">a problem that was obviously too much for me</a>. I knew this about an hour into working on it. But I have this problem: I can't quit. This is a disease. Please don't be like me.</p>
<p>I'll give a non-IPSC example to drive this point home. I've been working on some code that displays a grid of images. Items in this grid have some connections drawn between them and other little images drawn around them. These extras kind of break the grid concept, since they can be in multiple cells.</p>
<p>Now I had to write some code to select these cell-spanning extras. This proved pretty challenging, again because of the complexities of the grid they are in. The code got hard. I kept hacking. It got harder. I pushed harder.</p>
<p>My problem, in both of these situations is the same: <em>I keep telling myself that I'll get it.</em> The worst part is that I'm right. I will eventually break through and land on a solution.</p>
<p>However, and this is the super critical realization for me, <strong>that's totally not the point!</strong></p>
<p>I blew about four hours in that contest chasing a problem that I knew was hard. I've blown about two weeks of free time banging my head against this grid selection code. Even if I solved them both right then, I lost.</p>
<p>Let's go back to thinking about that grid problem. I figured out that there are nine special cases that I need to solve. Then I started attacking them. I had six handled at last count. While I can probably get the current approach working, eventually, I don't want to maintain that code! What am I going to do when the first bug report comes in? Pause for three weeks to get back into this code enough to debug the issue? Fail.</p>
<p>The contest is the same. Even if I had scored the solution before the buzzer (which I did not), I would have still been creamed by anyone who went after two or more easier challenges. Their tasks would have been simpler and more rewarding. I was pushing through the pain for less gain. That's just silly.</p>
<p><strong>It's far better to quit.</strong></p>
<p>I've had plenty of time now to re-architect the grid problem to remove the grid itself, which clearly doesn't fit, in favor of a better abstraction. In the contest, I could have quit when I noticed the depth of the problem and still spent three hours solving other problems.</p>
<p>It's so important to learn <a href="http://freakonomics.com/2011/09/30/new-freakonomics-radio-podcast-the-upside-of-quitting/">the value of quitting</a>.</p>
<h4>Teamwork: It's a Thing</h4>
<p>If I had been smart enough to give myself back all that time I wasted, I sure could have made better use of it. One way I could have done that would have been to remember that I was on a team.</p>
<p>The three of us just picked problems and worked pretty much in isolation. While that can be a valid approach, it may not be ideal in all cases.</p>
<p>I've already mentioned that I got hung up on a problem, but my teammates did as well. One spent time <a href="http://ipsc.ksp.sk/2014/real/problems/g.html">trying to work out all of possible scenarios for a simple game</a> while the other was <a href="http://ipsc.ksp.sk/2014/real/problems/k.html">building a crossword solver</a>.</p>
<p>I'm not sure if I could have helped with either case. The game may have been mostly about getting your head around how it works and adding another head to fill may not have made that any easier. Then again, talking out ideas may have.</p>
<p>The crossword didn't look too tough overall, it just had a fair bit of busy work in it. You had to build handlers for each of the clue types. I likely could have contributed there, just by offering to handle some of those types.</p>
<p>But here's the real kicker: <strong>I never asked!</strong></p>
<p>Talk about a rookie mistake. How hard is it to say, "I'm clearly spinning my wheels over here, is there anything I could do that would help you?"</p>
<p>Lessons learned… again… I hope…</p>James Edward Gray IIA Library in One Daytag:graysoftinc.com,2014-05-17:/posts/1172014-05-17T01:42:49ZA new focus I'm trying to cultivate for myself on smaller projects.<p>I was super inspired by <a href="http://tinysubversions.com/2014/05/thoughts-on-small-projects/">Darius Kazemi's recent blog post on small projects</a>, so I've been looking for ways to speed up my process.</p>
<p>Today, I tried an experiment: develop a library in one day. I wanted to go from an empty repository to a published gem that I could start using.</p>
<p><a href="https://github.com/JEG2/signal_lamp">Mission accomplished.</a></p>
<h4>Topic</h4>
<p>Obviously, I had to select a pretty simple idea to use. I wouldn't have time to do a huge project.</p>
<p>I think this may be the killer feature of this technique.</p>
<p>On one hand, you could argue that what I built may not be very library worthy. It's around 50 lines of code. It has ten specs and they really cover what it does. This isn't a complex beast and you could pretty easily hand roll a solution to replace it.</p>
<p>But in some ways that's the best part. I've dropped a 50 line pattern that I like down to a one line <code>Gemfile</code> include. I'm making it even easier for myself to get some mileage out of experimenting with this code. I can mix and match this new library with other small tools to build up the ecosystem that I want for a project. Plus, if it turns out to be something I regret, it's not like I'm tied down to a huge dependency when I go to rip it out. This thinking actually has me <a href="https://github.com/JEG2/signal_lamp#contributing">wanting to keep this library minimal</a>, at least for now.</p>
<h4>Process</h4>
<p>Here are the steps I took today:</p>
<ol>
<li> I began with, and spent the most time on, the README. <a href="http://tom.preston-werner.com/2010/08/23/readme-driven-development.html">This is a good idea.</a> It helped me figure out what I wanted to build and why.</li>
<li> I setup a basic project infrastructure by configuring Bundler, gemifying the project, and preparing to do some TDD. Later I circled back to add some documentation generation helpers.</li>
<li> I wrote the code for the project. This was easily that smallest part of what I did today.</li>
<li> I added documentation for what I had written.</li>
<li> I made various tweaks, cleaned up some construction dust, and published the gem.</li>
<li> I spent a little time tell people that I had made something, by writing this blog post for example.</li>
</ol><p>I'll confess that I had cheated a little on number three. I didn't write any code before today, but having decided that I would be doing this experiment, I've been thinking it through a week or so. I at least had a pretty good idea of what I was going to do. This was partially made possible by the fact that I chose something I could easily keep in my head.</p>
<h4>Project Scaffolding</h4>
<p>It hasn't escaped me that half of the steps I took today somehow involved project setup. The <code>bundle gem</code> command was helpful, but I found myself wanting to do some things a little differently and also wanting a little more help in some areas.</p>
<p>I know there are other tools in Rubyland for this kind of thing, like <a href="http://rubygems.org/gems/hoe">Hoe</a> and <a href="https://rubygems.org/gems/bones">Mr Bones</a>. It might be worth looking into these options.</p>
<p>Alternately this may be one of those areas where if pays to spend a day (no more!) preparing your preferred workflow. I definitely have my own style, so maybe it's worth coding that up.</p>
<p>The point of all of this is that I think I want to find ways to reduce the small friction I felt today even a little further. I like moving at this speed and I want to work hard to keep it up. Steps like this seem to help with that. <a href="https://github.com/dariusk/grunt-init-textgen">Darius even mentioned as much</a> in the article that got me trying this new strategy.</p>James Edward Gray IIWhen Passion Goes Wrongtag:graysoftinc.com,2012-03-11:/posts/1092014-04-25T22:07:15ZThis article is about how we do and should manage the stress inherent in programming.<p>I usually stick to pretty code heavy topics in these articles, but please allow me to take a detour this time. Our industry struggles with a problem that we don't discuss enough and I want to give it some air time.</p>
<p>The fact is, we're pretty lousy at controlling stress.</p>
<p>Let's look at why that is and some of the ways this problem manifests. Remember, the first step is admitting that we have a problem.</p>
<h4>We are a Passionate People</h4>
<p>I really believe good programmers are passionate about what we do. Our job can be pretty mentally taxing and, if you don't love it, it would be pretty rough to endure that day in and day out.</p>
<p>Because of that, we generally find that the programmers who survive the climb are passionate folks. Really think about that for a minute. I'll give some examples.</p>
<p>Kent Beck is a name I bet most of us know. One of his great successes was actually writing a book of guidelines for how individual lines of code should be structured. He had to care about the individual lines. That's how far he had to go to manage his programming. He's also done a ton for testing, for similar reasons.</p>
<p>I'm also pretty sure we all know that David Heinemeier Hansson put Ruby on the map when he created Rails. If you've ever seen DHH talk, you know he's a passionate guy. He was passionate about improving the standard cycle of Web development at that time. He also held himself to some surprisingly rigid standards, like his need to keep the code as DRY (Don't Repeat Yourself) as possible, that led to some of the key breakthroughs at the heart of Rails.</p>
<p>Of course, passion doesn't always require this laser focus. Take Aaron Patterson. He contributes both to the development of Ruby and Rails, but his passion comes out as a wacky fun side that has little to do with code. He wears goofy costumes, makes hilarious videos set to silly music, takes regular pictures of himself giving out hugs to the entire Internet, and more. That's just how Aaron copes.</p>
<p>Obviously, passion, in some form, is important. We need it. It helps us do the challenging stuff. I'm for us being passionate. We want to keep that.</p>
<p>But what are the costs for us being this way?</p>
<h4>Confusing Passion With Stress</h4>
<p>There's absolutely a dark side to all of this passion that we have.</p>
<p>Like most things, I believe it starts out innocently with no one really intending any harm. Things just spiral out from these first steps until we are confused about what's important to us.</p>
<p>He's an example of one of those humble beginnings: "Joe Coder loves slinging code, so I'm sure I can ask him to stay late a bit this week. That will allow us to get a few more features into the big release."</p>
<p>When you use someone's passion like this, you flip if from a positive asset to a painful disadvantage. This may work for a while and, if it's infrequent, it may be hard to see the price you are paying. But there's definitely a price and you shouldn't ever want to pay it.</p>
<p>Imagine these two different scenarios. First, let's say there's a big push looming and your lead developer is struggling to keep up. As a manager, you could come to the developer at 4 PM and say, "Not quite finished yet? Any chance you could stay a little late tonight and wrap this feature up?"</p>
<p>For the second scenario, let's assume the same pressing deadline. This time though, the manager comes in right around 5 PM and says, "You still hammering away at this? Go home. Have some fun with your wife and kids. Come back refreshed tomorrow and you can wrap this up then."</p>
<p>The question is, who gets better and faster results?</p>
<p>Many studies have shown the value of things like rest. Telling ourselves we can function at the same levels without it is just wishful thinking.</p>
<p>Even if our limits weren't fact, we wouldn't want to live like that.</p>
<h4>Our Best Intentions</h4>
<p>Sometimes our passion even manufactures more stress. A lot of us like to help out on open source projects, for example. That's great and noble, but it can become challenging over time.</p>
<p>Things like bug reports and feature requests can be extra stress sent to you on someone else's schedule. That's not comfortable to very many people.</p>
<p>I'm not saying we should give these things up. I love open source and I don't ever want to live without it. I contribute when I can and I encourage all of you to do the same.</p>
<p>We do need to remember to always keep these efforts in the highest respect though. Don't get too heated when it takes someone else time to address your concerns.</p>
<p>Similarly, we have to filter the things people tell us through the lens of reality. I know I have personally said things like, "New programmers need to be eating and sleeping code." I did not mean to imply that they should be programming around the clock. I just meant that their stage of development is better passed through by writing code than reading our books or other similar tasks. Don't let others push you into this stress framework. Especially not me.</p>
<h4>Death to Death Marches</h4>
<p>I'm interested in this topic because I've seen enough of it to pass judgement now.</p>
<p>On one software project, I literally watched the lead developer's physical health tank as the project managers pushed harder and harder. He began to have bizarre episodes where various systems of his body would fail in a sometimes drastic manner. The doctors struggled to explain these episodes, because there didn't seem to be an underlying cause. Want to know the really sad part? If you overlaid these episodes on the project's release calendar it was an almost perfect match.</p>
<p>Another project I worked on seemed to operate under the assumption that if our hair wasn't on fire, we were doing it wrong. It was an end to end death march. They would give us some assignment and explain the critical timing of getting it in. We would negotiate a reasonable feature set for that time frame, then set to work. Then something else, even more critical, would come up or management would decide our progress showed we weren't going to make it. They probably cancelled around 50% of what we were working on before we could ship it. That's powerfully demoralizing and it threw one of the cheeriest developers I've ever met into a depression.</p>
<p>Hopefully it's more obvious that we've crossed a line at this level. That should make it easier to avoid.</p>
<p>I do vote that we treat this as a hard line in the sand that can't be crossed. You know the saying about counter-offers? "Good companies don't offer them and smart employees don't take them." We need to apply the same no-tolerance policy to death marches.</p>
<h4>Is it Really the End of the World?</h4>
<p>It's worth noting that we're not just talking about work schedules here either. This passion gone wrong infects many aspects of our industry. One area I see it a lot is thinking that any problem is the end of the world.</p>
<p>It's almost always not the end. Really.</p>
<p>Let's talk about Amazon.com. They're huge. Now their architecture is constructed to make full outages extremely unlikely, but let's say their luck goes sour and they really do go all the way down. Maybe they are down for a day. Users cannot make orders. For a whole day.</p>
<p>Does this matter? Would it hurt them?</p>
<p>I seriously doubt it. In fact, I suspect their orders would be artificially high the moment the came back up as the outage urges some users to get their needs seen to when they can. That's what happens anytime the Apple store is offline for 30 seconds, right?</p>
<p>Furthermore, Amazon.com is going to spin it great. That's what they do. They're going to apologize, go to great lengths to tell us what went wrong, and assure us that they've greatly reduced the danger of it happening again. They've done exactly that in the past. It gets them positive press.</p>
<p>If Amazon.com tanking isn't the end of the world, it's just not likely much else is either. Sure, there are some mission critical systems, but those are just super rare exceptions.</p>
<p>One day, while I was at lunch with my wife and a friend, one of my clients called in a dead panic. He was sorry to disturb me, but this was a true emergency. He explained the problem in painful detail and I calmly reassured him that I would handle it.</p>
<p>I'm so glad this call happened when it did, because I got to see how truly absurd we are through the eyes of another. You could tell by the look on my friend's face, who only heard the gentle reassuring side of my conversation, that she expected me to leap into immediate action. I seriously doubt she would have been surprised if I had stolen a nearby laptop, fought my way to a power plug, and started frantically coding. I'm pretty sure she thought lives were on the line.</p>
<p>When I calmly put the phone down, apologized for the interruption, and went back to eating and chatting, the friend had to ask me if I needed to go. I told her not to worry, everything was fine.</p>
<p>Was it really fine?</p>
<p>Yep. I had done some quick and dirty estimation while on the call. The problem was in the order system of a moderate traffic site. That's the worst it can get right? I mean credit cards were involved. The fact is, assuming the worst possible scenario, I figured there could be 20 messed up orders by the time I made it back from lunch. The client has also told me they had stopped filling orders until this was sorted out. So the worst case scenario was that we might need to revert some charges and send some apology emails. Maybe 20 of them. That was the quick and dirty assessment that kept me from panicking.</p>
<p>The reality was even better. There was an issue with the order system. It was an extreme edge case that only one user had managed to trigger with some pretty odd behavior. That's the order the client found. No other orders had been affected. Oh and that type of order required approval, so it hadn't gone far enough in the process to even be charged yet. The safeguards we had in place were doing their job perfectly. Count of abused credit cards: 0.</p>
<p>I'm very glad I didn't ruin my lunch over that.</p>
<p>There are so few problems in the world that cannot wait until Monday morning. Assume yours isn't one of them. It's a superior position to play from.</p>
<h4>Show me the Money</h4>
<p>Speaking of credit cards and money… well, let's talk about that too. I had a conversation last week that went something like this:</p>
<p>Startup employee: "Our lead developer quit this week."</p>
<p>Me: "Sorry to hear that. They weren't happy with the job?"</p>
<p>Startup employee: "They just ran out of runway. Their wife lost their job, so they couldn't hold out any longer."</p>
<p>This type of thinking is so alien to me that it took me a while to realize what was even being said. That programmer was apparently working for little or no money, and promises of future equity, in order to help get this idea off the ground. Circumstances changed and he couldn't keep doing that.</p>
<p>Of course, I've known multiple people who do this. They are odd to converse with, if you ask me.</p>
<p>Me: "You're not getting paid for this?"</p>
<p>Typical answers: "We're changing the world;" "they've created an amazing culture to work in;" etc.</p>
<p>Uh, no. All definitions of "an amazing culture to work in" begin with, "Here, let me pay you for your work." I promise. Oh and those people who end up changing the world are usually paid for doing so. Sometimes a lot.</p>
<p>If you are about to object with Steve Jobs, Twitter, Facebook, or some other amazing startup case, just stop. Put the keyboard down. Yes, there are some stories of businesses happening like that, but you don't want to actually aim to be like them. The odds are horrible. Go to a casino and bet everything you have on one lucky turn of the wheel. The odds are better. If you are going to "start up" a business, why would you aim for the bad odds? You want to learn the rules and play the game right like everyone else does, gradually building something that works.</p>
<p>Get paid for what you do, please.</p>
<h4>An Amazing Culture to Work In</h4>
<p>The startup folks have nailed one thing: we can build the culture we want to work in. We have the power to make it whatever we want. Managers can shape the environment by how they run things and programmers need to start politely but firmly rejecting unacceptable conditions.</p>
<p>Here are some modest suggestions for what I would like to see:</p>
<ul>
<li>Get paid for your work</li>
<li>Quit working around 5 PM</li>
<li>Take lunch breaks</li>
<li>Don't work weekends</li>
<li>No death marches, ever</li>
<li>Remember that our current problems are not the end of the world and behave accordingly</li>
</ul><p>Don't get me wrong though. I'm keeping the passion. Very few people love to program like I do and I'm not giving that up. I've got more than my fair share of the "Go Big or Go Home" attitude. When it's time to code, bring your A Game and give it your all.</p>
<p>I'm just recommending that we remember to properly delineate and respect when those times really are.</p>James Edward Gray IIPragmatic Thinking & Learningtag:graysoftinc.com,2009-02-08:/posts/782021-08-22T16:27:40ZMy take on the pragmatic guide to your brain and how it works.<p>I have a new standard by which all future reading material will measured. Any book that casually mentions lock picking and follows it up with a footnote reference to further reading that will improve your lock picking skills when restricted to an improvised toolset is an instant hit. <a href="http://www.pragprog.com/titles/ahptl">Pragmatic Thinking & Learning</a> does exactly that. While that description may be a bit of hyperbole (I had to look that word up Andy), the book really does deliver, both on the lock picking references and the great content.</p>
<p>If I had to sum up Pragmatic Thinking & Learning in one sentence it would be: it's a book about how to start thinking about thinking, with a moderate computer programmer slant. If that sounds a bit general, well, it is. A construction worker or anyone else could learn new things that would help them in their jobs and just day to day lives from this book. I know I would love for my teenage foreign exchange student to read it, because she could learn a lot from it. I'm pretty sure this book does exist in many other forms targeted at different groups of people. The advantage of this one is that I get the jokes and metaphors. Hooray for geek humor!</p>
<p>Pragmatic Thinking & Learning is very simple in content. It teaches you a lot of the basics about how we think and learn, then gives a boat load of suggestions about how you might use some of this to your advantage. Let's start with the thinking.</p>
<p>A major focus of this book is the <a href="http://en.wikipedia.org/wiki/Dreyfus_model_of_skill_acquisition">Dreyfus Model of Skill Acquisition</a>, which classifies the stages we go through as we acquire new skills. The book goes into quite a bit of detail on how to recognize these stages and what individuals need when they are in each stage.</p>
<p>Another major focus of the classification material is on the two primary modes of thinking used by the human brain. There's a lot of detail on the strengths of each mode and how they do and don't work together.</p>
<p>Lesser topics in this area include finding your <a href="http://en.wikipedia.org/wiki/Myers-Briggs_Type_Indicator">Myers-Briggs Type Indicator</a> and your <a href="http://surfaquarium.com/MI/inventory.htm">Multiple Intelligence Inventory</a>, other systems of describing your thought processes.</p>
<p>All of this adds up to make you more aware of how you, and those you interact with, think. It's early Psychology material, but the truth is that it's damn helpful to know even if you're not a shrink. The author makes a joke that if you have a brain, you can probably get something out of knowing how it works. I seriously inclined to agree.</p>
<p>To give an example, I've had multiple programming students over the years. Some lessons with some students went very well, but others went poorly. I could see that some worked and some didn't, but I had no idea why that was. Now I do. I can see which stages of learning my students must have been in at the time and then compare what I was giving them verses what they really needed. In some cases I was way off target. Would you believe those are the lessons that went badly? Shocking, I know. This insight into others is already helping me better understand the people I interact with. I'm trying to understand their needs more and think about how I could adapt to that.</p>
<p>This also makes it much easier to understand what you are going through at times. You know how people always say things like, "I have to do this because it's what works for me." All of these classifications help you understand why it is what works for you. If you are aware of that, you might even be able to make things work better for you. That's the whole point, of course, and that's really what the other half of the book is about.</p>
<p>After you know how we think and learn, you will ask that favored question of mothers and dictators the world over: how can I exploit this knowledge? Pragmatic Thinking & Learning includes 1,264 answers to that question. Yes, I counted.</p>
<p>Really, it gives you tips. A lot of tips. However many tips you have in mind after reading these last two sentences, it's more than that. Are they all great tips? In a way, yes. See, the book really hammers home one major concept: context matters. Given that, in the context of you personally, different things are going to work. Thus, you've got to try a lot of things, shop around a bit as it were, and see what fits you. I'll be shocked if something in the included tips won't. That's why you need so many.</p>
<p>Will drawing a picture upside down help you improve your use of the two separate thinking modes in your brain? I don't think it helped me very much. The jury is also still out on whether or not mind mapping is improving my ability to see relationships and whether or not mediative breathing is improving my concentration throughout the day.</p>
<p>However, the list of changes in me inspired by this book that already seem to be having a positive effect is large. Keeping key pieces of data in my line of sight is definitely helping me maintain focus on concepts that are important to me; a personal wiki has enhanced my brainstorming abilities; I'm getting better at explaining what I'm doing in metaphors to people who have no idea what I'm talking about and I believe that's improving my communication skills; my newly scheduled low distraction times are already my favorite part of the week. The list goes on and on.</p>
<p>I even already had some good habits that are suggestions from this book, like an <a href="http://www.studygs.net/texred2.htm">SQ3R reading strategy</a> and using virtual desktops to minimize context switching. It was neat to read about them and realize I had figured that out for myself.</p>
<p>There's no shortage of things to try here and I think the odds are good that some of them will help your thinking processes. If you need to figure out why some habit aren't working well for you or just figure out what you can do better, this should definitely help.</p>
<p>I said before that I think a lot of this is early Psychology material, but if you are looking to dig deeper this book has a super rich list of references you could use to continue your studies. That said, I think you can stop with this book and still be a wiser person. It stands alone just fine.</p>
<p>So again, if you have a brain, this book has something to offer you. It's not just a joke.</p>
<p><a href="https://pragprog.com/titles/ahptl/pragmatic-thinking-and-learning/">Pragmatic Thinking & Learning</a></p>James Edward Gray IIDoing It All!tag:graysoftinc.com,2006-11-07:/posts/232014-04-03T22:50:02ZHere's my best non-programming tip for all programmers.<p>I'm not really in the habit of putting non-code content on this blog, but more than one person asked me the same question at RubyConf. If people really want to know, I'll try to answer. Paraphrasing, the question was:</p>
<blockquote>
<p>How do you keep up with so many Ruby projects?</p>
</blockquote>
<p>First, this question surprised me. Do I really do that much? If you just said yes to that, I would like to introduce you to <a href="http://www.zenspider.com/">Ryan Davis</a>. He easily doubles my output and his projects are wicked complex compared to mine.</p>
<p>That doesn't answer the question though.</p>
<p>In short, I do as much as I possibly can with the time I have. The truth is that I would like to do a lot more. I turn down at least as many damn cool Ruby projects as I accept because I'm a wimp and not willing to give up my sleep. There are so many crazy cool Ruby projects out there that I would love to be a part of. There just aren't enough hours in the day.</p>
<p>I guess I still didn't answer the question.</p>
<p>The question was "How…" and the answer to that is actually trivial. Masayoshi Takahashi summed it up with a single slide in his presentation at RubyConf:</p>
<blockquote>
<p>Passion Matters</p>
</blockquote>
<p>Definitely.</p>
<p>If we were talking about working in a coal mine for sixteen hours a day, I would probably be a lot less capable. It just so happens that I love what I do. If anything gives me extra energy to devote to Ruby projects it's that. Love what you do. I can't stress that enough.</p>
<p>Beyond that, don't hesitate to get involved! I can't tell you how often I see people say things like, "I'm not really qualified to do that," or similar excuses. Oh hell, neither am I, but I wouldn't let a little thing like that stop me! You learn as you go, you drag in the help you need, or whatever. Passion will conquer so care enough to have some. Be the driving force and the rest will take care of itself.</p>
<p>That's all the advice I have to give, I fear. My secret formula probably seems bland, but if I <em>accomplish the work of ten men</em> (someone said that to me a while back), the reason is that I'm passionate and fearless.</p>
<p>Oh, and I have the most understanding wife on this planet. Get one of those too.</p>James Edward Gray II