Upgrade to Pro — share decks privately, control downloads, hide ads and more …

コードに語らせよう――自己ドキュメント化が内包する楽しさについて / Let the Code...

Avatar for nrs nrs
May 30, 2025

コードに語らせよう――自己ドキュメント化が内包する楽しさについて / Let the Code Speak

PHP Conference 新潟 2025 のトーク用スライドです。
自己ドキュメント化について語っています。

https://fortee.jp/phpconniigata-2025/proposal/18ea69a7-d6e4-44a3-b2d1-080c1ba9dc88

本トークでは自己ドキュメント化をテーマに、それが内包する楽しさについて紐解いていきます。

自己ドキュメント化は開発者の知的好奇心を満たすプロセスであり、コードを通じて思考を整理し、意図を明確に表現する行為そのものが、新たな発見や学びをもたらします。それは単なる効率化の手段にとどまらず、創造性や知的満足感を生むプロセスであることに疑いはありません。

本トークでは自己ドキュメント化の具体例をPHPコードを用いて示し、その具体的な手法について解説いたします。さらに、自己ドキュメント化に楽しさを感じにくくなる場面にも注目し、その要因を分析します。そして、それを克服し、継続的な好奇心と満足感を支えるための環境づくりについても考察します。

この取り組みを通じて自己ドキュメント化に潜む楽しさをより深く理解し、開発が個人やチームにとって豊かで意義のある活動へと昇華することを目指します。

# URL
YouTube: https://www.youtube.com/c/narusemi
HomePage: https://nrslib.com
Twitter: https://twitter.com/nrslib
Instagram: https://www.instagram.com/nrslib/

Avatar for nrs

nrs

May 30, 2025
Tweet

More Decks by nrs

Other Decks in Programming

