コードを初めて見たとき、すぐに理解できましたか?多くの開発者が、他人のコードを読み解くのに苦労しています。その解決策の一つが「自己文書化コード(Self-documenting Code)」です。これは、コメントに頼らず、コード自体がその目的や動作を明確に伝えるように書かれたコードのことです。
🧩 例:改善前のコード
以下は、ユーザーアカウントを作成するJavaScript関数の例です。一見シンプルですが、改善の余地があります。
async function createUser(user) {
if (!validateUserInput(user)) {
throw new Error('u105');
}
const rules = [/[a-z]{1,}/, /[A-Z]{1,}/, /[0-9]{1,}/, /\W{1,}/];
if (user.password.length >= 8 && rules.every((rule) => rule.test(user.password))) {
if (await userService.getUserByEmail(user.email)) {
throw new Error('u212');
}
} else {
throw new Error('u201');
}
user.password = await hashPassword(user.password);
return userService.create(user);
}
このコードでは、エラーコードが数字で表されており、何を意味するのか直感的に分かりません。また、パスワードのバリデーションロジックが関数内に直接書かれており、再利用性や可読性が低下しています。
🛠️ 改善ポイント
1. 名前付き定数の使用
エラーコードを意味のある名前の定数に置き換えることで、コードの意図が明確になります。([Lack of Imagination][2])
const err = {
userValidationFailed: 'u105',
userExists: 'u212',
invalidPassword: 'u201',
};
2. 単一責任の原則
パスワードのバリデーションロジックを別関数に分離することで、コードの再利用性と可読性が向上します。
function isPasswordValid(password) {
const rules = [/[a-z]{1,}/, /[A-Z]{1,}/, /[0-9]{1,}/, /\W{1,}/];
return password.length >= 8 && rules.every((rule) => rule.test(password));
}
これらの改善を適用した createUser
関数は以下のようになります。([Lack of Imagination][2])
async function createUser(user) {
if (!validateUserInput(user)) {
throw new Error(err.userValidationFailed);
}
if (isPasswordValid(user.password)) {
if (await userService.getUserByEmail(user.email)) {
throw new Error(err.userExists);
}
} else {
throw new Error(err.invalidPassword);
}
user.password = await hashPassword(user.password);
return userService.create(user);
}
3. 短絡評価の活用
短絡評価を使用することで、条件分岐を簡潔に表現できます。([Lack of Imagination][2])
function throwError(error) {
throw new Error(error);
}
async function createUser(user) {
validateUserInput(user) || throwError(err.userValidationFailed);
isPasswordValid(user.password) || throwError(err.invalidPassword);
!(await userService.getUserByEmail(user.email)) || throwError(err.userExists);
user.password = await hashPassword(user.password);
return userService.create(user);
}
このように書くことで、ネストが減り、コードの流れが直線的になります。
4. 型注釈の追加
JSDocを使用して型注釈を追加すると、IDEや静的解析ツールがコードを理解しやすくなります。
/** @typedef {{ id?: number, birthDate: Date, email: string, password: string }} User */
/**
* Creates a user and returns the newly created user's id on success
* @param {User} user
* @returns {Promise<any>}
*/
async function createUser(user) {
validateUserInput(user) || throwError(err.userValidationFailed);
isPasswordValid(user.password) || throwError(err.invalidPassword);
!(await userService.getUserByEmail(user.email)) || throwError(err.userExists);
user.password = await hashPassword(user.password);
return userService.create(user);
}
これにより、関数の入力と出力が明確になり、他の開発者がコードを理解しやすくなります。
✅ まとめ
自己文書化コードを実現するためのポイントは以下の通りです。
- 意味のある名前付き定数を使用する
- 関数は単一の責任を持つようにする
- 短絡評価を活用してコードを簡潔にする
- 型注釈を追加してコードの意図を明確にする
これらを実践することで、コメントに頼らずとも、コード自体がその目的や動作を明確に伝えるようになります。結果として、コードの可読性と保守性が向上し、開発効率の向上につながります。
元記事はこちら:
🔗 Self-documenting Code - Lack of Imagination