Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
コードに語らせよう 自己ドキュメント化が内包する楽しさについて / Let the Code ...
Search
コドモン開発チーム
May 31, 2025
110
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
コードに語らせよう 自己ドキュメント化が内包する楽しさについて / Let the Code Speak: The Joy of Self-Documenting Code
コドモン開発チーム
May 31, 2025
More Decks by コドモン開発チーム
See All by コドモン開発チーム
アラート疲れからの脱却 - リソースタグで仕分けるSlack通知戦略 / Breaking Free from Alert Fatigue – A Slack Notification Strategy Using Resource Tags for Routing
codmoninc
0
31
SREに優しいTerraform構成 modulesとstateの組み方 / terraform-modules-state-for-sre
codmoninc
0
350
モノリスなプロダクトの「ほどよい」リプレイス戦略 / A "Just Right" Replacement Strategy for Monolithic Products
codmoninc
0
130
Don't Just Patch — MOTTAINAI! Learn Security from Laravel CVE Diffs
codmoninc
0
240
ソースコードで比較する React / Vue / Svelte の セキュリティ設計思想 / security design philosophy react vue svelte
codmoninc
5
640
少人数SREチームが、長寿なシステムを構築・運用するための取り組み / Efforts by a Small SRE Team to Build and Operate Long-Lived Systems
codmoninc
0
340
フルリモートのその先へ〜パパね、いつも家にいるけどちゃんとこうして働いてるよ〜 / Beyond Full Remote
codmoninc
0
650
多様な働き方を支えるチーム開発カルチャーと 今後の展望 / Team Development Culture Supporting Diverse Workstyles and Future Outlook
codmoninc
0
550
ペアプロ未経験・未知のスキル領域・フルリモートからでも挑戦できる? 40代転職者の実態 / pair-programming-remote-career-change
codmoninc
0
570
Featured
See All Featured
Highjacked: Video Game Concept Design
rkendrick25
PRO
1
410
Dealing with People You Can't Stand - Big Design 2015
cassininazir
367
27k
BBQ
matthewcrist
89
10k
Site-Speed That Sticks
csswizardry
13
1.2k
Keith and Marios Guide to Fast Websites
keithpitt
413
23k
Design and Strategy: How to Deal with People Who Don’t "Get" Design
morganepeng
133
19k
VelocityConf: Rendering Performance Case Studies
addyosmani
333
25k
Context Engineering - Making Every Token Count
addyosmani
9
1k
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
659
62k
SERP Conf. Vienna - Web Accessibility: Optimizing for Inclusivity and SEO
sarafernandez
2
1.5k
Code Review Best Practice
trishagee
74
20k
AI: The stuff that nobody shows you
jnunemaker
PRO
8
820
Transcript
コードに語らせよう 自己ドキュメント化が内包する楽しさについて nrs
2 Profile nrs(成瀬 允宣) @nrslib コドモンのCTO 趣味: カンファレンス講演 学生支援 小学校支援
写真
3 今日のお話
4 今日のお話
5 今日のお話
6 今日のお話
はじめに 自己ドキュメント化が内包する楽しさ コードで見る自己ドキュメント化 楽しさの根源 楽しくないを避ける環境づくり まとめ コードに語らせよう
はじめに 自己ドキュメント化が内包する楽しさ コードで見る自己ドキュメント化 楽しさの根源 楽しくないを避ける環境づくり まとめ コードに語らせよう
こんな経験ありませんか?
10 • ドキュメントが陳腐化している こんな経験ありませんか?
11 • ドキュメントがそもそもない こんな経験ありませんか?
12 • Gitのログが最高にロック こんな経験ありませんか?
13 • コメントに騙された こんな経験ありませんか?
こんな課題を解決するには?
15 • コードはドキュメントと無関係に存在する こんな問題を解決するための道筋
16 • コードをドキュメントにすればいい こんな問題を解決するための道筋
あれ? 楽しい……?
はじめに 自己ドキュメント化が内包する楽しさ コードで見る自己ドキュメント化 楽しさの根源 楽しくないを避ける環境づくり まとめ コードに語らせよう
19 • 複雑なものをできるだけシンプルに • 機能美との両立 • 制限プレイ パズルに似た楽しさ
20 • 頭の中のモヤモヤをコードで明文化 • 「あ、これだよこれ!」というすっきり感 • 「関連資料を見る必要がない!」 言語化の喜び
21 • 受け取るのは自分だったりするけどな!!! 未来の誰かへの贈り物
はじめに 自己ドキュメント化が内包する楽しさ コードで見る自己ドキュメント化 楽しさの根源 楽しくないを避ける環境づくり まとめ コードに語らせよう
具体的にはどんなコードなのさ!
無口なコード
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; }
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; } なにやってんだこれー?
語り始めたコード
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; } :
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; } : 子どもを受け入れられるか
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; } : クラス
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; } : 何時からを希望? 何歳?
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; } : 時間外じゃないかの確認
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; } : クラスのキャパを確認
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; } : 専門的なケアが必要?
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(); }
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時
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(); } 教室キャパと人数確認
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歳未満で幼児向けに特化されてない
流暢なコード
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(); } }
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とは別軸の処理に切り出し
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 から脱却
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); } }
もはや喋りすぎ?
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; } } 一長一短に思う
無口なコード②
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’ => ‘登園を記録しました’]; } :
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’ => ‘登園を記録しました’]; } : ちゃんと読めば分かる →ちゃんと読まないとわからん
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’ => ‘登園を記録しました’]; } : 配列かぁ
語り始めたコード②
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, '無効な操作です'); } }
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, '無効な操作です'); } } 読む気になる!!
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, '無効な操作です'); } } 登園
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, '無効な操作です'); } } 降園
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, '登園を記録しました'); }
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の意図を理解しなくていい)
流暢なコード②
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(); } :
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(); } : データを主語でなく、モデルを主語に
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(); } : データを確認するのでなく、オブジェクトに移譲
無口なコード③
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' => 'お迎えの権限がありません']; }
語り始めたコード③
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, 'お迎えの権限がありません'); }
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, 'お迎えの権限がありません'); } 同じ内容でも見るべき場所がここであると語っている
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; }
保護者か? 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; }
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; } 権限はあるか
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; } 緊急連絡先であり、有効な緊急通知権限をもっているか
流暢なコード③
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'); } }
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'); } } <「お迎えできるかは私を見なさい」
実はコード以外も語るよ!
74 • 重要なのは実は構造 何かを伝えるのは文字に限らない PHPカンファレンス新潟2025用 コードは大きく3パターン 1stだけは過剰パターンがある
はじめに 自己ドキュメント化が内包する楽しさ コードで見る自己ドキュメント化 楽しさの根源 楽しくないを避ける環境づくり まとめ コードに語らせよう
何が楽しいんだい?
77 • 時間的プレッシャー 制限される楽しさ マリオだって制限時間がある RTAという文化がある
78 • 今できる最高に挑戦 最大限への挑戦 自分との戦い アスリートの気持ち
どういうとき楽しくなくなるの?
80 • 時間がなさすぎる • 知識・スキルが不足しすぎている • 最初の一歩が怖い 楽しくなくなる要素
楽しさを取り戻すには?
82 • 時間がなさすぎるに対する解 楽しさを取り戻す方法 一般的に言われることは 丁寧に時間をかけて 保守性を高くする時間も含めて 工数にする ※開発者には楽観的工数を 言いがちな方もいる
83 • 時間がなさすぎるに対する解 楽しさを取り戻す方法 (脳筋な)自分としては 時間をかけても 品質は変わらないので 常に最高を書けるよう精進せよ 物事の多くは ある程度までは知識ゲー
84 • 知識・スキルが不足しすぎているに対する解 楽しさを取り戻す方法 そりゃペアプロよ あとはとにかく読むこと 今なら生成AIもあるしなー(※)
85 • 最初の一歩が怖い 楽しさを取り戻す方法 そりゃペアプロよ
はじめに 自己ドキュメント化が内包する楽しさ コードで見る自己ドキュメント化 楽しさの根源 楽しくないを避ける環境づくり まとめ コードに語らせよう
87 • それを支えるには環境づくりが重要 楽しさは失われやすい
88 • チーム 環境づくり ・レビューで学び合い ・ペアプロでナレッジの共有 ・自己ドキュメント性という指標
89 • 組織 環境づくり 文化として根付く組織作り ・評価/認知の仕組み ・学習/共有の場作り
はじめに 自己ドキュメント化が内包する楽しさ コードで見る自己ドキュメント化 楽しさの根源 楽しくないを避ける環境づくり まとめ コードに語らせよう
91 • 自己ドキュメント化は…… まとめ 思考の整理、表現の喜び、問題への挑戦 人間の知的好奇心を満たす行為のひとつ コードが仕様書と胸を張って言えるコードを目指そう
92 仲間を探しています 子どもを取り巻く環境をテクノロジーの力でよりよいものにしていく仲間を大募集! • 子育てしやすい環境です ◦ 子育てにドメイン知識がありすぎるメンバー ◦ フレックス/フルリモート可 •
開発者体験が最高です ◦ アジャイル(XP) ◦ バーチャルオフィス(Gather) ◦ ペアプロでコードについて議論できます ◦ CTOが財布
93 「募集中ポジション」や 「カジュアル面談の申込み」から ぜひご連絡ください!
94 • X ◦ @nrslib • HomePage ◦ https://nrslib.com/ •
YouTube ◦ https://www.youtube.com/@nrslib おしまい