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
$nl ~僕が見た”最高”のPHPコード~
Search
kokuyouwind
April 16, 2016
Programming
0
23
$nl ~僕が見た”最高”のPHPコード~
NL名古屋
で発表したときのスライドです。
本当は10分の発表枠だったんですが、ぶっちぎりでオーバーした挙句予備スライドまで話しました。
内容については…お察し下さい。
kokuyouwind
April 16, 2016
Tweet
Share
More Decks by kokuyouwind
See All by kokuyouwind
Let's use LLMs from Ruby 〜 Refine RBS types using LLM 〜
kokuyouwind
0
5.6k
APMをちゃんと使おうとしたら、いつのまにか独自gemを作っていた話
kokuyouwind
0
660
RBS meets LLMs - Type inference using LLM
kokuyouwind
0
760
オンラインボードゲームを作りたい人生だった
kokuyouwind
0
390
1年間本番運用してわかった、スタートアップこそAWS Copilot CLIを使うべきNつの理由
kokuyouwind
2
11k
なるべく楽したいAWSセキュリティ
kokuyouwind
1
46
Railsパフォーマンス・チューニング入門
kokuyouwind
0
230
Rubyパターンマッチに闇の力が備わり最強に見える
kokuyouwind
0
66
Slackワークフロー活用術
kokuyouwind
0
73
Other Decks in Programming
See All in Programming
Amazon Bedrock Agentsを用いてアプリ開発してみた!
har1101
0
330
WebフロントエンドにおけるGraphQL(あるいはバックエンドのAPI)との向き合い方 / #241106_plk_frontend
izumin5210
4
1.4k
Snowflake x dbtで作るセキュアでアジャイルなデータ基盤
tsoshiro
2
520
CSC509 Lecture 09
javiergs
PRO
0
140
카카오페이는 어떻게 수천만 결제를 처리할까? 우아한 결제 분산락 노하우
kakao
PRO
0
110
cmp.Or に感動した
otakakot
1
120
Jakarta EE meets AI
ivargrimstad
0
530
Macとオーディオ再生 2024/11/02
yusukeito
0
370
Click-free releases & the making of a CLI app
oheyadam
2
110
レガシーシステムにどう立ち向かうか 複雑さと理想と現実/vs-legacy
suzukihoge
14
2.2k
「今のプロジェクトいろいろ大変なんですよ、app/services とかもあって……」/After Kaigi on Rails 2024 LT Night
junk0612
5
2.1k
Laravel や Symfony で手っ取り早く OpenAPI のドキュメントを作成する
azuki
2
110
Featured
See All Featured
Keith and Marios Guide to Fast Websites
keithpitt
409
22k
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
25
1.8k
Cheating the UX When There Is Nothing More to Optimize - PixelPioneers
stephaniewalter
280
13k
GraphQLの誤解/rethinking-graphql
sonatard
67
10k
個人開発の失敗を避けるイケてる考え方 / tips for indie hackers
panda_program
93
16k
How to Create Impact in a Changing Tech Landscape [PerfNow 2023]
tammyeverts
47
2.1k
Writing Fast Ruby
sferik
627
61k
Product Roadmaps are Hard
iamctodd
PRO
49
11k
The Invisible Side of Design
smashingmag
298
50k
Into the Great Unknown - MozCon
thekraken
32
1.5k
Practical Tips for Bootstrapping Information Extraction Pipelines
honnibal
PRO
10
720
Stop Working from a Prison Cell
hatefulcrawdad
267
20k
Transcript
$nl 〜僕が⾒た" 最⾼" のPHP コード〜 黒曜 @kokuyouwind http://kokuyouwind.com/a
▲ スライドのリンクは常に表示されています スライドめくりもリンクされます http://kokuyouwind.com/a
$ whoami 黒曜 / 株式会社Misoca に転職して半年 現職: Ruby on Rails/React
前職: PHP/Scala 趣味: OCaml @kokuyouwind http://kokuyouwind.com/a
宣伝 プログラマ向けの謎解きゲームを作りました http://kokuyouwind.com/a
宣伝 Github で公開中 「プログラマ 脱出ゲーム」でググると多分出てくる http://kokuyouwind.com/b http://kokuyouwind.com/a
本題 http://kokuyouwind.com/a
RubyKaigi2015 http://kokuyouwind.com/a
http://kokuyouwind.com/a
http://kokuyouwind.com/a
私が⼈⽣で⾒た 最悪のRuby コード http://kokuyouwind.com/a
http://kokuyouwind.com/a
聞きに⾏った http://kokuyouwind.com/a
感想 実際、割と酷かった に動画がある 教訓もあり、良い発表だった RubyKaigi のセッションページ http://kokuyouwind.com/a
⼀つ思ったこと http://kokuyouwind.com/a
⾃分が⾒た PHP コードは これより酷かった! ( 主観) http://kokuyouwind.com/a
$nl 〜僕が⾒た" 最⾼" のPHP コード〜 黒曜 @kokuyouwind http://kokuyouwind.com/a
構成 http://kokuyouwind.com/a
独⾃フレームワーク! 構成 http://kokuyouwind.com/a
構成 /core /model - MVC のM /logic - 共有ロジック /for_xxx
/public - web 公開ディレクトリ /controller - MVC のC /view - MVC のV /template - Smarty テンプレート http://kokuyouwind.com/a
構成 /core /model - MVC のM /logic - 共有ロジック /for_xxx
/public - web 公開ディレクトリ /controller - MVC のC /view - MVC のV /template - Smarty テンプレート http://kokuyouwind.com/a
controller MVC のC… ではない! ⼤体のメイン処理を書く場所 変数名が⼤体ひどい 後述する 例外駆動プログラミングという技法がフル活⽤される 後述する とりあえずひどい
後述する http://kokuyouwind.com/a
構成 /core /model - MVC のM /logic - 共有ロジック /for_xxx
/public - web 公開ディレクトリ /controller - MVC のC メイン処理を書く場所 /view - MVC のV /template - Smarty テンプレート http://kokuyouwind.com/a
view MVC のV… ではない! 謎 そもそもテンプレートは別にある controller から受け取った値を表示⽤に加⼯する? ⼤体は受け流すだけ たまにネストのレベルを変えてて悶絶する
できることはController とほぼ⼀緒 たまにすごい処理が書いてあってビビる http://kokuyouwind.com/a
構成 /core /model - MVC のM /logic - 共有ロジック /for_xxx
/public - web 公開ディレクトリ /controller - MVC のC メイン処理を書く場所 /view - MVC のV 謎 /template - Smarty テンプレート http://kokuyouwind.com/a
model MVC のM… ではない! 配列を上⼿く扱うためのクラス ドメインモデルのデータはすべて配列で扱う (PHP の配列は連想配列) DB との⼊出⼒も扱う
PDO でクエリを直接投げる 独⾃のクエリビルダーが4 種類くらいある どこからでも$this->getModel('Name') で取れる http://kokuyouwind.com/a
model <?php class UserModel extends HogeFWModel { public function get($id,
$ignore_delete = false) { $table = $this->get_table_name($id); // ソフトシャーディング $query = "SELECT * FROM $table WHERE id = ?"; if (!$ignore_delete) { // deleted が未来のことがあるので、IS NULL だけではだめ $query .= "AND (deleted IS NULL OR deleted <= '$(data('Y-m-d h:i:s') } $records = $this->db->execute($query, $id); if ($records === false) { // 厳密にfalse ならSQL 接続エラー throw new SQLException('SQL 接続エラーです'); } elseif (!$records) { // クエリは成功したが中身が空 throw new NotFoundException(' ユーザーが⾒つかりませんでした'); } $user = $records[0]; // 他に必要な情報を補完する return $this->appendInfo($user); } } http://kokuyouwind.com/a
model // こんな感じで使う // call はメソッドの呼び出し結果をキャッシュする関数 $this->cache->call('User', 'get', $id); //
call には配列形式で渡しても良い $um = $this-> getModel('User'); $this->cache->call([$um, 'get'], $id); // 返ってくるのは配列( というかハッシュ) ['id' => '758', 'name' => ' 黒曜', 'registered' => '2016-04-16 15:50:00', // ⼤抵MySQL 形式 'last_login' => 1460789400, // たまにUnixTime が⼊っている 'attributes' => [ 'belongs' => [ 'Misoca' => ['id' => '331'] // どんどんネストする ], 'is_penalty' => 'none', // なぜか真偽値ではなく⽂字列 ], 'append_info' => [ // 3 段くらいネストしたなんらかの情報 ] ] http://kokuyouwind.com/a
構成 /core /model - MVC のM 配列の加⼯とDB 処理 /logic -
共有ロジック /for_xxx /public - web 公開ディレクトリ /controller - MVC のC メイン処理を書く場所 /view - MVC のV 謎 /template - Smarty テンプレート http://kokuyouwind.com/a
logic 共有ロジック… ではないものがほとんど ⼤体はcontroller の中身を分割したもの バージョン移⾏の際に、controller の処理を まるまるコピーしlogic クラスができたりした controller
でできることはlogic でもmodel でもできる たまにlogic やmodel から直接view に渡す値を 設定していてビビる どこからでも$this->getLogic('Name') で取れる http://kokuyouwind.com/a
構成 /core /model - MVC のM 配列の加⼯とDB 処理 /logic -
共有ロジック メイン処理を書く場所2 /for_xxx /public - web 公開ディレクトリ /controller - MVC のC メイン処理を書く場所 /view - MVC のV 謎 /template - Smarty テンプレート http://kokuyouwind.com/a
" 最⾼" のPHP コード http://kokuyouwind.com/a
" 最⾼" のPHP コード 単⼀のコンテンツを表示するページのController メイン処理のメソッドが3000 ⾏くらい 1 メソッドで3000 ⾏
うち2000 ⾏くらいがcatch 節 5 段くらいにネストした100 項⽬くらいの配列を構築 これが1 つのコンテンツを表す とりあえずvar_dump してデバッグする 5 環境くらいにfork して、それぞれ独⾃に進化している http://kokuyouwind.com/a
2 ⽂字変数 http://kokuyouwind.com/a
変数名が酷い <?php class ContentController extends HogeFWController { public function execute()
{ // ⼤体2 ⽂字変数 $cm = $this->getModel('Content'); $nl = $this->getLogic('Normalize'); $cont = $this->cache->call([$cm, 'get'], $this->getAttribute('i $cont = $nl->norm($cont); // 間に300 ⾏くらい挟まる if($has_body) { $cont = $nl->normBody($cont); } // 間に500 ⾏くらい if ($need_next) { // さっきの$nl を上書きする $nl = $this->getLogic('NextContent'); $content['next'] = $nl->calc($content); } // ... まだ続きます http://kokuyouwind.com/a
変数名が酷い // ... 続き // 間に300 ⾏くらい // さっきの分岐を通ってると死ぬバグ $content['append']
= $nl->normAppend($content); // 間に200 ⾏くらい if ($need_extra) { // 明らかに$nl じゃないけど$nl を上書きする $nl = $this->getLogic('Extra'); $content['extra'] = $nl->compute($content); } // 間に300 ⾏くらい // view にcontent を受け渡す(Model, Logic からも可能) $this->setAttribute('content', $content); return; } } http://kokuyouwind.com/a
例外駆動プログラミング http://kokuyouwind.com/a
例外駆動プログラミング <?php class ContentController extends HogeFWController { public function execute()
{ try { $cm = $this->getModel('Content'); $content = $this->cache->call($cm, 'get', $this->getAttribute if(!this->user) { throw new NotLoginException(); } // ログイン時、コンテンツありの処理が1000 ⾏くらい続く } catch (NotLoginException $e) { // ⾮ログイン時の処理 if ($cm->isAllowGuest($content)) { // ⾮ログイン時、コンテンツ閲覧可の処理が800 ⾏くらい続く // うち600 ⾏くらいはログイン時処理のコピペ } else { // view/content_error_view を描画する return 'content_error'; } } } } http://kokuyouwind.com/a
http://kokuyouwind.com/a
どうしてこうなったか そもそも2007 年ごろから作られ始めたコード PHP5.2 の頃で名前空間すらない モダンで実績のあるフレームワークもあまりない 初期は数名での開発だった レビュー⽂化のないカウボーイプログラミング テスト⽂化もない 新機能や企画優先で継ぎ⾜し実装が横⾏した
品質より納期を優先するスケジュール 追加された機能が消せず、膨れる⼀⽅ http://kokuyouwind.com/a
どうやって戦っていたか レビュー⽂化が根付き始めた時期の⼊社だった 当時はsvn でFisheye/Crucible を使っていた PHPUnit も先輩が広めていた 複雑すぎてテスト不可能な部分が多すぎた 新規に書くコードはテストを書く、という共通認識 ⼿作業確認によるリファクタリング(
リスク⾼) grep ⼒を⾼めて物理で殴る 最近はScala で書きなおそうとしている( はず) 転職したため現状は謎 http://kokuyouwind.com/a
教訓 機能追加偏重の⽂化は保守不能なコードを⽣み出しうる 無論、ユーザーに価値を届けることは⼤事 うまくバランスを取ることが重要 レビュー⽂化、テスト⽂化で品質を保つ スピードを上げ過ぎない「⾜枷」としての機能 無理な納期から開発チームを守る防⽕壁が必要 機能もコードも「捨てられる」ように作る 犠牲的アーキテクチャ 機能テスト
http://kokuyouwind.com/a
予備スライド http://kokuyouwind.com/a
他に⾯⽩かったコード バグにバグが重なって上⼿く動いているコード 1 ⽇が 60 * 60 * 60 *
24 秒ある 1 週間が 8 ⽇ある コメントと返り値が真逆 関数名は block 「過負荷ブロックされたらfalse が返ります」 実際は過負荷ブロック時にtrue が返っていた for が3 つネストした中に「continue $n; 」 100 ⾏くらいあるメソッド先頭に「return false; 」 http://kokuyouwind.com/a
構成 /core /model - MVC のM /logic - 共有ロジック /for_xxx
/public - web 公開ディレクトリ /controller - MVC のC /view - MVC のV /template - Smarty テンプレート http://kokuyouwind.com/a
public web からのエントリポイント apache でDirectory として指定される Controller をkick するphp ファイルが配置される
URL routes の数だけファイルが置かれる Router? なにそれおいしいの? 画像/js/css などの静的ファイルもここに配置される http://kokuyouwind.com/a
構成 /core /model - MVC のM /logic - 共有ロジック /for_xxx
/public - web 公開ディレクトリ /controller - MVC のC メイン処理を書く場所 /view - MVC のV 謎 /template - Smarty テンプレート http://kokuyouwind.com/a
template Smarty のテンプレートを置く場所 ネストが⾮常に深い 3 重if の中身が数⼗⾏とかはざら ロジック分離? なにそれ美味し(ry 多⾔語対応の影響でさらにカオスになった
⽇本語以外では出さない、みたいな分岐が そこら中にある http://kokuyouwind.com/a