🙄

CI-CDをバグについての考察から組み立てて考える!!

2023/08/04に公開

まえがき

「次の案件ではCI/CDを担当してもらいます」
私はCI/CDについてわかっていることを列挙した。そして、わかっていないことを列挙した。
私はCI/CDの利点すらわかっていなかった!
CI/CDを行うことによって、私たちは何ができる?私はまず、何をすればいいのだろう?

CI/CDとは何か?何のためにあるか?

CI/CDとは何か?

CI/CDとは、開発プロセスの自動化を進めることによって、品質改良を促す手法です。
CIとCDは、それぞれ自動化をするという意図は似ていますが、適用される実行フェーズが異なります。

CIとはContinuous Integrationの略で、継続的インテグレーションと翻訳されます。具体的には、任意のタイミングで自動的なビルド、テストそしてマージを行います。

一方、CDとはContinuous Delivery/Continuous Deploymentの略で、それぞれ継続的デリバリーと継続的デプロイメントに翻訳されます。
Continuous DeliveryとContinuous Deploymentの違いは、リリースをどの範囲まで自動的に進めるかにあります。
Continuous Deliveryはテスト環境まで、Continuous Deploymentは本番環境まで自動化します。[1]

CI/CDとは何のためにあるか?

CI/CDの目的は、問題を発見し、試行錯誤を素早く、また安全に行うことです。

CIはテストと頻繁なマージにより、バグを少なく共同開発することを助けます。
一方CDは、問題を発見し試行錯誤を素早く行うために高速なフィードバックループを構築します。

高速なフィードバックループの利点は2つあります。

  1. 要求に対する機動力を高める
  2. 自動的に開発者のモチベーションを高める

  1. 要求に対する機動力を高める

2000年代初頭からビジネスの世界で広く受け入れられている、VUCAという言葉があります。
冷戦終結後から感じられる、世界が不確実で変動制の高いものだ、ということを表現した言葉です。
不確実で、ルールすら変わってしまう世界では、素早く試す事で常にルールを確認することが求められます。
その意味で、要求に対する機動力を高めることが大切になります。

  1. 自動的に開発者のモチベーションを高める

高速なフィードバックループは開発者のモチベーションも高めてくれます。
なぜなら、小さな進捗を出しやすくするからです。

インナーワークライフに影響を与えるすべてのポジティブな出来事のなかで、最も強力なのがやりがいのある仕事が進捗することである。

マネジャーの最も大切な仕事――95%の人が見過ごす「小さな進捗」の力 第4商「進捗の法則」の発見

小さな進捗は、「やりがいのある仕事が進捗する」ことを支援するため、高速なフィードバックループは私たちのモチベーションを高めてくれます。

CI

先程述べたように、CIの目的はバグを少なく共同開発することです。
それを妨げる2つのものが存在します。それは不要な、コードレビューとマージコンフリクトです。

CIの障害 ― 不要な、コードレビューとマージコンフリクト

理想の状態を想定してみます。バグがなく、マージコンフリクトが存在しないコードならば、すべて自動的にマージできます。
そういった理想に近づくために、適切に自動化を行うのがCIの役割です。

適切に、とはどういうことでしょうか。
それを考えるために、まずコンピューターと人間の性質をそれぞれ考えてみます。

コンピューターの得意・不得意

コンピューターは0と1に基づいて、決められたコマンドを実行します。ただ、複雑で思考の必要なタスクは、コンピューターに命令するのが難しいので苦手としています。

よって、得意なことは

  • 繰り返しの作業
  • ミスのない作業
  • 大量の作業

不得意なことは

  • 設計などの複雑な思考を要する作業
  • 定型化し辛い作業

であることがわかります。

人間の得意・不得意

反対に、人間はコンピューターに比べて、抽象的な思考能力を持っています。ただ、地道で膨大な単純作業は苦手です。
例えばTypoglycemiaという現象があります。
わたたしちは こよのうな まちっがた ぶしょんうも かんんたに よめて しまのうです。

よって、得意なことは

  • 設計などの複雑な思考を必要とする作業
  • 定型化し辛い作業

不得意なことは

  • 繰り返しの作業
  • ミスのない作業
  • 大量の作業

以上の、コンピューターと人間のそれぞれの性質から、自動化に適したタスクの性質は、

  • 繰り返し
  • ミスのない
  • 大量

であるということがわかりました。

ただこれだけでは、コードレビューとマージコンフリクトの代替手段が何なのかわかりません。
そもそも、コードレビューの目的はバグを少なくすることです。
そこで、今度はバグについてもう少し詳しく考えてみたいと思います。

