『リーダブルコード』の要点をまとめる
記事の目的
「エンジニアが読むべき本」のような話題になると絶対と言っていいほど挙げられる良書『リーダブルコード』を読んで、自分なりに要点を整理していきます。
あくまで自分のためという比重が大きいので、この記事を読めばリーダブルコードがわかる!というものではありません。リーダブルコードを読んでいる方の多少の補助にはなるかもしれません。
この記事を書いている人間はJavaScriptに最も親しみを感じているので、自分の理解の促進のために適宜JSのコードを挟みます。JavaScriptで問題になりにくい部分(goto文など)はあえて無視しているところもあります。
4部選抜テーマに関してはまとめていません。
興味のある方はぜひ書籍を読んでみてください。
初心者の方は「第1部 表面上の改善」だけでも読んでみると、何を言っているのか理解しやすく取り入れやすいと思います。
はじめに
リーダブルコードの目的
プログラムコードをよくすること(アーキテクチャやデザインパターンの話はしない)
例えば、変数に名前を付けたり、ループの処理を書いたり、問題を関数のレベルまで分解したりすること
本書の中心となる考え
コードは理解しやすくなければいけない
1章 理解しやすいコード
読みやすさの基本
コードは他の人が最短時間で理解できるように書かなければいけない
-
ここでの「理解」とは?
コードに他の人が変更を加えたりバグを見つけたりでき、他のコードを連携する方法も理解できるという意味
よって必ずしも短くてコンパクトなコードが良いコードというわけではない。
第1部 表面上の改善
2章 名前に情報を詰め込む
明確な単語を選ぶ
例えば、新規のIDを発行する関数を作成するとして、
やることは「IDを作る」だから関数名を「makeID」にしよう!というのはよくない。
- 他のデータはあってIDのみ追加なら「addID」
-
新規にアカウントを作成してIDも発行するなら「createID」
など、関数でやることを明確にする名前にする
シソーラス(類語辞典)が参考になる。
tmpやretvalなどの汎用的な名前を避ける
戻り値を格納する変数だからretval、一時的な情報の保管のための変数だからtmpという名前の付け方はよくない。
変数にとって一番大切な役割から名前をつける。
function getTotal(num) {
let retval = 0;
for (let i = 0; i <= num; i++) {
retval += i;
}
return retval;
}
この場合戻り値は「足し合わせた合計」だからtotalの方が良い。
function getTotal(num) {
let total = 0;
for (let i = 0; i <= num; i++) {
total += i;
}
return total;
}
下記のような場合はtmpの重要な役割は「一時的な情報の保管」だから変数名tmpのままで良い。
const array = [1,4,9,2,7];
for(let i = 0; i < array.length; i++){
for(let j = array.length-1; j > i ; j-- ){
if(a[j] < a[j-1]){
let tmp = a[j];
a[j] = a[j-1];
a[j-1] = tmp;
}
}
}
ここでも使われているイテレータ(配列のようなデータ構造の要素を順に走査していく繰り返し処理を簡潔に記述できる構文やオブジェクトのこと、ここではfor文内のiやjのことを指す)もarray_iにするなど、よりよく改善できる場合がある。
ただし、イテレータはイテレータ以上の役割を持つことがないのでむやみに変更しない。
抽象的な名前よりも具体的な名前を使う
メソッドや関数の動作をよく確認して、その動作を表す具体的な名前を使うようにする。
名前に情報を追加する
変数名や関数名は短いコメントのようなもの。例えば、ミリ秒を入れるような変数名をtime
とするのではなく、msTime
とする、メモリバッファーを入れるような変数名をsize
とするのではなくmbSize
とするなど、属性情報を追加する。
スコープの大きな変数には長い名前をつける
例えば、下の配列名を考える。
const a = [1, 2, 3, 4, 5];
for(let i = 0; i <= a.length - 1; i++){
console.log(a[i])
};
このように小さいスコープで使用するだけなら、すぐに理解できるので短い名前でも良い。
ただ、もしこの配列を別の場所でも使うならもっと長くてもわかりやすい名前にするべき。
const numberArray = [1, 2, 3, 4, 5];
for(let i = 0; i <= numberArray.length - 1; i++){
console.log(numberArray[i])
};
名前やフォーマットで情報を伝える
コーディング規約に沿って変数名や関数名、メソッド名をつけることで、「これは大文字で始まっているからメソッド」「アンダースコアが含まれているから変数」など直感的に理解することができる。
JSのコーディング規約
3章 誤解されない名前
名前が「他の意味と間違えられることはないだろうか?」と何度も自問自答する。
例えば、filterは「フィルターをかける」という大きすぎる意味がある。
フィルターした結果特定の要素を選択するのであればselect
除外するのであればexclude
を使うなど、勘違いを生まない名前を選ぶ。
値の始まりと終わりも単語を使い分ける
- 限界値(2個まとめての購入が必要な場合の2や、10個まで購入できる場合の10)を表す場合は、
min
とmax
を使用して、min_items
やmax_items
と記載する。 - 範囲を指定して終端も範囲に含める場合(月曜日から日曜日まで、など)は
first
とlast
を使用して、first = monday
やlast = sunday
と記載する。 - 範囲を指定して終端を範囲に含めない場合(10月10日のみ指定)は
begin
とend
を使用して、begin = 10/10
やend = 10/11
と記載する。
ブール値の名前
ブール値の場合、基本的に変数名はhasPermission
やisActive
のようにわかりやすくする。
さらにtrueとfalseの意味が明確になるようにする。
const read_password = true
パスワードを読み取った後なのか、これから読むところなのかわかりにくいし、さらにis
やhas
もないのでブール値なのかもわかりにくい。
例えば、「パスワードが入力されたら」にするのなら下記のように改善できる。
const isPasswordEntered = true;
ユーザーの期待に合わせる
get
やsize
は軽量なメソッドであることが期待されているので、重量のあるメソッドの名前にしない方が良い。
4章 美しさ
コードの美しさを改善すると、コードが読みやすくなる。
原則は下記の3点。
- 読み手が慣れているパターンと一貫性なるレイアウトを使う
- 似ているコードは似ているように見せる
- 関連するコードをまとめてブロックにする
この原則を守るためにできるテクニックを紹介する。
-
一貫性のある簡潔な改行位置を守る悪い例
function initial() //ここだけ{を改行している { return function() { let string = 'Hello'; console.log(string); } } let func = initial(); //ここだけ代入の値を改行している
良い例function initial() { return function() { let string = 'Hello'; console.log(string); } } let func = initial();
-
メソッドを使用できるところは使用して、コードを整列させる悪い例
const numberArray = [1, 2, 3, 4, 5]; function addValueForArray(array, num) { const trimmedArray = []; for (let i = 1; i < array.length - 1; i++) { trimmedArray.push(array[i]); } const result = []; for (const elem of trimmedArray) { result.push(elem + num); } return result; } console.log(addValueForArray(numberArray, 5));
良い例const numberArray = [1, 2, 3, 4, 5]; function addValueForArray(array, num) { const trimmedArray = array.slice(1, -1); const result = trimmedArray.map(elem => elem + num); return result; } console.log(addValueForArray(numberArray, 5));
-
縦の線をまっすぐにする
オブジェクトなどで縦の線を揃えるように意識して並べる。悪い例const array = [ [1,2,3],[4,5,6], [7,8,9],[10,11,12],[13,14,15] ];
良い例const array = [ [1,2,3], [4,5,6], [7,8,9], [10,11,12], [13,14,15] ];
-
一貫性と意味のある並びにする
もしconst numberArray = [1,2,3,4,5]
という配列を用意して、「よし!数字の配列は昇順にしよう!」 と決めたら、他の箇所でも昇順にする。ある場所では昇順、ある場所では降順のように一貫性のない並びにはしない。どの並び順にするかは、コードにおいて最適な並び順にする。 - 宣言をブロックにまとめる
-
コードを「段落」に分割する
2つまとめて説明する。
const testString1 = "apple";
const testString2 = "grape";
const existingStrings = ["apple", "banana", "orange"];
function checkExistingString(targetString, existingStrings) {
if (existingStrings.includes(targetString)) {
console.log("OK");
} else {
console.log("NG");
}
}
checkExistingString(testString1, existingStrings);
checkExistingString(testString2, existingStrings);
上記は動作はするものの、改行や段落分けがされておらず、実行のためのコードが関数の上にあるなど、わかりにくい順番で置かれている。
function checkExistingString(targetString, existingStrings) {
// 配列の要素に存在する文字列かどうかを調べる
if (existingStrings.includes(targetString)) {
console.log("OK");
} else {
console.log("NG");
}
}
const existingStrings = ["apple", "banana", "orange"];
const testString1 = "apple";
const testString2 = "grape";
checkExistingString(testString1, existingStrings);
checkExistingString(testString2, existingStrings);
このように、適宜段落や改行を挟み、コメントを掲載することでわかりやすいコードになる。
コメントの書き方については次章で。
5章 コメントすべきことを知る
コメントするべきでは「ない」こと
- コードからすぐわかることをコメントに書かない
- 関数の名前と引数をそのまま文章形式で書くような「コメントのためのコメント」のようなことをしない
- わかりにくい関数名を補助するようなコメントの場合は、まず関数名を変更する
// 2つの数字を足し合わせる関数
function numfunc(numA,NumB){
//ここで2つの数字を足し合わせる
return numA + NumB
}
関数名が適当すぎるし、不要なコメントがついている。
function addTwoNumber(numA,NumB){
return numA + NumB
}
自分の考えを記録する
コードを書いているときに気づいた大切な点を記載しておく。
例えば、コードに対して欠陥がある場合はTODO:サブクラスを作って整理
などのように記載する。
欠陥や改善点を皆に知らせることを忌避しない。
他にも、定数を定めたときになぜその値にしたのか?などは書いた人にしかわからないので、考えを記載しておく。
//10個が購入上限のため
const items = 10;
この時に読み手(ここでは、コードを自分のように熟知していない人のこと)の立場になって何をコメントするべきか考えることが大切。
全体コメントと要約コメント
例えば、新しくコードに触れる人にそのコードを説明するという場面がある。
ソースコードを見ただけではわからない全体情報を口頭で補足しなければならない。
この口頭で補足しなければならない情報はコメントとして残しておくべき。
口頭で補足が必要そうな、大きな関数の塊などにも適宜要約コメントを挟んでおくと理解がしやすい。
コメントの書き方
- まずは思っていることを書く。
(例:「やばい!これはリストに重複があったら面倒なことになる」) - もっと詳細な言葉に書き換える。
(例:やばい→注意、これ→このコード、面倒なこと→実装が難しい「注意:このコードはリストに重複があったら実装が難しくなります」)
6章 コメントは正確で簡潔に
代名詞を避ける
「データをキャッシュに入れる、ただし、先にそのサイズをチェックする」
この場合の「その」は何を指すか曖昧。データ?キャッシュ?
なるべく具体的に指し示すようにする。「データをキャッシュに入れる、ただし、先にそのデータサイズをチェックする」
入出力のコメントに実例を使う
//"src"の先頭や末尾にある"chars"を除去する
function stripChars(src, chars) {
...
};
srcの末尾に複数のcharsがあったらどうなるのか?などさまざまなケースに答えるコメントになっていない。
//実例:Strip("abba/a/ba","ab")は"/a/を返す"
function stripChars(src, chars) {
...
};
これでどのような動きになるかわかりやすくなった。
情報密度の高い言葉を使う
//所在地から余分な空白を除去する。それから「Avenue」を「Ave.」にするなどの成形を施す。
//こうすれば、表記がわずかに違う所在地でも同じものであると判別できる。
コメントが長すぎる。やっていることは正規化(比較や分析を容易にするために、データの単位やスケールを共通の基準に整えること)だ。だから下記のように書く。
//所在地を正規化する(例:"Avenue" -> "Ave.")
このようにコメントを簡潔にする。
第2部 ループとロジックの単純化
7章 制御フローを読みやすくする
条件式の引数の並び順
if(age >= 18)
のような条件式がある場合、左側を変化する調査対象(ここではage)、右側を変化しない比較対象(ここでは18)にする。
言語的にも無理がない並び(「もし年齢が18歳以上なら」は自然な言い回し)を意識する。
if/elseブロックの並び順
下記の3つの原則に従う
- 条件は否定形よりも肯定形を使う(
if(a != b)
より、if(a === b)
) - 単純な条件を先に書く
- 関心を引く条件や目立つ条件を先に書く
これらの原則はぶつかり合うことがあるが、優先度をつけて書くようにする。
例えば、ファイルの有無を検索してからファイルがあったものだけ条件分岐したいような時は、否定文になってしまうが先頭にif not file
をのように最初に検索をかけた方が良い。
三項演算子の使いどころ
三項演算子という書き方がある。
条件に続いて疑問符 (?)、そして条件が真値であった場合に実行する式、コロン (:) が続き、条件が偽値であった場合に実行する式が最後に来ます。
三項演算子は単純な2つの値から1つを選ぶような場合のみに使う。
function getFee(isMember) {
return isMember ? '$2.00' : '$10.00';
}
これは条件がtrueなら$2.00、falseなら$10.00という極めて単純な条件なので使っても良い。
下記は三項演算子がわかりにくい例。
function getValue(numA,numB,numC){
return (numA > 0 && numB < 10 && numC !== 0) ? numA * 2 : numB / 2;
}
条件が長かったり、単純に値を返すのではない場合if/else文を使う。
function getValue(numA, numB, numC) {
if (numA > 0 && numB < 10 && numC !== 0) {
return numA * 2;
} else {
return numB / 2;
}
}
関数から早く返す
returnは一つでなくてはいけないというのは思い込み。
関数から早く返してネストを浅くすることで、実行の流れをわかりやすくすることができる。
const players = {
player1: {
name: "Alice",
stats: {
score: {
current: 95
}
}
},
player2: {
name: "Bob",
stats: {
score: {
current: 80
}
}
}
};
function calculateScore(player) {
if (player) {
if (player.stats) {
if (player.stats.score) {
if (player.stats.score.current) {
return player.stats.score.current;
}
}
}
}
return 0;
}
ネストが深くなっていて理解することが難しい。
要するにplayer.stats.score.current
に値があれば返し、なければ0を返すということ。
条件ごとに早くreturnしてネストを浅くする。
const players = {
player1: {
name: "Alice",
stats: {
score: {
current: 95
}
}
},
player2: {
name: "Bob",
stats: {
score: {
current: 80
}
}
}
};
function calculateScore(player) {
if (!player) {
return 0;
}
const { stats } = player;
if (!stats || !stats.score) {
return 0;
}
const { score } = stats;
if (!score.current) {
return 0;
}
return score.current;
}
※const { stats } = player;の書き方は分割代入
8章 巨大な式を分割する
説明変数を導入する
下記のようなコードがある。文からaを取り出して表示しようとしている。
const str = "Hello! My name is Taro."
console.log(str.slice(19).split("")[0]) //「a」が表示される
その式でやっていることを変数名として(説明変数)代入するだけで、何をしているのかわかりやすくなる。
const str = "Hello! My name is Taro."
const pickOutA = str.slice(19).split("")[0];
console.log(pickOutA)
論理式がわかりにくくなっていたら、ドモルガンの法則を使う
条件式は「notを分配してand/orを反転する」(逆は「notを条件式の先頭に括り出す」)ドモルガンの法則を応用することができる。
if (!condition1 || !condition2) {
// 条件1がtureであるか、または条件2がtrueである場合にtrueとなる
}
// ドモルガンの法則を使う
if (!(condition1 && condition2)) {
// 条件1と条件2の両方がtrueでない場合にtrueとなる
}
同じ式がいくつも出てきたら要約変数でまとめる。
関数内で同じ式が出てくることがある。
function calculateAreaAndPerimeter(length, width) {
const total = length * width + 2 * (length + width) + 10 * (length * width) - (width + length);
return total;
}
このような場合は要約変数で関数の上部に抽出して使用する。
行数は増えるが、コンパクトで読みやすいコードになる。
function calculateAreaAndPerimeter(length, width) {
const area = length * width;
const perimeter = 2 * (length + width);
const tempCalculation = 10 * area - (width + length);
const total = area + perimeter + tempCalculation;
return total;
}
9章 変数と読みやすさ
変数を削除する
下記のような変数は削除するようにコードを書き換える
- 複雑な式を分割していない
- 変数を使うことでより明確になっていない(要約できていない)
- 重複コードを削除するような目的を持っていない
変数のスコープを縮める
グローバル変数をなるべく使わないことはもちろん、関数内の変数もなるべくスコープを縮めるようにする。
JSならクロージャーでプライベート変数を作ることもできるので、必要があればその使用も考える。
定義の位置を下げる
無理に関数の先頭部で全ての変数を定義する必要はない。
大きな関数の場合は必要になった部分で定義するとわかりやすくなる。
変数は一度だけ書き込む
JSならletよりconstをなるべく使用する。
定義がコロコロ変わったり、上書きできてしまうとわかりにくくなる。
const 宣言はブロックスコープのローカル変数(定数)を宣言します。定数の値は代入演算子を使用して再代入することができませんが、定数がオブジェクトであった場合、そのプロパティを追加したり、更新したり、削除したりすることができます。
第3部 ループとロジックの単純化
10章 無関係の下位問題を抽出する
この章での主張はプロジェクト固有のコードから汎用コードを分離するということ。
下記のコードは与えられた地点から最も近い場所を見つけるものだが、度をラジアンに変換する、2つの地点の距離を計算する、と様々な問題を取り扱う部分が一つの関数になってしまっている。
// 与えられた地点
const givenPoint = { lat: 37.7749, lng: -122.4194 }; // 例: サンフランシスコの緯度経度
// 目的地のリスト (例)
const destinations = [
{ name: 'Destination 1', lat: 37.7749, lng: -122.4194 }, // 目的地1
{ name: 'Destination 2', lat: 34.0522, lng: -118.2437 }, // 目的地2
{ name: 'Destination 3', lat: 40.7128, lng: -74.0060 }, // 目的地3
// 他の目的地...
];
// 与えられた地点から最も近い場所を見つける関数
function findNearestPlace(givenPoint, destinations) {
// 最も近い場所とその距離を初期化
let nearestPlace = null;
let minDistance = Infinity;
// 度をラジアンに変換する
const toRadians = degrees => degrees * (Math.PI / 180);
// 2つの地点の距離を計算する
const calculateDistance = (point1, point2) => {
const R = 6371; // 地球の半径 (km)
const lat1 = toRadians(point1.lat);
const lat2 = toRadians(point2.lat);
const dLat = toRadians(point2.lat - point1.lat);
const dLng = toRadians(point2.lng - point1.lng);
// ハーフハーベルシンの公式に基づいて距離を計算
const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(lat1) * Math.cos(lat2) *
Math.sin(dLng / 2) * Math.sin(dLng / 2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
const distance = R * c;
return distance;
};
// 各目的地と与えられた地点との距離を計算し、最も近い場所を見つける
for (const destination of destinations) {
const distance = calculateDistance(givenPoint, destination);
if (distance < minDistance) {
minDistance = distance;
nearestPlace = destination;
}
}
return nearestPlace; // 最も近い場所を返す
}
// 最も近い場所を見つける
const nearestPlace = findNearestPlace(givenPoint, destinations);
console.log('Nearest Place:', nearestPlace.name); // 結果を出力
下記のように、関数を抽出するとその関数を別の部分で使用できるし、コードのメンテナンスも簡単になる。
// 与えられた地点
const givenPoint = { lat: 37.7749, lng: -122.4194 }; // 例: サンフランシスコの緯度経度
// 目的地のリスト (例)
const destinations = [
{ name: 'Destination 1', lat: 37.7749, lng: -122.4194 }, // 目的地1
{ name: 'Destination 2', lat: 34.0522, lng: -118.2437 }, // 目的地2
{ name: 'Destination 3', lat: 40.7128, lng: -74.0060 }, // 目的地3
// 他の目的地...
];
// 与えられた地点と目的地との距離を計算する関数
function calculateDistance(point1, point2) {
const R = 6371; // 地球の半径 (km)
const lat1 = toRadians(point1.lat);
const lat2 = toRadians(point2.lat);
const dLat = toRadians(point2.lat - point1.lat);
const dLng = toRadians(point2.lng - point1.lng);
const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(lat1) * Math.cos(lat2) *
Math.sin(dLng / 2) * Math.sin(dLng / 2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
const distance = R * c;
return distance;
}
// 度をラジアンに変換する関数
function toRadians(degrees) {
return degrees * (Math.PI / 180);
}
// 最も近い場所を見つける関数
function findNearestPlace(givenPoint, destinations) {
let nearestPlace = null;
let minDistance = Infinity;
for (const destination of destinations) {
const distance = calculateDistance(givenPoint, destination);
if (distance < minDistance) {
minDistance = distance;
nearestPlace = destination;
}
}
return nearestPlace;
}
// 最も近い場所を見つける
const nearestPlace = findNearestPlace(givenPoint, destinations);
console.log('Nearest Place:', nearestPlace.name);
ただし、切り分け過ぎても可読性は下がるので、プロジェクトの他の部分から再利用できる見込みがないレベルには切り分けない方が良い。
11章 一度に1つのことを
10章の関数の切り分けも、一つの関数で一つのことを処理するようにして、コードの再利用性を高めたりメンテナンスをしやすくしたりする効果がある。
一度に1つのことをすることは関数だけでなく、例えば条件分岐のブロックのレベルでもやるべきことである。
一度に一つのタスクを処理する
例えば、あるスコアに対してupボタンを押したら+1されて、downボタンを押したら-1されるような実装をする。
const vote_changed = function(old_vote, new_vote){
let score = get_score();
if(new_vote !== old_vote){
if(new_vote === "Up"){
score += (old_vote === "Down" ? 2 : 1);
}else if(new_vote === "Down"){
score += (old_vote === "Up" ? 2 : 1);
}else if(new_vote === ""){
score += (old_vote === "Up" ? -1 : 1);
}
}
set_score(score);
};
vote_changed(old_vote, new_vote);
ここで行なっているタスクを分解してみると、
1.old_voteとnew_voteを数値に変換する
2.scoreを更新する
そのため、下記のコードに改良することができる。
//old_voteとnew_voteを数値に変換する
const vote_value = function(vote){
if(vote === "Up"){
return +1;
}
if(vote === "Down"){
return -1;
}
return 0;
}
//scoreを更新する
const vote_changed = function(old_vote, new_vote){
let score = get_score();
score -= vote_value(old_vote);
score += vote_value(new_vote);
set_score(score);
}
読みにくい、何をしているのかわかりにくいコードがあればまず、そこで行われているタスクを全て列挙する。そして、別の関数やクラスに分割できるタスクを分割する。
ここで最も難しいことはプログラムがおこなっていることを正確に説明してタスクに落とし込むことである。
12章 コードに思いを込める
自分よりも知識が少ない人が理解できるような「簡単な言葉」で説明する必要がある。
ポイントは下記の三点。
- コードの動作を簡単な言葉で同僚にもわかるように説明する
- その説明の中で使っているキーワードやフレーズに注目する
- その説明に合わせてコードを書く
ロジックを単純化するには、まずロジックを簡単な言葉で説明する。
そして、まず提供されているライブラリやメソッドによって簡潔なコードにできないか検討する。
再度ロジックを簡単な言葉で説明して改善していく。
問題を声に出して説明するだけで解決策が見つかることがある。
ラバーダッキング法
声に出して悩みを説明する過程で、「何について悩んでいるのか」「その解決策は何か」ということが次第に見えてきます
13章 短いコードを書く
コードはなるべく書かないほうが、保守の手間も少なくて済むし、コードが増えすぎたプロジェクトはその後の開発も難しくなる。
コードをなるべく書かないようにするポイントは以下の三点。
-
不必要な機能をプロダクトから削除する、過剰な機能は持たせない
念の為の機能は作っても使わない。そのとき必要な機能を実装する。 -
最も簡単に問題を解決できるような要求を考える
問題を解決するために、何をコードに要求して実装すれば良いのかよく考える。 -
定期的に全てのAPIを読んで、標準ライブラリに慣れ親しんでおく
簡単にライブラリやメソッドで解決する方法がないかまず調べる。
例えば、JSならMDNというHTMLやXML、CSS、JavaScriptなどの技術に関する文書がまとめられたWeb開発者向けサイトにメソッドやオブジェクトがまとめられている。
記事のまとめ
私は未経験からプログラミングを始めて1年半程度なのですが、この本は今まで2回程度流し読みしています。
今回まとめるために再度読みましたが、コードに慣れてきたこともあり何を言っているのか理解度が上がったなと思います。
これからも折に触れて読みたいです。
Discussion