Transcript

  1. function check($c, $t, $s) { if ($t < 9 ||

    $t > 18) return false; if ($s == 1 && count($c->getP()) >= 30) return false; if ($s == 2 && count($c->getP()) >= 20) return false; if ($c->age < 3 && $s != 1) return false; return true; }
  2. function check($c, $t, $s) { if ($t < 9 ||

    $t > 18) return false; if ($s == 1 && count($c->getP()) >= 30) return false; if ($s == 2 && count($c->getP()) >= 20) return false; if ($c->age < 3 && $s != 1) return false; return true; } なにやってんだこれー?
  3. class EnrollmentService { public function canAcceptNewChild( Classroom $classroom, int $desiredStartHour,

    int $childAge): bool { if ($this->isOutsideOperatingHours($desiredStartHour)) { return false; } if ($this->isClassroomAtCapacity($classroom)) { return false; } if ($this->isToddlerRequiringSpecializedCare($childAge, $classroom)) { return false; } return true; } :
  4. class EnrollmentService { public function canAcceptNewChild( Classroom $classroom, int $desiredStartHour,

    int $childAge): bool { if ($this->isOutsideOperatingHours($desiredStartHour)) { return false; } if ($this->isClassroomAtCapacity($classroom)) { return false; } if ($this->isToddlerRequiringSpecializedCare($childAge, $classroom)) { return false; } return true; } : 子どもを受け入れられるか
  5. class EnrollmentService { public function canAcceptNewChild( Classroom $classroom, int $desiredStartHour,

    int $childAge): bool { if ($this->isOutsideOperatingHours($desiredStartHour)) { return false; } if ($this->isClassroomAtCapacity($classroom)) { return false; } if ($this->isToddlerRequiringSpecializedCare($childAge, $classroom)) { return false; } return true; } : クラス
  6. class EnrollmentService { public function canAcceptNewChild( Classroom $classroom, int $desiredStartHour,

    int $childAge): bool { if ($this->isOutsideOperatingHours($desiredStartHour)) { return false; } if ($this->isClassroomAtCapacity($classroom)) { return false; } if ($this->isToddlerRequiringSpecializedCare($childAge, $classroom)) { return false; } return true; } : 何時からを希望? 何歳?
  7. class EnrollmentService { public function canAcceptNewChild( Classroom $classroom, int $desiredStartHour,

    int $childAge): bool { if ($this->isOutsideOperatingHours($desiredStartHour)) { return false; } if ($this->isClassroomAtCapacity($classroom)) { return false; } if ($this->isToddlerRequiringSpecializedCare($childAge, $classroom)) { return false; } return true; } : 時間外じゃないかの確認
  8. class EnrollmentService { public function canAcceptNewChild( Classroom $classroom, int $desiredStartHour,

    int $childAge): bool { if ($this->isOutsideOperatingHours($desiredStartHour)) { return false; } if ($this->isClassroomAtCapacity($classroom)) { return false; } if ($this->isToddlerRequiringSpecializedCare($childAge, $classroom)) { return false; } return true; } : クラスのキャパを確認
  9. class EnrollmentService { public function canAcceptNewChild( Classroom $classroom, int $desiredStartHour,

    int $childAge): bool { if ($this->isOutsideOperatingHours($desiredStartHour)) { return false; } if ($this->isClassroomAtCapacity($classroom)) { return false; } if ($this->isToddlerRequiringSpecializedCare($childAge, $classroom)) { return false; } return true; } : 専門的なケアが必要?
  10. private function isOutsideOperatingHours(int $hour): bool { return $hour < 9

    || $hour > 18; } private function isClassroomAtCapacity(Classroom $classroom): bool { return count($classroom->getEnrolledChildren()) >= $classroom->getMaxCapacity(); } private function isToddlerRequiringSpecializedCare(int $childAge, Classroom $classroom) : bool { return $childAge < 3 && !$classroom->isToddlerSpecialized(); }
  11. private function isOutsideOperatingHours(int $hour): bool { return $hour < 9

    || $hour > 18; } private function isClassroomAtCapacity(Classroom $classroom): bool { return count($classroom->getEnrolledChildren()) >= $classroom->getMaxCapacity(); } private function isToddlerRequiringSpecializedCare(int $childAge, Classroom $classroom) : bool { return $childAge < 3 && !$classroom->isToddlerSpecialized(); } 9時〜18時
  12. private function isOutsideOperatingHours(int $hour): bool { return $hour < 9

    || $hour > 18; } private function isClassroomAtCapacity(Classroom $classroom): bool { return count($classroom->getEnrolledChildren()) >= $classroom->getMaxCapacity(); } private function isToddlerRequiringSpecializedCare(int $childAge, Classroom $classroom) : bool { return $childAge < 3 && !$classroom->isToddlerSpecialized(); } 教室キャパと人数確認
  13. private function isOutsideOperatingHours(int $hour): bool { return $hour < 9

    || $hour > 18; } private function isClassroomAtCapacity(Classroom $classroom): bool { return count($classroom->getEnrolledChildren()) >= $classroom->getMaxCapacity(); } private function isToddlerRequiringSpecializedCare(int $childAge, Classroom $classroom) : bool { return $childAge < 3 && !$classroom->isToddlerSpecialized(); } 3歳未満で幼児向けに特化されてない
  14. class EnrollmentPolicy { public function canAccept(Child $child, Classroom $classroom, int

    $desiredStartHour) : EnrollmentResult { if ($desiredStartHour < 9 || $desiredStartHour > 18) { return EnrollmentResult::rejected('outside_operating_hours'); } if (!$classroom->hasCapacityFor($child)) { return EnrollmentResult::rejected('classroom_at_capacity'); } if ($child->getAge() < 3 && !$classroom->isToddlerSpecialized()) { return EnrollmentResult::rejected('classroom_not_suitable_for_age'); } return EnrollmentResult::accepted(); } }
  15. class EnrollmentPolicy { public function canAccept(Child $child, Classroom $classroom, int

    $desiredStartHour) : EnrollmentResult { if ($desiredStartHour < 9 || $desiredStartHour > 18) { return EnrollmentResult::rejected('outside_operating_hours'); } if (!$classroom->hasCapacityFor($child)) { return EnrollmentResult::rejected('classroom_at_capacity'); } if ($child->getAge() < 3 && !$classroom->isToddlerSpecialized()) { return EnrollmentResult::rejected('classroom_not_suitable_for_age'); } return EnrollmentResult::accepted(); } } Serviceとは別軸の処理に切り出し
  16. class EnrollmentPolicy { public function canAccept(Child $child, Classroom $classroom, int

    $desiredStartHour) : EnrollmentResult { if ($desiredStartHour < 9 || $desiredStartHour > 18) { return EnrollmentResult::rejected('outside_operating_hours'); } if (!$classroom->hasCapacityFor($child)) { return EnrollmentResult::rejected('classroom_at_capacity'); } if ($child->getAge() < 3 && !$classroom->isToddlerSpecialized()) { return EnrollmentResult::rejected('classroom_not_suitable_for_age'); } return EnrollmentResult::accepted(); } } true / false から脱却
  17. class EnrollmentResult { private bool $accepted; private ?string $rejectionReason; :

    public static function accepted(): self { return new self(true); } public static function rejected(string $reason): self { return new self(false, $reason); } }
  18. class Age { private int $value; public function __construct(int $value)

    { $this->value = $value; } public function isUnderThree(): bool { return $this->value < 3; } } class DesiredStartTime { private int $hour; public function isWithinOperatingHours(): bool { return $this->hour >= 9 && $this->hour <= 18; } } 一長一短に思う
  19. function recordAttendance($child_id, $action, $timestamp) { $db = new PDO(‘sqlite:attendance.db’); $today

    = date(‘Y-m-d’, $timestamp); // 今日の記録を取得 $stmt = $db->prepare(“SELECT * FROM attendance WHERE child_id = ? AND date = ?”); $stmt->execute([$child_id, $today]); $record = $stmt->fetch(); if ($action == ‘checkin’) { if ($record && $record[‘checkin_time’]) { return [‘success’ => false, ‘message’ => ‘既に登園済みです’]; } if ($record) { $stmt = $db->prepare(“UPDATE attendance SET checkin_time = ? WHERE id = ?”); $stmt->execute([date(‘H:i:s’, $timestamp), $record[‘id’]]); } else { $stmt = $db->prepare(“INSERT INTO attendance (child_id, date, checkin_time) VALUES (?, ?, ?)”) $stmt->execute([$child_id, $today, date(‘H:i:s’, $timestamp)]); } return [‘success’ => true, ‘message’ => ‘登園を記録しました’]; } :
  20. function recordAttendance($child_id, $action, $timestamp) { $db = new PDO(‘sqlite:attendance.db’); $today

    = date(‘Y-m-d’, $timestamp); // 今日の記録を取得 $stmt = $db->prepare(“SELECT * FROM attendance WHERE child_id = ? AND date = ?”); $stmt->execute([$child_id, $today]); $record = $stmt->fetch(); if ($action == ‘checkin’) { if ($record && $record[‘checkin_time’]) { return [‘success’ => false, ‘message’ => ‘既に登園済みです’]; } if ($record) { $stmt = $db->prepare(“UPDATE attendance SET checkin_time = ? WHERE id = ?”); $stmt->execute([date(‘H:i:s’, $timestamp), $record[‘id’]]); } else { $stmt = $db->prepare(“INSERT INTO attendance (child_id, date, checkin_time) VALUES (?, ?, ?)”) $stmt->execute([$child_id, $today, date(‘H:i:s’, $timestamp)]); } return [‘success’ => true, ‘message’ => ‘登園を記録しました’]; } : ちゃんと読めば分かる →ちゃんと読まないとわからん
  21. function recordAttendance($child_id, $action, $timestamp) { $db = new PDO(‘sqlite:attendance.db’); $today

    = date(‘Y-m-d’, $timestamp); // 今日の記録を取得 $stmt = $db->prepare(“SELECT * FROM attendance WHERE child_id = ? AND date = ?”); $stmt->execute([$child_id, $today]); $record = $stmt->fetch(); if ($action == ‘checkin’) { if ($record && $record[‘checkin_time’]) { return [‘success’ => false, ‘message’ => ‘既に登園済みです’]; } if ($record) { $stmt = $db->prepare(“UPDATE attendance SET checkin_time = ? WHERE id = ?”); $stmt->execute([date(‘H:i:s’, $timestamp), $record[‘id’]]); } else { $stmt = $db->prepare(“INSERT INTO attendance (child_id, date, checkin_time) VALUES (?, ?, ?)”) $stmt->execute([$child_id, $today, date(‘H:i:s’, $timestamp)]); } return [‘success’ => true, ‘message’ => ‘登園を記録しました’]; } : 配列かぁ
  22. public function recordAttendance(int $childId, string $action, DateTime $timestamp) : AttendanceResult

    { $today = $timestamp->format('Y-m-d'); switch ($action) { case 'checkin': return $this->processCheckIn($childId, $today, $timestamp); case 'checkout': return $this->processCheckOut($childId, $today, $timestamp); default: return new AttendanceResult(false, '無効な操作です'); } }
  23. public function recordAttendance(int $childId, string $action, DateTime $timestamp) : AttendanceResult

    { $today = $timestamp->format('Y-m-d'); switch ($action) { case 'checkin': return $this->processCheckIn($childId, $today, $timestamp); case 'checkout': return $this->processCheckOut($childId, $today, $timestamp); default: return new AttendanceResult(false, '無効な操作です'); } } 読む気になる!!
  24. public function recordAttendance(int $childId, string $action, DateTime $timestamp) : AttendanceResult

    { $today = $timestamp->format('Y-m-d'); switch ($action) { case 'checkin': return $this->processCheckIn($childId, $today, $timestamp); case 'checkout': return $this->processCheckOut($childId, $today, $timestamp); default: return new AttendanceResult(false, '無効な操作です'); } } 登園
  25. public function recordAttendance(int $childId, string $action, DateTime $timestamp) : AttendanceResult

    { $today = $timestamp->format('Y-m-d'); switch ($action) { case 'checkin': return $this->processCheckIn($childId, $today, $timestamp); case 'checkout': return $this->processCheckOut($childId, $today, $timestamp); default: return new AttendanceResult(false, '無効な操作です'); } } 降園
  26. private function processCheckIn(int $childId, string $date, DateTime $timestamp): AttendanceResult {

    $record = $this->dao->findTodaysRecord($childId, $date); if ($record && $record->checkinTime) { return new AttendanceResult(false, '既に登園済みです'); } if (!$record) { $record = new AttendanceRecord(); $record->childId = $childId; $record->date = $date; } $record->checkinTime = $timestamp->format('H:i:s'); $this->dao->save($record); return new AttendanceResult(true, '登園を記録しました'); }
  27. private function processCheckIn(int $childId, string $date, DateTime $timestamp): AttendanceResult {

    $record = $this->dao->findTodaysRecord($childId, $date); if ($record && $record->checkinTime) { return new AttendanceResult(false, '既に登園済みです'); } if (!$record) { $record = new AttendanceRecord(); $record->childId = $childId; $record->date = $date; } $record->checkinTime = $timestamp->format('H:i:s'); $this->dao->save($record); return new AttendanceResult(true, '登園を記録しました'); } 意図を語ってくれてる (SQLの意図を理解しなくていい)
  28. public function recordAttendance(ChildId $childId, AttendanceAction $action, DateTime $timestamp): AttendanceResult {

    $todaysRecord = $this->repository->findTodaysRecord($childId); try { if ($action->isCheckIn()) { $record = $todaysRecord ?: new AttendanceRecord($childId, new Date($timestamp)); $record->recordCheckIn($timestamp); $this->repository->save($record); return AttendanceResult::checkInSuccess(); } if ($action->isCheckOut()) { if (!$todaysRecord) { return AttendanceResult::notCheckedIn(); } $todaysRecord->recordCheckOut($timestamp); $this->repository->save($todaysRecord); return AttendanceResult::checkOutSuccess(); } :
  29. public function recordAttendance(ChildId $childId, AttendanceAction $action, DateTime $timestamp): AttendanceResult {

    $todaysRecord = $this->repository->findTodaysRecord($childId); try { if ($action->isCheckIn()) { $record = $todaysRecord ?: new AttendanceRecord($childId, new Date($timestamp)); $record->recordCheckIn($timestamp); $this->repository->save($record); return AttendanceResult::checkInSuccess(); } if ($action->isCheckOut()) { if (!$todaysRecord) { return AttendanceResult::notCheckedIn(); } $todaysRecord->recordCheckOut($timestamp); $this->repository->save($todaysRecord); return AttendanceResult::checkOutSuccess(); } : データを主語でなく、モデルを主語に
  30. public function recordAttendance(ChildId $childId, AttendanceAction $action, DateTime $timestamp): AttendanceResult {

    $todaysRecord = $this->repository->findTodaysRecord($childId); try { if ($action->isCheckIn()) { $record = $todaysRecord ?: new AttendanceRecord($childId, new Date($timestamp)); $record->recordCheckIn($timestamp); $this->repository->save($record); return AttendanceResult::checkInSuccess(); } if ($action->isCheckOut()) { if (!$todaysRecord) { return AttendanceResult::notCheckedIn(); } $todaysRecord->recordCheckOut($timestamp); $this->repository->save($todaysRecord); return AttendanceResult::checkOutSuccess(); } : データを確認するのでなく、オブジェクトに移譲
  31. function validatePickup($person_id, $child_id, $current_time) { $db = new PDO('sqlite:nursery.db'); //

    子供の情報を取得 $stmt = $db->prepare("SELECT * FROM children WHERE id = ? AND status = 'present'"); $stmt->execute([$child_id]); $child = $stmt->fetch(); if (!$child) { return ['success' => false, 'message' => '子供が見つからないか、まだ登園していません']; } // 迎えに来た人の情報を取得 $stmt = $db->prepare("SELECT * FROM persons WHERE id = ?"); $stmt->execute([$person_id]); $person = $stmt->fetch(); if (!$person) { return ['success' => false, 'message' => '迎えに来た方の情報が見つかりません']; } // 親または保護者の場合は常に許可 if ($person['type'] == 'parent' || $person['type'] == 'guardian') { return ['success' => true, 'message' => 'お迎え可能です']; } // 事前に承認された人の場合 $stmt = $db->prepare("SELECT * FROM authorized_persons WHERE child_id = ? AND person_id = ? AND is_active = 1"); $stmt->execute([$child_id, $person_id]); $authorized = $stmt->fetch(); if ($authorized) { return ['success' => true, 'message' => 'お迎え可能です']; } // 緊急連絡先の場合(15分前までに連絡が必要) if ($person['type'] == 'emergency') { $stmt = $db->prepare("SELECT * FROM pickup_notifications WHERE child_id = ? AND person_id = ? AND notify_time <= ?"); $notify_deadline = $current_time - (15 * 60); // 15分前 $stmt->execute([$child_id, $person_id, $notify_deadline]); $notification = $stmt->fetch(); if ($notification) { return ['success' => true, 'message' => 'お迎え可能です']; } else { return ['success' => false, 'message' => '緊急連絡先の方は15分前までにご連絡ください']; } } return ['success' => false, 'message' => 'お迎えの権限がありません']; }
  32. public function validatePickup(int $personId, int $childId, DateTime $currentTime): PickupResult {

    $child = $this->childDAO->find($childId); if (!$child || !$child->isPresentAtNursery()) { return new PickupResult(false, '子供が見つからないか、まだ登園していません'); } $person = $this->personDAO->find($personId); if (!$person) { return new PickupResult(false, '迎えに来た方の情報が見つかりません'); } if ($this->canPickupChild($person, $childId, $currentTime)) { return new PickupResult(true, 'お迎え可能です'); } return new PickupResult(false, 'お迎えの権限がありません'); }
  33. public function validatePickup(int $personId, int $childId, DateTime $currentTime): PickupResult {

    $child = $this->childDAO->find($childId); if (!$child || !$child->isPresentAtNursery()) { return new PickupResult(false, '子供が見つからないか、まだ登園していません'); } $person = $this->personDAO->find($personId); if (!$person) { return new PickupResult(false, '迎えに来た方の情報が見つかりません'); } if ($this->canPickupChild($person, $childId, $currentTime)) { return new PickupResult(true, 'お迎え可能です'); } return new PickupResult(false, 'お迎えの権限がありません'); } 同じ内容でも見るべき場所がここであると語っている
  34. private function canPickupChild(Person $person, int $childId, DateTime $currentTime): bool {

    if ($person->isParentOrGuardian()) { return true; } if ($this->authorizationDAO->isAuthorized($childId, $person->id)) { return true; } if ($person->isEmergencyContact() && $this->hasValidEmergencyNotification($childId, $person->id, $currentTime)) { return true; } return false; }
  35. 保護者か? private function canPickupChild(Person $person, int $childId, DateTime $currentTime): bool

    { if ($person->isParentOrGuardian()) { return true; } if ($this->authorizationDAO->isAuthorized($childId, $person->id)) { return true; } if ($person->isEmergencyContact() && $this->hasValidEmergencyNotification($childId, $person->id, $currentTime)) { return true; } return false; }
  36. private function canPickupChild(Person $person, int $childId, DateTime $currentTime): bool {

    if ($person->isParentOrGuardian()) { return true; } if ($this->authorizationDAO->isAuthorized($childId, $person->id)) { return true; } if ($person->isEmergencyContact() && $this->hasValidEmergencyNotification($childId, $person->id, $currentTime)) { return true; } return false; } 権限はあるか
  37. private function canPickupChild(Person $person, int $childId, DateTime $currentTime): bool {

    if ($person->isParentOrGuardian()) { return true; } if ($this->authorizationDAO->isAuthorized($childId, $person->id)) { return true; } if ($person->isEmergencyContact() && $this->hasValidEmergencyNotification($childId, $person->id, $currentTime)) { return true; } return false; } 緊急連絡先であり、有効な緊急通知権限をもっているか
  38. class PickupAuthorizationPolicy { : public function canPickUp(Person $person, Child $child,

    DateTime $requestTime): PickupAuthorization { if ($person->isParentOf($child) || $person->isGuardianOf($child)) { return PickupAuthorization::approved(); } if ($child->hasAuthorizedPerson($person)) { return PickupAuthorization::approved(); } if ($person->isEmergencyContactFor($child)) { $notificationDeadline = (clone $requestTime)->modify('-15 minutes'); if ($this->notificationRepository->hasAdvanceNotificationFor( $child->getId(), $person->getId(), $notificationDeadline)) { return PickupAuthorization::approved(); } return PickupAuthorization::denied('emergency_no_notification'); } return PickupAuthorization::denied('unauthorized_person'); } }
  39. class PickupAuthorizationPolicy { : public function canPickUp(Person $person, Child $child,

    DateTime $requestTime): PickupAuthorization { if ($person->isParentOf($child) || $person->isGuardianOf($child)) { return PickupAuthorization::approved(); } if ($child->hasAuthorizedPerson($person)) { return PickupAuthorization::approved(); } if ($person->isEmergencyContactFor($child)) { $notificationDeadline = (clone $requestTime)->modify('-15 minutes'); if ($this->notificationRepository->hasAdvanceNotificationFor( $child->getId(), $person->getId(), $notificationDeadline)) { return PickupAuthorization::approved(); } return PickupAuthorization::denied('emergency_no_notification'); } return PickupAuthorization::denied('unauthorized_person'); } } <「お迎えできるかは私を見なさい」
  40. 94 • X ◦ @nrslib • HomePage ◦ https://nrslib.com/ •

    YouTube ◦ https://www.youtube.com/@nrslib おしまい