バグについての考察

この文章では、バグを想定外の挙動と定義します。
共同開発における実装で私たちが何をしているのか観察することを通じて、バグの性質を大まかに捉えます。
そうすることで、コードレビューとマージコンフリクトのかわりに、何を自動化すればいいのかが見えてくるはずです。

実装で私たちは何をしているか

実装とは、問題を解決するのに適したデータ・ロジックを、誤解なくコードに移すことです。
以下、そのようなデータ・ロジック、そしてそれらの関係性などの知識を、DDDの言葉を拝借して、解決領域の知識と呼びます。

もう一段階詳しく実装プロセスを観察してみると、私たちは

  1. 解決領域の知識を理解する
  2. 理解した知識を記述する

という2つの段階を往復しながら、段階的にプログラムを構築しています。
この2段階で、私たちはどのようにミスをするのでしょうか?その問題に対して、私たちはどのような自動化による対策を取ることができるでしょうか?

私たちは理想の解決領域の知識を理解できない

私たちは、理想の解決領域の知識を理解することができません。それは以下の理由からです。

  • ワーキングメモリーが少ない
  • 脳の性質上、盲点が常に存在する
ワーキングメモリーが少ない

そもそも私たちのワーキングメモリーは少ないです。頻繁に引き合いに出されるある研究によると、私たちが一度に保持できるチャンク数は7±2だそうです。[2]
そのような限られた認知資源では、解決領域の知識を理解することはできません。
例えば、Eコマースアプリケーションでは、カート・在庫管理・支払いなど、様々な部品が相互に関連してシステムを構築しています。

意識の性質上、盲点が常に存在する

また、私たちは常に盲点を持っています。なぜなら、私たちの脳は繋がりによって考える、文脈依存性を持っているからです。
例えば、放射線問題というものがあります。[3][4]これは次のような問題です。

眼の前に胃がん患者がいる。ある事情により、放射線を利用して治療することに決めた。
ただ、治療に十分な放射線は正常な細胞を傷つけてしまい、正常な細胞を傷つけないようにすると治療するには足りない。
さて、どうするか?

この問題の解決策は、「正常な細胞を傷つけないレベルの放射線を、様々な角度から、対象で交わるように照射する」ということです。多くの人は、自らの力でこの問題を解くことができません。ただ、ヒントとして似たようなストーリーを与えることで、類推によって解くことができるようになります。
このように、視点・切り口によって考え理解する精度が格段に変わると言えます。
直観的に理解した知識では、不足することが多いのです。

これらの結果、以下のようなバグが起こります。

私たちは頭の理解だけでは不十分で、ミスなく記述することも難しい

記述において、私たちはおもに2つの視点で失敗します。

  1. タイポ
  2. 頭の中の理解だけでは記述できない

1つ目はわかりやすく、表面的な記述に関するミスです。
単純に、constではなくcosntとタイポをしてしまうことがあります。

2つ目はより重要な点で、私たちは考えるために、脳みそだけでなく環境も必要とする、ということです。

書くことによって考える

私たちの思考は、環境と不可分なものです。

例えば、オプティカルフローというものがあります。これは、自分が動いている時の光のパターンです。遠景はゆっくり動き、近景は速く動きます。車に乗って外の風景を見ることを想像すると良いでしょう。富士山は全く動かないですが、近くにある木々は速く過ぎ去っていきます。
人間は通路を通る際、視界の左右のオプティカルフローを比較して、中心を通るそうです。通路の片方の壁のオプティカルフローを速くして、その中心を歩こうとする時、実際にオプティカルフローが速く設定された壁から離れて歩くそうです[6]

さらにわかりやすい例で言うと、筆算が想像しやすいのではないでしょうか。
複雑な、複数桁同士の掛け算も、筆算を使えば容易に行うことが可能です。これは、一度に必要なワーキングメモリーを制限しているから簡単なのです。

二つの例から、考えるのに環境が必要不可欠ということわかります。
実際、上で述べたように、理解と記述を反復するということからも書くことと思考することは切って考えられないことではありますが、思考において書くことも重要である、という側面は強調して良いと思います。

共同開発の問題

ここまで、単純な一人の実装によって生まれるバグを見てきました。
ただ、私たちは大規模なシステムを開発する上で、他者との共同開発は避けられません。ここで言う他者とは、将来の自分も含みます。
共同開発ではどのようなバグが生まれるでしょうか?

