Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

2 Profile nrs(成瀬 允宣) @nrslib コドモンのCTO 趣味: カンファレンス講演 学生支援 小学校支援 写真

Slide 3

Slide 3 text

3 今日のお話

Slide 4

Slide 4 text

4 今日のお話

Slide 5

Slide 5 text

5 今日のお話

Slide 6

Slide 6 text

6 今日のお話

Slide 7

Slide 7 text

はじめに 自己ドキュメント化が内包する楽しさ コードで見る自己ドキュメント化 楽しさの根源 楽しくないを避ける環境づくり まとめ コードに語らせよう

Slide 8

Slide 8 text

はじめに 自己ドキュメント化が内包する楽しさ コードで見る自己ドキュメント化 楽しさの根源 楽しくないを避ける環境づくり まとめ コードに語らせよう

Slide 9

Slide 9 text

こんな経験ありませんか?

Slide 10

Slide 10 text

10 ● ドキュメントが陳腐化している こんな経験ありませんか?

Slide 11

Slide 11 text

11 ● ドキュメントがそもそもない こんな経験ありませんか?

Slide 12

Slide 12 text

12 ● Gitのログが最高にロック こんな経験ありませんか?

Slide 13

Slide 13 text

13 ● コメントに騙された こんな経験ありませんか?

Slide 14

Slide 14 text

こんな課題を解決するには?

Slide 15

Slide 15 text

15 ● コードはドキュメントと無関係に存在する こんな問題を解決するための道筋

Slide 16

Slide 16 text

16 ● コードをドキュメントにすればいい こんな問題を解決するための道筋

Slide 17

Slide 17 text

あれ? 楽しい……?

Slide 18

Slide 18 text

はじめに 自己ドキュメント化が内包する楽しさ コードで見る自己ドキュメント化 楽しさの根源 楽しくないを避ける環境づくり まとめ コードに語らせよう

Slide 19

Slide 19 text

19 ● 複雑なものをできるだけシンプルに ● 機能美との両立 ● 制限プレイ パズルに似た楽しさ

Slide 20

Slide 20 text

20 ● 頭の中のモヤモヤをコードで明文化 ● 「あ、これだよこれ!」というすっきり感 ● 「関連資料を見る必要がない!」 言語化の喜び

Slide 21

Slide 21 text

21 ● 受け取るのは自分だったりするけどな!!! 未来の誰かへの贈り物

Slide 22

Slide 22 text

はじめに 自己ドキュメント化が内包する楽しさ コードで見る自己ドキュメント化 楽しさの根源 楽しくないを避ける環境づくり まとめ コードに語らせよう

Slide 23

Slide 23 text

具体的にはどんなコードなのさ!

Slide 24

Slide 24 text

無口なコード

Slide 25

Slide 25 text

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; }

Slide 26

Slide 26 text

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; } なにやってんだこれー?

Slide 27

Slide 27 text

語り始めたコード

Slide 28

Slide 28 text

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; } :

Slide 29

Slide 29 text

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; } : 子どもを受け入れられるか

Slide 30

Slide 30 text

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; } : クラス

Slide 31

Slide 31 text

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; } : 何時からを希望? 何歳?

Slide 32

Slide 32 text

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; } : 時間外じゃないかの確認

Slide 33

Slide 33 text

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; } : クラスのキャパを確認

Slide 34

Slide 34 text

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; } : 専門的なケアが必要?

Slide 35

Slide 35 text

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(); }

Slide 36

Slide 36 text

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時

Slide 37

Slide 37 text

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(); } 教室キャパと人数確認

Slide 38

Slide 38 text

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歳未満で幼児向けに特化されてない

Slide 39

Slide 39 text

流暢なコード

Slide 40

Slide 40 text

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(); } }

Slide 41

Slide 41 text

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とは別軸の処理に切り出し

Slide 42

Slide 42 text

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 から脱却

Slide 43

Slide 43 text

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); } }

Slide 44

Slide 44 text

もはや喋りすぎ?

Slide 45

Slide 45 text

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; } } 一長一短に思う

Slide 46

Slide 46 text

無口なコード②

Slide 47

Slide 47 text

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’ => ‘登園を記録しました’]; } :

Slide 48

Slide 48 text

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’ => ‘登園を記録しました’]; } : ちゃんと読めば分かる →ちゃんと読まないとわからん

Slide 49

Slide 49 text

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’ => ‘登園を記録しました’]; } : 配列かぁ

Slide 50

Slide 50 text

語り始めたコード②

Slide 51

Slide 51 text

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, '無効な操作です'); } }

Slide 52

Slide 52 text

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, '無効な操作です'); } } 読む気になる!!

Slide 53

Slide 53 text

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, '無効な操作です'); } } 登園

Slide 54

Slide 54 text

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, '無効な操作です'); } } 降園

Slide 55

Slide 55 text

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, '登園を記録しました'); }

Slide 56

Slide 56 text

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の意図を理解しなくていい)

Slide 57

Slide 57 text

流暢なコード②

Slide 58

Slide 58 text

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(); } :

