防衛的プログラミングと契約的プログラミングの違いがイマイチ分かっていない

防衛的プログラミング防御的プログラミングとかいう手法と、契約による設計だとか契約的プログラミングとかいう手法があるらしい。が、イマイチその違いが分からない。概念的な話ばかりで、コードに落とし込んだ時にどういう違いが出るのかを説明してくれている文献があまり見当たらない。

いくつかの文献を見てみて僕なりの解釈をしてみたが、しっくり来ていないので誰か教えてほしい。

僕の防衛的プログラミングの解釈

僕の契約的プログラミングの解釈

僕のコードで比較する両者の違い

防衛的プログラミング

function readFile(path, fileName) {
  if(!path) {
    throw new Error('Illegal Path');
  }
  
  if(!fileName) {
    throw new Error('Illegal File Name');
  }
  
  let result;
  try {
    result = fs.readFileSync(path + fileName);
  }
  catch(error) {
    throw new Error('Could not read file');
  }
  
  if(!result.text) {
    throw new Error('Illegal File');
  }
  
  return result.text;
}

// 利用側の処理
const path = getPath();
if(!path) {
  throw new Error('Path is null');
}

const fileName = getFileName();
if(!fileName) {
  throw new Error('File Name is null');
}

const result = readFile(path, fileName);

なんていうんだろう、防衛的プログラミングは、「そのバグが起こりうるのは知ってましたから!対策ありますから!」みたいなイメージかなと思った。だから、throw new Error() の部分は、要件が合えば「上手くいかない場合は空文字を返す」みたいな作りにしても良いのかなーと思った。

で、呼び出し元の方でも、引数として渡す変数のチェックをしていたりして、冗長だけど絶対に「予期していないエラーはない」状態にすることが、防衛的プログラミングかなと思った。

契約的プログラミング

function readFile(path, fileName) {
  assert(!path);
  assert(!fileName);
  
  const result = fs.readFileSync(path + fileName);
  
  assert(result.text);
  return result.text;
}

// 利用側
const path = getPath();
const fileName = getFileName();
const result = readFile(path, fileName);

契約的プログラミングは呼び出し元でのチェックはせず、呼ばれた側が、自メソッド内で入力値チェックと出力値チェックをして終わる感じかなと思った。チェックもアサーションを使うのが特徴的というか、開発時だけエラーが検知できていれば、本番コードからはそのチェック仕様は取り除ける作りなのかな?と思った。

なんか勘違いしてそうなので、ご指摘賜りたく。

参考