共同開発とは、理解が共有されづらい他者との、非同期な開発と捉えられます。
それぞれの性質から、どのようなバグが生まれやすいかを考えることができます。

理解が共有されづらい、ということは、それぞれが開発した部品がどう影響するのか分かりづらい、ということです。

  • 自分の実装によって、他人の実装がうまく動かなくなった
  • 他人の実装によって、自分の実装がうまく動かなくなった
  • 実装したら、昔実装したときには動いていたものが動かなくなった

といったことが起きるでしょう。

非同期という性質からはどんなことが起きるでしょうか?
これは、例えば同じコードを同時に編集してしまう、ということが考えられます。
これがマージコンフリクトです。

深く見ていくと違った性質のバグが考えられるかもしれませんが、まとめると、

バグについての考察まとめ

ここまで、「実装が解決領域の知識の理解・記述の反復である」という観察を通じて、
一人の実装の過程でどのようにバグが生まれるのかを考察しました。
また、「共同開発とは他者との非同期な開発である」ということから、共同開発で起こりうるバグを考察しました。

CIでやることは、これらのバグを、コンピューターが得意なやり方で自動化するということです。
以下に、これまで考察したバグを整理して、それらの対策についてそれぞれ考えてみましょう。

  1. 部品単体のバグ
    1. 複雑性
      1. 正しいロジックを理解していない
      2. 適切にデータをモデリングできていない
    2. 暗黙の知識を見落とす
    3. 記述ミス
  2. 部品相互の関係性による、複雑性のバグ
  3. マージコンフリクト

バグに、コンピューター流の対処を

以上でまとめたバグそれぞれに対して、どのようにコンピューター流に自動化するかを見て行きましょう。
それぞれの問題に複数の対処法があり、重なり合っていて厳密に論ずることは難しいですが、実用上この分類は役立つと思います。

部品単体 ― ロジックの複雑性

ロジックの複雑性に対処するには、小さく分割された単体テストと、複雑なロジックを招くバッドプラクティスを抑制するリンターが効果的です。

ルネ・デカルトが方法序説で述べたように、困難は多数の小部分に分割することです。
小さな部品について単体テストを書くことで、私たちはロジックの複雑性に対処できます。

また、リンターによっても、間接的にロジックの複雑性に対処することができます。
不適切な記述によって生まれる余計な複雑性を排除する、という方向性での対処です。
例えば、Javascriptでむやみにvarを利用すれば、Mutabilityによる無用な複雑性に対処する必要が生まれてしまいます。

部品単体 ― データの複雑性

データの複雑性に対処する方法は、Type Driven Development契約プログラミングなどです。

Type Driven Developmentや契約プログラミングは、独自の型から定義して、事前条件を強制できるように開発しよう、という考え方です。
型に情報を入れよう。自明なのに書かなければいけない面倒なものと捉えるのではなく、積極的に、「値の範囲や性質を保証する」ために型を使っていこう。そうして、部品が利用されるために必要な、値などの事前条件を強制できるように。

例えば、スマートコンストラクターというパターンがあります。
これは、スコープにある値が全て正常であることを、「値の生成方法を強制する」事によって保証する方法です。GoFのデザインパターンの中では、Factoryパターンとも呼ばれています。
VIPルームに入る方法が、スタッフにVIPであることを確認される事だけであれば、VIPルームにいる人はみんなVIPである。そのようなイメージのデザインパターンです。

独自の型を書くことによって、明示的にデータのモデリングをすれば、どのようなデータの複雑性を理解していないのかわかります。
データの複雑性は、意識すらされないことに問題があるのです。

暗黙の知識を見落とす

暗黙の知識を見落とすことに対処する方法は、単体テストによる知識の累積や、Type Driven Developmentです。

単体テストを実行するということは、それ自体ロジックやパラメーター関する仮説を立て、検証するということに見立てられます。

このように設定した部品に、この値をわたして実行したら、こうなるはずである
このような仮説をたくさん立てて、検証することになります。
すると、部品に関する知識はどんどん深まっていく事になり、できる限り暗黙の知識を理解できる様になるはずです。

また、Type Driven Development、より正確に言うと、独自の型を利用した開発も重要です。
なぜなら、明示的な知識が増えるからです。
String型より、PhoneNumber型を。Int型よりPrice型を引数に取る方が、一つの関数で表現できる有益な知識が増えます。
これは、ワーキングメモリーの節約にも繋がる、考えるのが楽な手段だと言えます。

記述ミス

