<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>プログラミング - Yifeitao ｜ 次生進化</title><link>https://yifeitao.com/ja/tags/%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0/</link><description>創造と学習と投資について</description><generator>Hugo -- gohugo.io</generator><language>ja</language><lastBuildDate>Tue, 17 Jan 2017 00:00:00 +0000</lastBuildDate><atom:link href="https://yifeitao.com/ja/tags/%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0/index.xml" rel="self" type="application/rss+xml"/><item><title>高可配置性ソフトウェア開発</title><link>https://yifeitao.com/ja/development-of-highly-configurable-software/</link><pubDate>Tue, 17 Jan 2017 00:00:00 +0000</pubDate><guid>https://yifeitao.com/ja/development-of-highly-configurable-software/</guid><description>&lt;p&gt;可配置性はソフトウェア工学の実践における重要な問題の一つである。設定ファイルは実行ファイルとともに、ソフトウェアの実行時バージョン管理を構成する要素となる。Visual Basic、Python、Lua などのインタプリタ型言語は、いわゆる「スクリプト」の形で現れることが多く、オペレーティングシステムやゲームなどの大規模ソフトウェアの可配置性をさらに高めてきた。また、ドメイン駆動設計はドメインモデルを強調すると同時に、設定ドメインモデルを記述するドメイン固有言語の発展も促した。産業制御の分野では、ソフトウェアの可配置性は組態（コンフィギュレーション）という概念で語られる。ゲーム開発の分野では、一般にエンジンとスクリプトという概念で語られる。&lt;/p&gt;
&lt;h2 id="1-基本原理"&gt;1 基本原理&lt;/h2&gt;
&lt;p&gt;ソフトウェアの複雑さの根源は現実世界の複雑さにある。ソフトウェアの複雑さが人の理解能力を超えると、ソフトウェア開発の困難、さらには失敗をもたらす。優れたソフトウェア工学の方法論は現実世界の良好な抽象モデルを与え、合理的な抽象階層構造によってソフトウェアの複雑さを管理し、人がある全体から細部へと階層的にソフトウェアを把握できるようにする。現実世界に由来する複雑さは減少も消滅もしないが、制約を受けることになる。&lt;/p&gt;
&lt;p&gt;高可配置性は、急速な要求変更の問題をある程度解決できる。ソフトウェアの中で不変の部分をモデルとして捉え、変わりやすい部分を設定ファイルなどの方法で制約するのである。&lt;/p&gt;
&lt;pre class="mermaid"&gt;graph TB
subgraph ソフトウェア1
complexity2[複雑性]--&amp;gt;abstract[共通抽象]
end
subgraph ソフトウェア
complexity1[複雑性]--&amp;gt;complexity3[複雑性]
end&lt;/pre&gt;
&lt;h2 id="2-構築方法"&gt;2 構築方法&lt;/h2&gt;
&lt;p&gt;高可配置性ソフトウェアを構築するには、まずドメインの特徴に応じて汎用ソフトウェアモデルを確立し、次にそのモデルに対して設定技術を用いる。異なる設定とモデルの組み合わせが、最終的に異なるソフトウェアインスタンスを構築する。&lt;/p&gt;
&lt;pre class="mermaid"&gt;graph TB
subgraph ソフトウェア2
config2[ソフトウェア設定2]--&amp;gt;abstract2[汎用モデル]
end
subgraph ソフトウェア1
config1[ソフトウェア設定1]--&amp;gt;abstract1[汎用モデル]
end&lt;/pre&gt;
&lt;h3 id="21-モデリング"&gt;2.1 モデリング&lt;/h3&gt;
&lt;p&gt;モデリングの問題は非常に複雑な問題であり、設計時のシナリオによって異なる。ドメイン知識と設計経験を必要とし、ドメインの専門家と密接に協力する必要がある。この分野にはいくつかの古典がある：&lt;a href="https://book.douban.com/subject/26819666/"
rel="noopener noreferrer" target="_blank" class="external"&gt;『エリック・エヴァンスのドメイン駆動設計』&lt;/a&gt;
と&lt;a href="https://book.douban.com/subject/25844633/"
rel="noopener noreferrer" target="_blank" class="external"&gt;『実践ドメイン駆動設計』&lt;/a&gt;
。&lt;/p&gt;
&lt;h3 id="22-設定技術"&gt;2.2 設定技術&lt;/h3&gt;
&lt;h4 id="221-設定ファイル"&gt;2.2.1 設定ファイル&lt;/h4&gt;
&lt;p&gt;設定ファイルは最も広く使われ、最も基本的な設定技術である。設定ファイルは主にソフトウェアの起動および実行に必要なデータを記述するために用いられ、一般にソフトウェアの実行ロジックを直接記述するものではない。一般的な設定ファイル形式には ini、xml、json などがある。簡単な比較を下表に示す。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;形式&lt;/th&gt;
&lt;th&gt;表現力&lt;/th&gt;
&lt;th&gt;可読性&lt;/th&gt;
&lt;th&gt;典型的な用途&lt;/th&gt;
&lt;th&gt;例&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ini&lt;/td&gt;
&lt;td&gt;弱い、ネスト不可&lt;/td&gt;
&lt;td&gt;最も良い&lt;/td&gt;
&lt;td&gt;ソフトウェアの起動パラメータ。&lt;/td&gt;
&lt;td&gt;起動ディレクトリ、IP アドレス。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;xml&lt;/td&gt;
&lt;td&gt;最も強い&lt;/td&gt;
&lt;td&gt;最も悪い&lt;/td&gt;
&lt;td&gt;複雑なオブジェクトのシリアライズ。&lt;/td&gt;
&lt;td&gt;データフレームの構造と解析規則。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;json&lt;/td&gt;
&lt;td&gt;普通&lt;/td&gt;
&lt;td&gt;普通&lt;/td&gt;
&lt;td&gt;Web API インタフェース。&lt;/td&gt;
&lt;td&gt;天気予報サービスのインタフェース。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 id="222-スクリプト言語"&gt;2.2.2 スクリプト言語&lt;/h4&gt;
&lt;p&gt;大規模なプログラムは、しばしば複数のプログラミング言語を組み合わせて実現される。一般に、コンパイル型言語でシステムの比較的固定された部分と実行効率が重要な部分を実装し、インタプリタ型言語でシステムの変わりやすい部分を実装する。たとえば Linux オペレーティングシステムは C で多くのツールを実装し、bash スクリプトでそれらのツールを総合的に組み合わせて応用する。大規模ゲームは C++ のゲームエンジン上に構築され、Lua スクリプトなどでゲームの動作ロジックを記述する。&lt;/p&gt;</description><content:encoded><![CDATA[<p>可配置性はソフトウェア工学の実践における重要な問題の一つである。設定ファイルは実行ファイルとともに、ソフトウェアの実行時バージョン管理を構成する要素となる。Visual Basic、Python、Lua などのインタプリタ型言語は、いわゆる「スクリプト」の形で現れることが多く、オペレーティングシステムやゲームなどの大規模ソフトウェアの可配置性をさらに高めてきた。また、ドメイン駆動設計はドメインモデルを強調すると同時に、設定ドメインモデルを記述するドメイン固有言語の発展も促した。産業制御の分野では、ソフトウェアの可配置性は組態（コンフィギュレーション）という概念で語られる。ゲーム開発の分野では、一般にエンジンとスクリプトという概念で語られる。</p>
<h2 id="1-基本原理">1 基本原理</h2>
<p>ソフトウェアの複雑さの根源は現実世界の複雑さにある。ソフトウェアの複雑さが人の理解能力を超えると、ソフトウェア開発の困難、さらには失敗をもたらす。優れたソフトウェア工学の方法論は現実世界の良好な抽象モデルを与え、合理的な抽象階層構造によってソフトウェアの複雑さを管理し、人がある全体から細部へと階層的にソフトウェアを把握できるようにする。現実世界に由来する複雑さは減少も消滅もしないが、制約を受けることになる。</p>
<p>高可配置性は、急速な要求変更の問題をある程度解決できる。ソフトウェアの中で不変の部分をモデルとして捉え、変わりやすい部分を設定ファイルなどの方法で制約するのである。</p>
<pre class="mermaid">graph TB
    subgraph ソフトウェア1
        complexity2[複雑性]--&gt;abstract[共通抽象]
    end
    subgraph ソフトウェア
        complexity1[複雑性]--&gt;complexity3[複雑性]
    end</pre>

<h2 id="2-構築方法">2 構築方法</h2>
<p>高可配置性ソフトウェアを構築するには、まずドメインの特徴に応じて汎用ソフトウェアモデルを確立し、次にそのモデルに対して設定技術を用いる。異なる設定とモデルの組み合わせが、最終的に異なるソフトウェアインスタンスを構築する。</p>
<pre class="mermaid">graph TB
    subgraph ソフトウェア2
        config2[ソフトウェア設定2]--&gt;abstract2[汎用モデル]
    end
    subgraph ソフトウェア1
        config1[ソフトウェア設定1]--&gt;abstract1[汎用モデル]
    end</pre>

<h3 id="21-モデリング">2.1 モデリング</h3>
<p>モデリングの問題は非常に複雑な問題であり、設計時のシナリオによって異なる。ドメイン知識と設計経験を必要とし、ドメインの専門家と密接に協力する必要がある。この分野にはいくつかの古典がある：<a href="https://book.douban.com/subject/26819666/"
   rel="noopener noreferrer" target="_blank" class="external">『エリック・エヴァンスのドメイン駆動設計』</a>
と<a href="https://book.douban.com/subject/25844633/"
   rel="noopener noreferrer" target="_blank" class="external">『実践ドメイン駆動設計』</a>
。</p>
<h3 id="22-設定技術">2.2 設定技術</h3>
<h4 id="221-設定ファイル">2.2.1 設定ファイル</h4>
<p>設定ファイルは最も広く使われ、最も基本的な設定技術である。設定ファイルは主にソフトウェアの起動および実行に必要なデータを記述するために用いられ、一般にソフトウェアの実行ロジックを直接記述するものではない。一般的な設定ファイル形式には ini、xml、json などがある。簡単な比較を下表に示す。</p>
<table>
  <thead>
      <tr>
          <th>形式</th>
          <th>表現力</th>
          <th>可読性</th>
          <th>典型的な用途</th>
          <th>例</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>ini</td>
          <td>弱い、ネスト不可</td>
          <td>最も良い</td>
          <td>ソフトウェアの起動パラメータ。</td>
          <td>起動ディレクトリ、IP アドレス。</td>
      </tr>
      <tr>
          <td>xml</td>
          <td>最も強い</td>
          <td>最も悪い</td>
          <td>複雑なオブジェクトのシリアライズ。</td>
          <td>データフレームの構造と解析規則。</td>
      </tr>
      <tr>
          <td>json</td>
          <td>普通</td>
          <td>普通</td>
          <td>Web API インタフェース。</td>
          <td>天気予報サービスのインタフェース。</td>
      </tr>
  </tbody>
</table>
<h4 id="222-スクリプト言語">2.2.2 スクリプト言語</h4>
<p>大規模なプログラムは、しばしば複数のプログラミング言語を組み合わせて実現される。一般に、コンパイル型言語でシステムの比較的固定された部分と実行効率が重要な部分を実装し、インタプリタ型言語でシステムの変わりやすい部分を実装する。たとえば Linux オペレーティングシステムは C で多くのツールを実装し、bash スクリプトでそれらのツールを総合的に組み合わせて応用する。大規模ゲームは C++ のゲームエンジン上に構築され、Lua スクリプトなどでゲームの動作ロジックを記述する。</p>
<h4 id="223-ドメイン固有言語">2.2.3 ドメイン固有言語</h4>
<p>ドメイン固有言語は一般的な設定ファイルとプログラムコードの中間に位置する。すなわち、ある特定のドメインに向けた、表現力が制限された計算機プログラミング言語である。</p>
<p>ドメイン固有言語はさらに内部ドメイン固有言語、外部ドメイン固有言語、言語ワークベンチなどに分かれる。内部ドメイン固有言語は主要言語のプログラムコードの特殊な書き方であり、設定とは見なせない。外部ドメイン固有言語は新しい言語を設計して表現するもので、依然としてテキスト設定の範囲に属する。言語ワークベンチは主に可視化された設定方式を提供するもので、可視化プログラミングとも呼ばれる。</p>
<p>ドメイン固有言語は設定ファイルとスクリプトプログラムの中間に位置し、ドメイン内で高い表現力を提供するとともに、ドメインの専門家にも理解しやすい。専門家の知識をソフトウェアへと生成することを加速できる。</p>
<p>モデルの必要に応じて、上記 3 つの設定技術はいずれも高可配置性ソフトウェア開発に適用できる。下表では 3 つの設定技術の主な特徴と典型的な用途を比較する。</p>
<table>
  <thead>
      <tr>
          <th>設定技術</th>
          <th>表現力</th>
          <th>可読性</th>
          <th>典型的な用途</th>
          <th>例</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>設定ファイル</td>
          <td>ソフトウェアの実行に必要なデータを記述する。一般にソフトウェアの実行ロジックを直接記述しない。</td>
          <td>具体的な設定ファイル形式による。</td>
          <td>ソフトウェアの起動パラメータ。</td>
          <td>起動ディレクトリ、ネットワーク通信アドレス。</td>
      </tr>
      <tr>
          <td>スクリプト言語</td>
          <td>ソフトウェアの実行ロジックを直接記述する。</td>
          <td>プログラマにしか適さず、ドメインの専門家には理解しにくい。</td>
          <td>基本インタフェースの形式が固定されている場合に、変わりやすいロジックを記述する。</td>
          <td>C++ で構築した機器監視インタフェースの上で、Python スクリプトで監視フローを記述する。</td>
      </tr>
      <tr>
          <td>ドメイン固有言語</td>
          <td>ドメインモデルの設定を記述し、それによって間接的にソフトウェアの実行ロジックを記述する。</td>
          <td>プログラマにもドメインの専門家にも適する。</td>
          <td>認容度の高いドメインモデルが確立され、モデルの設定が豊富で変わりやすい場合。</td>
          <td>「状態-反応」モデルで故障処理を表現し、ドメイン固有言語を確立して具体的な内容を記述する。</td>
      </tr>
  </tbody>
</table>
<h2 id="3-エンジニアリング実践">3 エンジニアリング実践</h2>
<p>図に示すように、高可配置性ソフトウェア開発は実際に従来のソフトウェア開発プロセスを変え、ソフトウェアのライフサイクルにおける設定の重要な位置づけを際立たせる。以下では、高可配置性が従来のソフトウェア開発の各段階に与える影響を述べる。</p>
<pre class="mermaid">graph TB
    subgraph 高可配置性ソフトウェアプロセス
    	requirements2[要件分析]--&gt;construct2[ソフトウェア本体構築]
    	construct2--&gt;config[ソフトウェア設定]
        config--&gt;test2[ソフトウェア本体テスト]
        config--&gt;config_test[ソフトウェア設定テスト]
		test2--&gt;maintain2[ソフトウェア本体保守] 
		config_test--&gt;config_maintain[ソフトウェア設定保守]
    end
    
    subgraph 一般的なソフトウェアプロセス
    	requirements1[要件分析]--&gt;construct1[ソフトウェア構築]
        construct1--&gt;test1[ソフトウェアテスト]
		test1--&gt;maintain1[ソフトウェア保守] 
    end</pre>

<h3 id="31-要件分析">3.1 要件分析</h3>
<p>要件分析の段階では、通常の開発と比べて、高可配置性ソフトウェア開発では以下の問題を重点的に分析する必要がある：</p>
<ol>
<li>高可配置性技術を採用する必要性。これは主にソフトウェアの適用範囲によって決まる。すなわち、ソフトウェアに展開可能性を持たせるかどうか、ソフトウェアが対応すべき要求が変わりやすいかどうかである。適応すべきシナリオが多いほど、要求が変わりやすいほど必要性は大きくなる。ソフトウェアがほぼ固定された一つのシナリオにしか使われないなら、設定を用いる必要はない。</li>
<li>モデル要求と設定要求を区別すること。要求収集の際には、ソフトウェアの各種の適用シナリオにおける要求を収集し、ソフトウェアの展開に伴って生じうる要求の拡張を分析し、できるだけ多くの事例を分析し、深く分析してシステムの不変性を抽出し、システムのモデルを抽象化する。そしてシステムの変わりやすい部分を設定要求として識別する。</li>
</ol>
<h3 id="32--ソフトウェア構築">3.2  ソフトウェア構築</h3>
<p>高可配置性ソフトウェア開発におけるソフトウェア構築は二つの段階に分かれる：</p>
<ol>
<li>ソフトウェア本体の構築：この段階はプログラマが行う。高可配置性はアーキテクチャ設計への要求が高く、オブジェクト指向パラダイムを用いる場合には、オブジェクト指向の設計原則を深く理解し適用する必要があり、各種の構造型設計パターン、およびリフレクションや依存性注入といった実行時関連技術を大量に応用する必要がある；</li>
<li>ソフトウェア設定の段階：この段階はプログラマが行うこともでき、ドメインの専門家が行うこともできる。設定がソフトウェアの重要な構成部分となるため、設定ファイルはソースコードと同様に厳格に管理し、バージョン管理システムに組み入れるべきである。手作業の設定は誤りを生じやすいため、できるだけ可視化された設定ツールを提供し、利用者が勝手に設定を変更することを制限すべきである。</li>
</ol>
<h3 id="33-ソフトウェアテスト">3.3 ソフトウェアテスト</h3>
<p>ソフトウェアの二段階の開発に対応して、高可配置性ソフトウェアのテストも二つの部分に分かれる：</p>
<ol>
<li>
<p>ソフトウェア本体のテスト。テストケースを構築する際には、各種のシナリオの設定を設計すべきである。これらのシナリオではモデルの境界状況を考慮し、テストを通じてソフトウェア本体の欠陥をできるだけ取り除き、ソフトウェアの展開・普及時に設定後に欠陥が発見されることを減らす。</p>
</li>
<li>
<p>ソフトウェア設定のテスト。設定テストを完了して初めて具体的なシナリオで応用できる。設定の過程はプログラマではなくドメインの専門家が行うことが多いため、設定の検証を行い、設定完了後のテストツールとテスト方法の細則を提供して、ドメインの専門家もソフトウェアのテストを完了できるようにするのがよい。</p>
</li>
</ol>
<h3 id="34--ソフトウェア保守">3.4  ソフトウェア保守</h3>
<p>ソフトウェアの二段階の開発に対応して、高可配置性ソフトウェアの保守も二つの部分に分かれる：</p>
<ol>
<li>
<p>ソフトウェア本体の保守。汎用的な機能のアップグレードはソフトウェア本体のアップグレードによって行う。ソフトウェア本体の機能の変更は設定との互換性を保つべきであり、設定の形式を変更せざるを得ない場合には、設定のアップグレード変換機能を提供すべきである；</p>
</li>
<li>
<p>ソフトウェア設定の保守。利用者の要求と適用シナリオが変化し、その変化がソフトウェア本体の想定の範囲内であれば、ドメインの専門家が設定を変更して完了できる。ソフトウェア設定は次第に利用者の重要な資産となる。</p>
</li>
</ol>
]]></content:encoded><category>coding</category><category domain="tags">ソフトウェア工学</category><category domain="tags">プログラミング</category></item><item><title>「デバッグの法則」（Debugging Rules!）——デバッグは技術である</title><link>https://yifeitao.com/ja/debug-is-technology/</link><pubDate>Sat, 24 Dec 2016 00:00:00 +0000</pubDate><guid>https://yifeitao.com/ja/debug-is-technology/</guid><description>&lt;p&gt;一般に、エンジニアがあるものを芸術、あるいは玄学と呼ぶときは、その難度が高く、従うべき規則があまりないことを意味する。デバッグはこの範疇に属する。ほとんどすべてのプログラマは、バグに翻弄されて七転八倒した経験を持つ。時にあれこれいじっているうちに問題は解決したように見えるが、なぜ解決したのかは分からない。それを説明不能な玄学現象として扱うしかない。最も厄介なバグは、たまにしか現れず再現が難しいもので、学名をハイゼンベルクバグという。&lt;/p&gt;
&lt;p&gt;しかし、ソフトウェアはやはり科学技術の範疇に属するものであり、デバッグもまた技術的な仕事であるべきだ。「デバッグの法則」（Debugging Rules!）は、デバッグ技術を扱った珍しい書物である。九つの普遍的な原則は、ソフトウェアとハードウェアの開発・保守に当てはまるだけでなく、日常生活にさえ応用できる。この本を見つけるのにも少し苦労したが、幸いなことに図霊コミュニティには&lt;a href="http://www.ituring.com.cn/book/84"
rel="noopener noreferrer" target="_blank" class="external"&gt;正規の電子書籍&lt;/a&gt;
が売られていた。&lt;/p&gt;
&lt;p&gt;正直に言えば、著者が挙げる例の多くはハードウェア寄りで、理解するのにやや隔たりを感じる。しかし九つの原則は実に実用的で、私の以前の開発・デバッグの経験の中でも無意識にいくつかはまとめていたかもしれない。それでもこの本を読むと、武術の心法を手に入れたような感覚がある。読んだ後、以下で九つの原則をもう一度たどり、理解を深める。&lt;/p&gt;
&lt;h2 id="1-システムを理解する"&gt;1 システムを理解する&lt;/h2&gt;
&lt;p&gt;これは最も重要な原則である。バグを分析するには、当然システムがどのように動作するかを理解する必要がある。そのためには基本原理をいくつか学び、身につける必要があり、具体的なクラスライブラリ、ツール、技術については関連するドキュメントを真剣に読む必要がある。私が公式ドキュメントを真剣に読む習慣を身につけたのはここ数年のことである。検索エンジンの発達によって、我々は google 指向プログラミングの習慣を身につけてしまった。問題に直面すると、いつも一気に解決しようとする。結果として多くの回り道をすることになる。道標を見る時間を惜しむからだ。本書は特に「一字一句」マニュアルを読むことを強調しており、これは浮ついた人間への頭からの一撃である。システムを理解せずにデバッグを始めることと、原理を理解せずにコードを書くことは一脈相通じるもので、俗に「偶然指向プログラミング」と呼ばれる。&lt;/p&gt;
&lt;h2 id="2-失敗を再現する"&gt;2 失敗を再現する&lt;/h2&gt;
&lt;p&gt;この原則はバグを再現することの重要性と方法を説く。バグは必ずある特定の条件下で発生する。その特定の条件を丹念に突き止めれば、半分は成功したも同然である。私のハードウェア保守の経験から言えば、バグを発見した人がその過程全体を詳細に記録できれば、非常に役立つ。多くのバグは発生条件が厳しく、だからプログラマの口癖の一つが「私のところでは問題ないです」なのである。こうしたバグに対しては、まずシミュレーションの方法を見つけるのがよい。たとえばソフトウェアの動作や相互作用の速度を上げる、ストレステストを行うなどである。次に、ソフトウェア内部に詳細で合理的なバグ記録の仕組みを持ち、内部から再現条件を見つけやすくする。&lt;/p&gt;
&lt;h2 id="3-考えず見る"&gt;3 考えず、見る&lt;/h2&gt;
&lt;p&gt;この原則は観測の重要性を強調する。空想するよりも現実に向き合うことが大切である。言語は思考に影響する。だからバグに遭遇したときの口癖は「ちょっと見てみます」であるべきで、「たぶん〜が原因だと思います」ではない。我々はあらゆる手段を尽くしてバグの細部をはっきりと見るべきであり、成熟したプログラムにはデバッグ用の基盤が備わっているべきである。私の経験から言えば、少なくともログは不可欠である。初級のプログラマはブレークポイントだけに頼ってデバッグする習慣があるが、問題はブレークポイント自体がプログラムの実行フローを変えてしまうことである（特にマルチスレッドの場合）。もちろん推測も依然として有用で、観測の範囲を絞り込む助けになる。あるいは少なくとも観測の優先順位を決めるのに役立ち、そこで経験が生きる。しかしどうであれ、経験は観測に代わりえない。&lt;/p&gt;
&lt;h2 id="4-分割統治"&gt;4 分割統治&lt;/h2&gt;
&lt;p&gt;二分探索法は探索の時間計算量を線形から対数に変えられる。これはプログラム中の探索アルゴリズムだけでなく、デバッグ時の方法論でもある。システムにこの意識があれば、鍵となるのはシステムをどう分割するかであり、ここで原則1に戻る。少なくともブレークポイントでデバッグするとき、バグの位置を見つけるのに二分法が使えるし、ログを挿入するときにもこの原則を応用できる。さらに本書ではバグ同士が互いに影響し合う現象にも触れている。私はむしろバグのシーソー現象と言ってもよいとさえ思う。解決の道は一つも見逃さないことである。バグをデバッグしているうちにコードの品質があまりに悪いことに気づき、リファクタリングが必要になることもある。そうでなければ砂の上に高台は築けない。効果的なリファクタリングを経て、バグが自然に発見され解消されることさえある。&lt;/p&gt;
&lt;h2 id="5-一度に一箇所だけ変える"&gt;5 一度に一箇所だけ変える&lt;/h2&gt;
&lt;p&gt;この原則は実験科学における対照の原則に似ている。一度に一つの変数だけを考察し、正常な状況と異常な状況を比較し、いかなるテスト条件の差異も見落としてはいけない。前の条ではバグを一つも見逃さないと言ったが、一つずつ片付ける必要もある。リファクタリングの際も、理解していないコードをやたらにいじらないよう注意すべきである。多くのコードの危険性は、一箇所を触ると全体に響くことにある。これがバグ修正時の最大のリスクだと思う。しばしば一つのバグを消すと同時にいくつかのバグを増やすことになる。やはり原則1に戻り、システムを理解することである。とはいえ、先人が書いたコードを理解するのは容易ではないこともある。これはまた、コードを書くときに徳を積むべきことを我々に示している。コードはコンパイラのためにあるのではなく、後に我々のコードを保守する人のためにあるのだ。&lt;/p&gt;
&lt;h2 id="6-監査証跡を残す"&gt;6 監査証跡を残す&lt;/h2&gt;
&lt;p&gt;この原則は記録の重要性と方法を説く。医者が診察するのと同じで、適切な質問をし、具体的で有効な答えを出す必要がある。私の経験では、バグのデバッグを始めると、人はすっかり深みにはまって抜け出せなくなることがある。記録を取ることで、思考の泥沼から自分を引き離し、デバッグの計画と方法を絶えず修正できる。専用のシステムソフトウェアでは、ソフトウェアの利用者に、バグの発生状況を有効な言葉で記録する方法を訓練する必要がある。記録がなければ、デバッグする側は適切な質問で利用者の記憶を呼び覚まさねばならない。また、深夜に解決できないバグは一眠りする必要があると思う。拡散思考が自動的に働き、たいてい朝には解決している。&lt;/p&gt;
&lt;h2 id="7-プラグを確認する"&gt;7 プラグを確認する&lt;/h2&gt;
&lt;p&gt;この原則は、いわゆる「初歩的なミス」について述べている。母が「パソコンのスピーカーから音が出ない」と言ってきたら、まず電源が入っているかを聞く、という類である。あるいは main 関数を mian 関数と書いてしまうとか。一度、ブレークポイントを置いてデバッグしてもブレークポイントで止まらず、後で分かったのは、似たコードが二箇所あり、ブレークポイントを間違った場所に置いていたことだった。シナリオと経験に照らして、そのシナリオでよくある初歩的なミスを犯していないか自問すれば、多くの場合すぐに効果が出る。多くの電気機器の取扱説明書の故障 FAQ でも、プラグの確認が強調されている。初歩的なミスが「初歩的」なのは難度であって、頻度ではない。&lt;/p&gt;
&lt;h2 id="8-新しい視点を得る"&gt;8 新しい視点を得る&lt;/h2&gt;
&lt;p&gt;この原則は助けを求めることと交流について述べている。ここで重要なのは、他者の視点によって自分の思考の固定観念を打ち破ることである。だから相手には現象だけを詳細に説明し、自分の推測は言わないほうがよい。相手の判断を汚染してしまうからだ。プログラマにとって、このときの google 指向プログラミングの力は驚異的である。おそらく九割の問題は、実際に質問する必要はなく、適切な検索語を見つけて検索すればよい。中国のプログラマにとって、英語で検索することは必須の技能である。もちろん、見つけたものを本当に理解することも非常に重要で、薬を見たらすぐ飲む、ではいけない。もし google で本当に検索できなければ、あなたのレベルが少し高い証拠である。そのときは stackoverflow などのコミュニティで質問すればよい。&lt;/p&gt;
&lt;h2 id="9-バグを修正しなければバグは依然として存在する"&gt;9 バグを修正しなければ、バグは依然として存在する&lt;/h2&gt;
&lt;p&gt;この原則は、甘い期待を抱くな、バグが一瞬顔を出しただけだからといってダチョウ政策を取るな、ということを述べている。来るべきものは必ず来る。報いがないのではない、時が来ていないだけである。もちろん実際の状況は複雑でありうる。ソフトウェア工学はもともと時間・コスト・品質の妥協の産物である。しかし、たとえ一部のバグを見逃すとしても、それによって生じうる結果を分析し、十分な防御機構を築くべきである。だから多くのソフトウェア認証体系にはソフトウェア安全水準の規格がある。ここにはバグの再現と一を聞いて十を知るという問題も関わる。本当にバグの原因を見つけ、有効な修正手段を提供したことを確認し、同様の問題をすべて洗い出す必要がある。たとえばメモリリークを一つ修正したら、そうしたリークを引き起こすすべてのコードを修正しなければならない。&lt;/p&gt;
&lt;h2 id="附記デバッグしやすいコードの書き方"&gt;附記：デバッグしやすいコードの書き方&lt;/h2&gt;
&lt;p&gt;デバッグの原則から逆に見ると、ソフトウェアの構築自体にも大いに役立つ。何しろデバッグは穴を塞ぐだけのものであり、書かれるコードのバグが少ないほうがよく、デバッグのときに便利なように手段を多く用意しておくのがよい。ここでも個人の経験を簡単にまとめておく。不断の改善が必要かもしれない。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;プログラムにはログの仕組みを備えるべきで、プログラムの debug 版ではデバッグ情報を出力しやすくする。release 版であっても、ソフトウェアに問題が発生したとき（たとえば C# で例外が投げられたとき）の詳細な状況を記録すべきである；&lt;/li&gt;
&lt;li&gt;人間が読むのに適したコードを書くべきである。そうでなければ後の保守をする人が読んでも分からない。どうデバッグせよというのか。どう書くかは『コードコンプリート』に最も詳しく書かれている。各種の慣習的な書き方のほかに、相手の身になって考えることも重要だと思う；&lt;/li&gt;
&lt;li&gt;各種のクラスライブラリやツールを使うときは、できるだけ原理を理解し、公式ドキュメントを丹念に読む。たとえばメモリリークをどう防ぐか、マルチスレッドの状況にどう対処するかを詳しく説明しているクラスライブラリもある。よく見てから使えば、禍根を植えることが少なくなる。少なくともデバッグのときに蠅のように当てもなく原因を推測する必要はなくなる；&lt;/li&gt;
&lt;li&gt;テストしやすいコードを書く。そうすればデバッグのときに問題を切り離しやすく、二分法も使いやすい。この点は TDD の実践が比較的十分に論じており、私自身も強化する必要がある。&lt;/li&gt;
&lt;/ol&gt;</description><content:encoded><![CDATA[<p>一般に、エンジニアがあるものを芸術、あるいは玄学と呼ぶときは、その難度が高く、従うべき規則があまりないことを意味する。デバッグはこの範疇に属する。ほとんどすべてのプログラマは、バグに翻弄されて七転八倒した経験を持つ。時にあれこれいじっているうちに問題は解決したように見えるが、なぜ解決したのかは分からない。それを説明不能な玄学現象として扱うしかない。最も厄介なバグは、たまにしか現れず再現が難しいもので、学名をハイゼンベルクバグという。</p>
<p>しかし、ソフトウェアはやはり科学技術の範疇に属するものであり、デバッグもまた技術的な仕事であるべきだ。「デバッグの法則」（Debugging Rules!）は、デバッグ技術を扱った珍しい書物である。九つの普遍的な原則は、ソフトウェアとハードウェアの開発・保守に当てはまるだけでなく、日常生活にさえ応用できる。この本を見つけるのにも少し苦労したが、幸いなことに図霊コミュニティには<a href="http://www.ituring.com.cn/book/84"
   rel="noopener noreferrer" target="_blank" class="external">正規の電子書籍</a>
が売られていた。</p>
<p>正直に言えば、著者が挙げる例の多くはハードウェア寄りで、理解するのにやや隔たりを感じる。しかし九つの原則は実に実用的で、私の以前の開発・デバッグの経験の中でも無意識にいくつかはまとめていたかもしれない。それでもこの本を読むと、武術の心法を手に入れたような感覚がある。読んだ後、以下で九つの原則をもう一度たどり、理解を深める。</p>
<h2 id="1-システムを理解する">1 システムを理解する</h2>
<p>これは最も重要な原則である。バグを分析するには、当然システムがどのように動作するかを理解する必要がある。そのためには基本原理をいくつか学び、身につける必要があり、具体的なクラスライブラリ、ツール、技術については関連するドキュメントを真剣に読む必要がある。私が公式ドキュメントを真剣に読む習慣を身につけたのはここ数年のことである。検索エンジンの発達によって、我々は google 指向プログラミングの習慣を身につけてしまった。問題に直面すると、いつも一気に解決しようとする。結果として多くの回り道をすることになる。道標を見る時間を惜しむからだ。本書は特に「一字一句」マニュアルを読むことを強調しており、これは浮ついた人間への頭からの一撃である。システムを理解せずにデバッグを始めることと、原理を理解せずにコードを書くことは一脈相通じるもので、俗に「偶然指向プログラミング」と呼ばれる。</p>
<h2 id="2-失敗を再現する">2 失敗を再現する</h2>
<p>この原則はバグを再現することの重要性と方法を説く。バグは必ずある特定の条件下で発生する。その特定の条件を丹念に突き止めれば、半分は成功したも同然である。私のハードウェア保守の経験から言えば、バグを発見した人がその過程全体を詳細に記録できれば、非常に役立つ。多くのバグは発生条件が厳しく、だからプログラマの口癖の一つが「私のところでは問題ないです」なのである。こうしたバグに対しては、まずシミュレーションの方法を見つけるのがよい。たとえばソフトウェアの動作や相互作用の速度を上げる、ストレステストを行うなどである。次に、ソフトウェア内部に詳細で合理的なバグ記録の仕組みを持ち、内部から再現条件を見つけやすくする。</p>
<h2 id="3-考えず見る">3 考えず、見る</h2>
<p>この原則は観測の重要性を強調する。空想するよりも現実に向き合うことが大切である。言語は思考に影響する。だからバグに遭遇したときの口癖は「ちょっと見てみます」であるべきで、「たぶん〜が原因だと思います」ではない。我々はあらゆる手段を尽くしてバグの細部をはっきりと見るべきであり、成熟したプログラムにはデバッグ用の基盤が備わっているべきである。私の経験から言えば、少なくともログは不可欠である。初級のプログラマはブレークポイントだけに頼ってデバッグする習慣があるが、問題はブレークポイント自体がプログラムの実行フローを変えてしまうことである（特にマルチスレッドの場合）。もちろん推測も依然として有用で、観測の範囲を絞り込む助けになる。あるいは少なくとも観測の優先順位を決めるのに役立ち、そこで経験が生きる。しかしどうであれ、経験は観測に代わりえない。</p>
<h2 id="4-分割統治">4 分割統治</h2>
<p>二分探索法は探索の時間計算量を線形から対数に変えられる。これはプログラム中の探索アルゴリズムだけでなく、デバッグ時の方法論でもある。システムにこの意識があれば、鍵となるのはシステムをどう分割するかであり、ここで原則1に戻る。少なくともブレークポイントでデバッグするとき、バグの位置を見つけるのに二分法が使えるし、ログを挿入するときにもこの原則を応用できる。さらに本書ではバグ同士が互いに影響し合う現象にも触れている。私はむしろバグのシーソー現象と言ってもよいとさえ思う。解決の道は一つも見逃さないことである。バグをデバッグしているうちにコードの品質があまりに悪いことに気づき、リファクタリングが必要になることもある。そうでなければ砂の上に高台は築けない。効果的なリファクタリングを経て、バグが自然に発見され解消されることさえある。</p>
<h2 id="5-一度に一箇所だけ変える">5 一度に一箇所だけ変える</h2>
<p>この原則は実験科学における対照の原則に似ている。一度に一つの変数だけを考察し、正常な状況と異常な状況を比較し、いかなるテスト条件の差異も見落としてはいけない。前の条ではバグを一つも見逃さないと言ったが、一つずつ片付ける必要もある。リファクタリングの際も、理解していないコードをやたらにいじらないよう注意すべきである。多くのコードの危険性は、一箇所を触ると全体に響くことにある。これがバグ修正時の最大のリスクだと思う。しばしば一つのバグを消すと同時にいくつかのバグを増やすことになる。やはり原則1に戻り、システムを理解することである。とはいえ、先人が書いたコードを理解するのは容易ではないこともある。これはまた、コードを書くときに徳を積むべきことを我々に示している。コードはコンパイラのためにあるのではなく、後に我々のコードを保守する人のためにあるのだ。</p>
<h2 id="6-監査証跡を残す">6 監査証跡を残す</h2>
<p>この原則は記録の重要性と方法を説く。医者が診察するのと同じで、適切な質問をし、具体的で有効な答えを出す必要がある。私の経験では、バグのデバッグを始めると、人はすっかり深みにはまって抜け出せなくなることがある。記録を取ることで、思考の泥沼から自分を引き離し、デバッグの計画と方法を絶えず修正できる。専用のシステムソフトウェアでは、ソフトウェアの利用者に、バグの発生状況を有効な言葉で記録する方法を訓練する必要がある。記録がなければ、デバッグする側は適切な質問で利用者の記憶を呼び覚まさねばならない。また、深夜に解決できないバグは一眠りする必要があると思う。拡散思考が自動的に働き、たいてい朝には解決している。</p>
<h2 id="7-プラグを確認する">7 プラグを確認する</h2>
<p>この原則は、いわゆる「初歩的なミス」について述べている。母が「パソコンのスピーカーから音が出ない」と言ってきたら、まず電源が入っているかを聞く、という類である。あるいは main 関数を mian 関数と書いてしまうとか。一度、ブレークポイントを置いてデバッグしてもブレークポイントで止まらず、後で分かったのは、似たコードが二箇所あり、ブレークポイントを間違った場所に置いていたことだった。シナリオと経験に照らして、そのシナリオでよくある初歩的なミスを犯していないか自問すれば、多くの場合すぐに効果が出る。多くの電気機器の取扱説明書の故障 FAQ でも、プラグの確認が強調されている。初歩的なミスが「初歩的」なのは難度であって、頻度ではない。</p>
<h2 id="8-新しい視点を得る">8 新しい視点を得る</h2>
<p>この原則は助けを求めることと交流について述べている。ここで重要なのは、他者の視点によって自分の思考の固定観念を打ち破ることである。だから相手には現象だけを詳細に説明し、自分の推測は言わないほうがよい。相手の判断を汚染してしまうからだ。プログラマにとって、このときの google 指向プログラミングの力は驚異的である。おそらく九割の問題は、実際に質問する必要はなく、適切な検索語を見つけて検索すればよい。中国のプログラマにとって、英語で検索することは必須の技能である。もちろん、見つけたものを本当に理解することも非常に重要で、薬を見たらすぐ飲む、ではいけない。もし google で本当に検索できなければ、あなたのレベルが少し高い証拠である。そのときは stackoverflow などのコミュニティで質問すればよい。</p>
<h2 id="9-バグを修正しなければバグは依然として存在する">9 バグを修正しなければ、バグは依然として存在する</h2>
<p>この原則は、甘い期待を抱くな、バグが一瞬顔を出しただけだからといってダチョウ政策を取るな、ということを述べている。来るべきものは必ず来る。報いがないのではない、時が来ていないだけである。もちろん実際の状況は複雑でありうる。ソフトウェア工学はもともと時間・コスト・品質の妥協の産物である。しかし、たとえ一部のバグを見逃すとしても、それによって生じうる結果を分析し、十分な防御機構を築くべきである。だから多くのソフトウェア認証体系にはソフトウェア安全水準の規格がある。ここにはバグの再現と一を聞いて十を知るという問題も関わる。本当にバグの原因を見つけ、有効な修正手段を提供したことを確認し、同様の問題をすべて洗い出す必要がある。たとえばメモリリークを一つ修正したら、そうしたリークを引き起こすすべてのコードを修正しなければならない。</p>
<h2 id="附記デバッグしやすいコードの書き方">附記：デバッグしやすいコードの書き方</h2>
<p>デバッグの原則から逆に見ると、ソフトウェアの構築自体にも大いに役立つ。何しろデバッグは穴を塞ぐだけのものであり、書かれるコードのバグが少ないほうがよく、デバッグのときに便利なように手段を多く用意しておくのがよい。ここでも個人の経験を簡単にまとめておく。不断の改善が必要かもしれない。</p>
<ol>
<li>プログラムにはログの仕組みを備えるべきで、プログラムの debug 版ではデバッグ情報を出力しやすくする。release 版であっても、ソフトウェアに問題が発生したとき（たとえば C# で例外が投げられたとき）の詳細な状況を記録すべきである；</li>
<li>人間が読むのに適したコードを書くべきである。そうでなければ後の保守をする人が読んでも分からない。どうデバッグせよというのか。どう書くかは『コードコンプリート』に最も詳しく書かれている。各種の慣習的な書き方のほかに、相手の身になって考えることも重要だと思う；</li>
<li>各種のクラスライブラリやツールを使うときは、できるだけ原理を理解し、公式ドキュメントを丹念に読む。たとえばメモリリークをどう防ぐか、マルチスレッドの状況にどう対処するかを詳しく説明しているクラスライブラリもある。よく見てから使えば、禍根を植えることが少なくなる。少なくともデバッグのときに蠅のように当てもなく原因を推測する必要はなくなる；</li>
<li>テストしやすいコードを書く。そうすればデバッグのときに問題を切り離しやすく、二分法も使いやすい。この点は TDD の実践が比較的十分に論じており、私自身も強化する必要がある。</li>
</ol>
]]></content:encoded><category>coding</category><category domain="tags">プログラミング</category><category domain="tags">デバッグ</category><category domain="tags">読書ノート</category></item></channel></rss>