A place to cache linked articles (think custom and personal wayback machine)
Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

index.md 13KB

5 år sedan
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. title: Every line of code is always documented
  2. url: https://mislav.net/2014/02/hidden-documentation/
  3. hash_url: d92f0d39965b2b41d2701d3a6a114c3c
  4. <p>Every line of code comes with a hidden piece of documentation.</p>
  5. <p>Whoever wrote line 4 of the following code snippet decided to access the
  6. <code>clientLeft</code> property of a DOM node for some reason, but do nothing with the
  7. result. It&rsquo;s pretty mysterious. Can you tell why they did it, or is it safe to
  8. change or remove that call in the future?</p>
  9. <div class="highlight"><pre><code class="language-js" data-lang="js"><span class="lineno">1</span> <span class="c1">// ...</span>
  10. <span class="lineno">2</span> <span class="k">if</span> <span class="p">(</span><span class="nx">duration</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="k">this</span><span class="p">.</span><span class="nx">bind</span><span class="p">(</span><span class="nx">endEvent</span><span class="p">,</span> <span class="nx">wrappedCallback</span><span class="p">)</span>
  11. <span class="lineno">3</span>
  12. <span class="lineno">4</span> <span class="hll"><span class="k">this</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="mi">0</span><span class="p">).</span><span class="nx">clientLeft</span>
  13. </span><span class="lineno">5</span>
  14. <span class="lineno">6</span> <span class="k">this</span><span class="p">.</span><span class="nx">css</span><span class="p">(</span><span class="nx">cssValues</span><span class="p">)</span></code></pre></div>
  15. <p>If someone pasted you this code, like I did here, you probably won&rsquo;t be able to
  16. tell who wrote this line, what was their reasoning, and is it necessary to keep
  17. it. <em>However</em>, most of the time when working on a project you&rsquo;ll have access to
  18. its history via version control systems.</p>
  19. <p><strong>A project&rsquo;s history is its most valuable documentation.</strong></p>
  20. <p>The mystery ends when we view the commit message which introduced this line:</p>
  21. <div class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>git show <span class="k">$(</span>git blame example.js -L 4,4 <span class="p">|</span> awk <span class="s1">&#39;{print $1}&#39;</span><span class="k">)</span></code></pre></div>
  22. <blockquote><p><strong>Fix animate() for elements just added to DOM</strong></p>
  23. <p>Activating CSS transitions for an element just added to the DOM won&rsquo;t work in
  24. either Webkit or Mozilla. To work around this, we used to defer setting CSS
  25. properties with setTimeout (see 272513b).</p>
  26. <p>This solved the problem for Webkit, but not for latest versions of Firefox.
  27. Mozilla seems to need at least 15ms timeout, and even this value varies.</p>
  28. <p>A better solution for both engines is to trigger &ldquo;layout&rdquo;. This is done here
  29. by reading <code>clientLeft</code> from an element. There are other properties and
  30. methods that trigger layout; see
  31. <a href="http://gent.ilcore.com/2011/03/how-not-to-trigger-layout-in-webkit.html">gent.ilcore.com/2011/03/how-not-to-trigger-layout-in-webkit</a></p></blockquote>
  32. <p>As it turns out, this line—more specifically, the <em>change</em> which introduced this
  33. line—is <strong>heavily documented</strong> with information about why it was necessary, why did
  34. the previous approach (referred to by a commit SHA) not work, which browsers are
  35. affected, and a link for further reading.</p>
  36. <p>As it also turns out, <a href="https://github.com/madrobby/zepto/pull/586">the author of that mysterious line was me</a>. There are
  37. ways I could have written that code itself better: by <strong>encapsulating the magic
  38. property access</strong> in a function with an <a href="http://signalvnoise.com/posts/3531-intention-revealing-methods">intention-revealing name</a> such as
  39. <code>triggerLayout()</code>, or at least by <strong>adding a code comment</strong> with a short
  40. explanation that this kicks off the animation. For whatever reason, I might have
  41. failed that day to make this particular code expressive. <strong>Code happens, and
  42. it&rsquo;s not always perfect</strong>.</p>
  43. <p>Even if this code <em>was</em> more expressive or if it <em>had</em> contained lines of code
  44. comments, a project&rsquo;s history will be able to provide even richer information:</p>
  45. <ol>
  46. <li><em>Who</em> added this code;</li>
  47. <li><em>When</em> did they add this code;</li>
  48. <li>Which was the <em>accompanying test</em> (if any);</li>
  49. <li>The full commit message can be a whole novel (while code comments should be
  50. kept succinct).</li>
  51. </ol>
  52. <p>Code quality still matters a lot. But when pondering how you could improve your
  53. coding even further, you should consider aiming for better commit messages. You
  54. should request this not just from yourself, but from your entire team and all
  55. the contributors. <strong>The story of a software matters as much as its latest
  56. checkout</strong>.</p>
  57. <h2>Effective spelunking of project&rsquo;s history</h2>
  58. <h3>git blame</h3>
  59. <p>I&rsquo;ve already demonstrated how to use <code>git blame</code> from the command line above.
  60. When you don&rsquo;t have access to the local git repository, you can also open the
  61. &ldquo;Blame&rdquo; view for <a href="https://github.com/madrobby/zepto/blame/2ed0123eaddc023a8579df0a3a084a70a392d792/src/fx.js#L90">any file on GitHub</a>.</p>
  62. <p>A very effective way of exploring a file&rsquo;s history is with Vim and <a href="https://github.com/tpope/vim-fugitive" title="fugitive.vim: a Git wrapper so awesome, it should be illegal">Fugitive</a>:</p>
  63. <ol>
  64. <li>Use <code>:Gblame</code> in a buffer to open the blame view;</li>
  65. <li>If you need to go deeper, press <kbd>Shift-P</kbd> on a line of blame pane to
  66. re-blame at the parent of that commit;</li>
  67. <li>Press <kbd>o</kbd> to open a split showing the commit currently selected in
  68. the blame pane.</li>
  69. <li>Use <code>:Gbrowse</code> in the commit split to open the commit in the GitHub web interface;</li>
  70. <li>Press <kbd>gq</kbd> to close the blame pane and return to the main buffer.</li>
  71. </ol>
  72. <p><img width=827 height=445 style="max-width:100%" alt="git blame view in vim Fugitive"
  73. src="https://mislav.net/images/Screen%20Shot%202014-02-07%20at%203.38.20%20PM.png"></p>
  74. <p>See <code>:help Gblame</code> for more information.</p>
  75. <h3>Find the pull request where a commit originated</h3>
  76. <p>With git blame you might have obtained a commit SHA that introduced a change,
  77. but commit messages don&rsquo;t always carry enough information or context to explain
  78. the rationale behind the change. However, if the team behind a project practices
  79. <a href="http://guides.github.com/overviews/flow/" title="Lightweight, branch-based workflow that supports teams and projects where deployments are made regularly">GitHub Flow</a>, the context might be found in the pull request discussion:</p>
  80. <div class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>git log --merges --ancestry-path --oneline &lt;SHA&gt;..origin <span class="p">|</span> tail
  81. ...
  82. <span class="hll">bc4712d Merge pull request <span class="c">#42 from sticky-sidebar</span>
  83. </span>3f883f0 Merge branch <span class="s1">&#39;master&#39;</span> into sticky-sidebar</code></pre></div>
  84. <p>Here, a single commit SHA was enough to discover that it originated in pull
  85. request #42.</p>
  86. <h3>The git pickaxe</h3>
  87. <p>Sometimes you&rsquo;ll be trying to find something that is missing: for instance, a
  88. past call to a function that is no longer invoked from anywhere. The best way to
  89. find which commits have introduced or removed a certain keyword is with the
  90. &lsquo;pickaxe&rsquo; argument to <code>git log</code>:</p>
  91. <pre><code>$ git log -S&lt;string&gt;
  92. </code></pre>
  93. <p>This way you can dig up commits that have, for example, removed calls to a
  94. specific function, or added a certain CSS classname.</p>
  95. <h3>git churn</h3>
  96. <p>It&rsquo;s possible to get valuable insight from history of a project not only by
  97. viewing individual commits, but by <strong>analyzing sets of changes as a whole</strong>. For
  98. instance, <a href="https://github.com/garybernhardt/dotfiles/blob/f0c0ff92209e5aed4fa3ef6faf056eb9944a8f12/bin/git-churn">git-churn</a> is a simple but valuable script that wraps
  99. <code>git log</code> to compile stats about which files change the most. For example, to
  100. see where the development of an app was focused on in the past 6 months:</p>
  101. <pre><code>$ git churn --since='6 months ago' app/ | tail
  102. </code></pre>
  103. <p>Incidentally, such analysis also highlights potential problems with technical
  104. debt in a project. A specific file changing too often is generally a red flag,
  105. since it probably means that the code in that file either needed to be
  106. frequently fixed for bugs, or that the file holds too much responsibility in
  107. general and should be split into smaller units.</p>
  108. <p>Similar methods of history analysis can be employed to see which people were
  109. responsible recently for development of a certain part of the codebase. For
  110. instance, to see who contributed most often to the API part of an application:</p>
  111. <pre><code>$ git log --format='%an' --since='6 months ago' app/controllers/api/ | \
  112. sort | uniq -c | sort -rn | head
  113. 109 Edmond Dantès
  114. 13 Jonathan Livingston
  115. 7 Ebanezer Scrooge
  116. </code></pre>
  117. <h2>Being on the right side of history</h2>
  118. <p>Keep in mind that everything that you&rsquo;re making today is going to enter the
  119. project&rsquo;s history and stay there forever. To be nicer to other people who work
  120. with you (even if it&rsquo;s a solo project, that includes yourself in 3 months),
  121. follow these ground rules when making commits:</p>
  122. <ul>
  123. <li><p>Always write commit messages as if you are <strong>explaining the change</strong> to a
  124. colleague sitting next to you who has no idea of what&rsquo;s going on. Per
  125. <a href="http://robots.thoughtbot.com/5-useful-tips-for-a-better-commit-message" title="5 Useful Tips For A Better Commit Message">Thoughtbot&rsquo;s tips for better commit messages</a>:</p>
  126. <blockquote><p>Answer the following questions:</p>
  127. <ul>
  128. <li>Why is this change necessary?</li>
  129. <li>How does it address the issue?</li>
  130. <li>What side effects does this change have?</li>
  131. <li>Consider including a link [to the discussion.]</li>
  132. </ul>
  133. </blockquote></li>
  134. <li><p><strong>Avoid unrelated changes in a single commit</strong>. You might have spotted a typo
  135. or did tiny code refactoring in the same file where you made some other changes,
  136. but resist the temptation to record them together with the main change unless
  137. they&rsquo;re directly related.</p></li>
  138. <li><p><strong>Always be cleaning up your history before pushing</strong>. If the commits haven&rsquo;t
  139. been shared yet, it&rsquo;s safe to <a href="/2013/02/merge-vs-rebase/" title="Git merge vs. rebase">rebase the heck out of them</a>. The
  140. following could have been permanent history of the Faraday project, but I
  141. squashed it down to only 2 commits and edited their messages to hide the fact
  142. I had troubles setting the script up in the first place:</p>
  143. <p><img width=470 style="max-width:100%" alt="messy git history before rebase"
  144. src="https://mislav.net/images/Image%202013-04-04%20at%201.38.33%20AM.png"></p></li>
  145. <li><p>Corollary of avoiding unrelated changes: <strong>stick to a line-based coding
  146. style</strong> that allows you to append, edit or remove values from lists without
  147. changing adjacent lines. Some examples:</p>
  148. <pre><code> var one = "foo"
  149. , two = "bar"
  150. , three = "baz" // Comma-first style allows us to add or remove a
  151. // new variable without touching other lines
  152. # Ruby:
  153. result = make_http_request(
  154. :method =&gt; 'POST',
  155. :url =&gt; api_url,
  156. :body =&gt; '...', // Ruby allows us to leave a trailing comma, making it
  157. ) // possible to add/remove params while not touching others
  158. </code></pre>
  159. <p>Why would you want to use such coding styles? Well, always think about the
  160. person who&rsquo;s going to <code>git blame</code> this. In the JavaScript example, if you were
  161. the one who added a committed the value <code>"baz"</code>, you don&rsquo;t want your name to
  162. show up when somebody blames the line that added <code>"bar"</code>, since the two
  163. variables might be unrelated.</p></li>
  164. </ul>
  165. <h2>Bonus script</h2>
  166. <p>Since you&rsquo;ve read this far, I&rsquo;ll reward you with an extra script. I call it
  167. <a href="https://github.com/mislav/dotfiles/blob/7ac8cbfcd56cfa6c39b5719ea183e87878ea6ed5/bin/git-overwritten">git-overwritten</a> and it shows blame information about original authors of
  168. lines changed or removed in a given branch:</p>
  169. <pre><code>$ git overwritten feature origin/master
  170. </code></pre>
  171. <pre class='ansi'> 28 2014-02-04 <span class='ansi-0 ansi-yellow'>1fb2633</span> <span class='ansi-0 ansi-green'>Mislav Marohnić</span>: Add Makefile for building and testing
  172. 1 2014-01-13 <span class='ansi-0 ansi-yellow'>b2d896a</span> <span class='ansi-0 ansi-green'>Jingwen Owen Ou</span>: Add -t to mktemp in script/make
  173. 17 2014-01-07 <span class='ansi-0 ansi-yellow'>385ccee</span> <span class='ansi-0 ansi-green'>Jingwen Owen Ou</span>: Add script/make for homebrew build
  174. </pre>
  175. <p>This is useful when opening pull requests per <a href="http://guides.github.com/overviews/flow/" title="Lightweight, branch-based workflow that supports teams and projects where deployments are made regularly">GitHub Flow</a>; you&rsquo;ll want your
  176. pull request reviewed by colleagues but you might not be sure who to ping. With
  177. <code>git-overwritten</code> you&rsquo;ll get the names of people who wrote the lines you just
  178. changed, so you&rsquo;ll know who to @-mention when opening a pull request.</p>