記述ミスに対処する方法は、リンターコンパイラーが正しく動くようにすることです。
失敗したことが速やかにわかるからです。

部品相互の関係性による複雑性

部品相互の関係性による複雑性に対処する方法は、統合テスト複雑度の測定が効果的です。

統合テストは、部品を組み合わせた時に、期待した通りに動くかをテストするものです。
コストが高いので、具体的にどこまで自動化し、実装するかを判断するのは難しいですが部品同士が正しく組み合わされることを確認するだけでも価値があります。

また、複雑度の解析では、いくつか有名な測定指標が存在します。

  • 結合度
  • 凝集度

システムは、疎結合で高凝集であれば良いとされています。なぜなら、関連するもの同士で協働し、同時に関連しないものに意図しない影響を与えないからです。小規模なシステムであれば、大掛かりな測定をせずとも複雑度の増加を判断できるかもしれません。大規模になるほど、こういった定量的な測定方法が役に立ちます。

  • 循環的複雑度
    循環的複雑度は、関数がどれだけ分岐やループを持っているかを測定します。
    この値が大きいほど、一つの部品で様々なことをしようとしていると推測できるため、この部品が他の部品に影響を与える確率が上がると考えられます。

マージコンフリクト

マージコンフリクトには、表面的なコードスタイル上のコンフリクトと、解決手段的なコンフリクトが存在します。

表面的なコンフリクトに対処する方法はフォーマッターを設定することです。
フォーマッターを設定することで、コード規約に違反することがほぼなくなり、コンフリクトが起きなくなります。

一方、解決手段的なコンフリクトに対しては、頻繁なマージが重要になります。
頻繁なマージをするためには、必然的にマージの単位が小さくなる必要があります。
その結果、それぞれのマージの意味範囲がかぶることが少なくなるのです。

CIで具体的に何を自動化するか

以上の考察から、CIで自動化する対象は

  1. フォーマッター・リンター・コンパイラー
  2. テスト

の2つにまとめることができました。
一旦自動化のことは考えずに、具体的にどのようにリンターやテストを行っているのか見ていきましょう。

フォーマッター・リンター・コンパイラー

フォーマッター・リンター・コンパイラーが防ぐことができるバグは、

  1. 部品単体 ― ロジックの複雑性
  2. 記述ミス
  3. 表面的なマージコンフリクト

です。具体的な設定場面について見ていきます。

フォーマッター・リンター・コンパイラーの実体は、文字列を受け取って、エラーを検出し、必要があれば修正するコマンドです。
なので、中身では文字列を変更するコマンドを実行しているだけになります。

独自ルールのlint

特に面白い事例として、独自ルールをlintする事例について見てみましょう。

これらの具体的な事例の他にも、

  • deprecatedなものの使用を制限する
  • Factoryパターンの強制
  • 独自のラッパー関数の利用を強制

など、様々な活用事例を想像することができます。