Slide 59

Slide 59 text

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(); } : データを主語でなく、モデルを主語に

Slide 60

Slide 60 text

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(); } : データを確認するのでなく、オブジェクトに移譲

Slide 61

Slide 61 text

無口なコード③

Slide 62

Slide 62 text

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' => 'お迎えの権限がありません']; }

Slide 63

Slide 63 text

語り始めたコード③

Slide 64

Slide 64 text

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, 'お迎えの権限がありません'); }

Slide 65

Slide 65 text

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, 'お迎えの権限がありません'); } 同じ内容でも見るべき場所がここであると語っている

Slide 66

Slide 66 text

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; }

Slide 67

Slide 67 text

保護者か? 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; }

Slide 68

Slide 68 text

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; } 権限はあるか

Slide 69

Slide 69 text

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; } 緊急連絡先であり、有効な緊急通知権限をもっているか

Slide 70

Slide 70 text

流暢なコード③

Slide 71

Slide 71 text

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'); } }

Slide 72

Slide 72 text

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'); } } <「お迎えできるかは私を見なさい」

Slide 73

Slide 73 text

実はコード以外も語るよ!

Slide 74

Slide 74 text

74 ● 重要なのは実は構造 何かを伝えるのは文字に限らない PHPカンファレンス新潟2025用 コードは大きく3パターン 1stだけは過剰パターンがある

Slide 75

Slide 75 text

はじめに 自己ドキュメント化が内包する楽しさ コードで見る自己ドキュメント化 楽しさの根源 楽しくないを避ける環境づくり まとめ コードに語らせよう

Slide 76

Slide 76 text

何が楽しいんだい?

Slide 77

Slide 77 text

77 ● 時間的プレッシャー 制限される楽しさ マリオだって制限時間がある RTAという文化がある

Slide 78

Slide 78 text

78 ● 今できる最高に挑戦 最大限への挑戦 自分との戦い アスリートの気持ち

Slide 79

Slide 79 text

どういうとき楽しくなくなるの?

Slide 80

Slide 80 text

80 ● 時間がなさすぎる ● 知識・スキルが不足しすぎている ● 最初の一歩が怖い 楽しくなくなる要素

Slide 81

Slide 81 text

楽しさを取り戻すには?

Slide 82

Slide 82 text

82 ● 時間がなさすぎるに対する解 楽しさを取り戻す方法 一般的に言われることは 丁寧に時間をかけて 保守性を高くする時間も含めて 工数にする ※開発者には楽観的工数を 言いがちな方もいる

Slide 83

Slide 83 text

83 ● 時間がなさすぎるに対する解 楽しさを取り戻す方法 (脳筋な)自分としては 時間をかけても 品質は変わらないので 常に最高を書けるよう精進せよ 物事の多くは ある程度までは知識ゲー

Slide 84

Slide 84 text

84 ● 知識・スキルが不足しすぎているに対する解 楽しさを取り戻す方法 そりゃペアプロよ あとはとにかく読むこと 今なら生成AIもあるしなー(※)

Slide 85

Slide 85 text

85 ● 最初の一歩が怖い 楽しさを取り戻す方法 そりゃペアプロよ

Slide 86

Slide 86 text

はじめに 自己ドキュメント化が内包する楽しさ コードで見る自己ドキュメント化 楽しさの根源 楽しくないを避ける環境づくり まとめ コードに語らせよう

Slide 87

Slide 87 text

87 ● それを支えるには環境づくりが重要 楽しさは失われやすい

Slide 88

Slide 88 text

88 ● チーム 環境づくり ・レビューで学び合い ・ペアプロでナレッジの共有 ・自己ドキュメント性という指標

Slide 89

Slide 89 text

89 ● 組織 環境づくり 文化として根付く組織作り ・評価/認知の仕組み ・学習/共有の場作り

Slide 90

Slide 90 text

はじめに 自己ドキュメント化が内包する楽しさ コードで見る自己ドキュメント化 楽しさの根源 楽しくないを避ける環境づくり まとめ コードに語らせよう

Slide 91

Slide 91 text

91 ● 自己ドキュメント化は…… まとめ 思考の整理、表現の喜び、問題への挑戦 人間の知的好奇心を満たす行為のひとつ コードが仕様書と胸を張って言えるコードを目指そう

Slide 92

Slide 92 text

92 仲間を探しています 子どもを取り巻く環境をテクノロジーの力でよりよいものにしていく仲間を大募集! ● 子育てしやすい環境です ○ 子育てにドメイン知識がありすぎるメンバー ○ フレックス/フルリモート可 ● 開発者体験が最高です ○ アジャイル(XP) ○ バーチャルオフィス(Gather) ○ ペアプロでコードについて議論できます ○ CTOが財布

Slide 93

Slide 93 text

93 「募集中ポジション」や 「カジュアル面談の申込み」から ぜひご連絡ください!

Slide 94

Slide 94 text

94 ● X ○ @nrslib ● HomePage ○ https://nrslib.com/ ● YouTube ○ https://www.youtube.com/@nrslib おしまい