以上の事例をまとめてみると、[先述](#部品単体\ ―\ ロジックの複雑性)のように、リンターはバッドプラクティスを検出することができるようです。
良いデザインの原則[7]のなかに、アフォーダンスという考え方があります。これは、デザインがもたらす、行為のやりやすさです。
様々な手段で、開発者に良いプラクティスを書くように強制するのは、とても有用なことだと言えます。[8]

テスト

テストは主に、

  1. 部品単体 ― ロジックの複雑性
  2. 暗黙の知識を見落とす
  3. 部品相互の関係性による複雑性

を解決すると述べました。
テストは種類によって、自動化したほうがよいテストと、人力でやったほうがコストが低くなりやすいテストがあります。
それぞれの種類のテストを見てみましょう。コストに対する認識が、どこまで自動化するか?どこまでテストを書くか?を判断する一助となります。

テストにはどのような種類が存在するか?

まずテスト対象の規模によって、テストを分類することができます。

  1. 単体テスト
  2. 結合テスト
  3. システムテスト
単体テスト

単体テストは、部品単体で機能するかをテストします。
単体テストのコストは、結合テスト・システムテストと比べて一番低いです。細かく単体テストが書かれていれば、部品が小さく疎結合になります。結果テストケースが少なくコストも下がるでしょう。

結合テスト

結合テストは、部品同士を組み合わせて機能するかをテストします。
結合テストのコストは、テスト設計の複雑さです。組み合わせの爆発という言葉がありますが、部品の集まりのテストケースの組み合わせの数は、それぞれの部品のテストケースの掛け合わせになるので、テスト設計がとても複雑になります。

システムテスト

システムテストは、システムが要件通り動くかをテストします。
機能要件だけでなく、非機能要件もテストされます。機能要件は動詞、非機能要件は副詞のようなイメージです。

例えば、非機能要件では

  • セキュリティ
  • パフォーマンス
  • ユーザビリティ

のような性質をテストします。

本番同等以上の環境が必要だったり、それぞれの分野で全く違ったテストシナリオの設計が求められ、複雑になります。

ユーザビリティテスト

筆者はフロントエンドエンジニアで、セキュリティやパフォーマンスに関する知見が無いので、特にユーザビリティテストの範囲についてのみ述べたいと思います。

まず、ユーザビリティのテストには、人間の目が必要になります。
なぜなら、

  1. UIには多様性があり、煩雑である。色・形・インタラクションなど、様々なパラメーターが存在する
  2. 複数のデバイスへ対応する必要がある。デバイスごとに使い心地が全く違うので、確認が煩雑
  3. デザインの良し悪しは、実際に使ってみないとわからない事が多い

からです。

ロジックと表示の分離

コンピュータによる自動化をスムーズに進めるために、重要な観点があります。
それは、ロジックと表示の分離です。reactでは特に、Container/Presentationalパターンとも呼ばれています。

この分離をすることによって、
コンピューターは得意なアプリケーションロジックに関するテストに集中し、人間は得意なユーザビリティのテストに集中することができるようになります。

UIカタログ

さらに手動テストのコストを下げるために、Storybookを導入することもできます。
UIカタログは、手動のユーザビリティテストの状態遷移の煩雑さを軽減するものです。

例えば、

  • ある特定のページの中にしかない部品をテストしたい。そのページに行くために何回もクリックする
  • あるアコーディオンコンポーネントが開いている状態の、特定の機能をテストしたい。その状態にするためにコンポーネントを開く

このような、状態遷移の煩雑さを、特定の状態の部品を一覧にするという方法で解決する手法です。
UIカタログが特に成果を発揮する場面は、

  • デザイナーと協働する
  • エンジニア自身がデザインを担当する

など、頻繁にデザインを確認する機会がある場面です。

ただ、ロジックと表示の分離やUIカタログという解決策は、小規模なプロジェクトではオーバーキルになりやすいです。
UIカタログの利点が得られるような場面で、積極的に活用していきましょう。

優先すべきテストの目的は?

優先すべきテストの目的についても見ていきましょう。
CIの目的は最小限のコストで最大限バグを少なくするということですから、少なくとも以下のテストは効果的です。

  • 変更があるたびに、既存の機能を担保する(リグレッションテスト)
  • 複雑性が集中している機能について
  • コアドメインについて
  • エラーの重要度が高い

テスト自動化の測定指標

建前では、色々なデータを測定しておいたほうがいいと思っている。
ただ本音では、テスト自動化の指標なんて測定したくないと思ってしまう。
一体その気持ちはどこから来ているんでしょう?

  • テスト自動化はやるべきだからやる
  • 速く結果が見たい

まず、そもそもデータを測定することは、印象だけではわからない様々な情報を得ることを助けます。
先述したように、私たちは環境によって思考します
ということは、考えたちょうどその時の体調や、プロジェクトの進展具合に大きく思考が左右されてしまうということです。
データを測定することで、簡単にその罠から抜け出すことができます。

そして、指標を測定することは、短期的なモチベーションが損なわれる物というより、
テスト自動化が長期に渡った時にモチベーションをもたらしてくれるものと捉えたほうが良いでしょう。
結果が明確になれば、開発者だけでなく上司やマネージャーの動機づけにもなります。

具体的にあなたのチームは、テスト自動化で何を得たいのでしょうか?何が分かれば、それを得たことを認識できるでしょうか?

  • 変動性が多いプロジェクトだから、リグレッションによる将来の変更への耐性?
  • 品質がとても悪いから、品質向上のためのカバレッジ?
  • 上司を説得するためにつくった小さなプロジェクトで、工数削減のデータを集める?
どのような測定指標があるか?

テスト自動化の測定指標に限っても、様々な考慮事項があります。
ここでは、EMTEという指標を紹介して終わりとします。
テスト自動化についての資格[9][10]もあり、網羅的に学べるので有用だと感じました。

EMTEとは、Equivalent Manual Test Effortの略語です。
テストを手動で実行した時間を1単位とし、自動化によってどのぐらいの時間を節約できるかを計測します。
そして、何回自動テストを実行したら、自動テスト構築の工数を取り戻せるかを計測するという指標です。

CD

CIについて、バグを少なく共同開発するための開発手法であることを見てきました。
最後にCDについて確認します。

以前述べたように、CDが行うのは、高速なフィードバックループの構築です。
高速なフィードバックループが、顧客の要求に速やかに対応する機動力と、開発者のモチベーションを高めてくれます。

高速なフィードバックループを構築するために必要なのは、

  • 問題を検知する
  • 素早く試行錯誤する

ことです。順番に見ていきましょう。

問題を検知する

問題を検知することで、顧客が求めない状況を速やかに検知し、回避することができます。
例えば、二重入金のような、顧客に直接損失が生まれてしまう状況が生まれてしまったらどうでしょうか?
どんなに最善を尽くしたとしても、障害が起こる可能性は常に残されています。ロギングサービスを設定して、もし障害が起きてしまった時に情報を集めやすくすることが重要です。

有用なログとは

ログの目的は、問題の検知と原因究明に役立つものです。
原因を究明する時、できるかぎりの文脈的な情報を、効率的に得られるととても役に立ちます。
例えば、

  • どのマイクロサービスか?
  • どのファイルか?
  • どの行か?
  • スタックトレースは?
  • ログの重要度は?(ログレベル)
  • もしかしたら、タグ付けなどもできると良いかもしれません。

このような、エラーを見るだけではわからない文脈的な情報を、如何に構造的に入れ込むのが大切です。

素早く試行錯誤する

素早く試行錯誤することで、ビジネス的な仮説検証を行うことができます。
CDパイプラインでは、CIによって動かせると確認したコードを、任意の

  • 本番環境
  • ステージング環境
  • テスト環境

にデプロイできるように設定します。

環境差異をなくす

ここで重要な性質は、環境差異をなくすことです。
本番で起きるような障害を先に検出するという性質から、ステージング・テスト環境はできる限り本番環境を模したものであるべきです。

そのためには、以下のような技術が有用になります。

  • 常に同じ環境を構築する
    • nix
    • docker
  • Infrastructure as Code

nixやdockerのような、冪等な環境イメージを作成を支援する技術を使えば、プラットフォームに関わらず同じ環境を作ることができます。
ほとんどのCI/CDパイプラインがnixやdockerに対応しており、効果的な環境構築を助けます。
また、この考え方を推し進めると、TerraformやChefのようなInfrastructure as Code[11]などが存在しており、バージョン管理と組み合わせてロールバックの実現や、ヒューマンエラーをなくすことができます。

まとめ

ここまでCI/CDについて見てきました。
CI/CDの目的は、バグの少ない状態で共同開発をし、高速なフィードバックループを構築することが目的でした。

共同開発と実装の性質を考察することを通じて、

  1. フォーマッター・リンター・コンパイラー
  2. テスト

を自動化して、CIがバグの少ない共同開発を実現していることを確認しました。

そしてCDでは、高速なフィードバックループを構築するために、
ロギング環境祭をなくすための環境構築技術が重要だと確認しました。

CI/CDについて調べて学ぶまで、なんとなく自動化って大事だよねぐらいの認識しかありませんでした。
そこから、顧客の要求にこんな対処をしているんだ、というぐらい解像度が上がったので、とても有意義な時間でした。
後は実践して、データや経験でしかわからないことを学んでいきたいです。

脚注
  1. Continuous Delivery: Reliable Software Releases through Build, Test, and Deployment Automation ↩︎

  2. 類似と思考 ↩︎

  3. M.L.GickandK.J.Holyoak.Analogicalproblemsolving.CognitivePsychology,12:306355,1980., M.L.GickandK.J.Holyoak.Schemainductionandanalogicaltransfer.CognitivePsychology,14:138,1983. ↩︎

  4. Simple Made Easy ↩︎

  5. The Magical Number Seven Plus or Minus Two ↩︎

  6. A.P.DuchonandW.H.WarrenJr.(2002).“AVisualEqualizationStrategyforLocomotorControl:OfHoneybees,Robots,andHumans.”PsychologicalScience13(3):272–278. ↩︎

  7. 誰のためのデザイン? ↩︎

  8. 他にも、依存型を採用するIdrisなどのプログラムを証明する形式的検証など、正しさを強制する方法は様々あります。ただし、今回は脱線しすぎるため省略します。 ↩︎

  9. 自動テストの効果測定に使われるEMTEとは? ↩︎

  10. Certified Tester Test Automation Engineer (CT-TAE) ↩︎

  11. IaC ↩︎

Discussion