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

組織もソフトウェアも難しく考えない、もっとシンプルな考え方で設計する #phpconfuk

組織もソフトウェアも難しく考えない、もっとシンプルな考え方で設計する #phpconfuk

Avatar for hideki kinjyo

hideki kinjyo PRO

November 03, 2025
Tweet

More Decks by hideki kinjyo

Other Decks in Programming

Transcript

  1. 自己紹介 • 金城秀樹 / きんじょうひでき • GitHub: @o0h / 𝕏

    : @o0h_ • 好きなFWはCakePHP • アイコンは美味しい鮭親子丼の写真です • 最近はPodcastをやっています • ハッシュタグ: #readlinefm  
  2. [ソ]フィードバック③ try { $fp = fopen($file, $mode); } catch (Throwable

    $e) { $ex = new RuntimeException( sprintf( 'Unable to open %s w/%s', $file, $mode, ), previous: $e); }   例外のthrowもフィードバック 反応が返る 何かをする
  3. 登壇した痕跡 残してぇ〜〜 フィードバックの働き -より俯瞰して-  34 行動の結果として フィードバックを得る  次の行動は、

    「目的」に照らし合わせて 調整される 「FFBなう」っ て呟いて! たくさん流れ てきた!! RePost.. RePost.. RePost.. RePost..
  4. フィードバックの働き -より俯瞰して-  35 行動の結果として フィードバックを得る  次の行動は、 「目的」に照らし合わせて 調整される

    登壇した痕跡 残してぇ〜〜 「FFBなう」っ て呟いて! あんま流れて こない。。。 早く!! 「FFBなう」って呟 いて!
  5. 契約書の 雛形を 更新したよ! 契約書の 雛形を 更新したよ! 組織で見るフィードバック  42 ★

    目的に影響を与えないフィードバックは影響を及ぼさない 経費精算 大変すぎる〜 ふーん 業務効率を 上げるぞ!
  6. [ソ]フィードバック $version = $this->getVersion(); if ($version->lt($minVersion)) { return false; }

    return true;   「バージョンどうですか?」 の入力 $version = $this->getVersion(); $version->lt($minVersion)
  7. [ソ]フィードバック $version = $this->getVersion(); if ($version->lt($minVersion)) { return false; }

    return true;   true or falseの フィードバック $version->lt($minVersion)
  8. [ソ]フィードバック $version = $this->getVersion(); if ($version->lt($minVersion)) { return false; }

    return true;   入力者側が、 フィードバック内容を見て 次のアクションを行う return false; return true;
  9. [ソ]フィードバック function isSupported() { $version = $this->getVersion(); if ( $version->lt($minVersion)

    ) { return false; } return true; }   この手続きは 「バージョンで判定する」 という目的がある function isSupported()
  10. 分析結果を踏まえて • 目的: 良い企画を考える(企画会議で提案する) • 現状の実装: CSからデータを取り寄せ、自分で解釈する • オーバーヘッド: •

    受け取り後の分析コスト • CSとの一往復(欲しい内容の指示、出力結果の受信)  69 ここのコストの高さが、 「組織の構造から生じる問題」の大きさ
  11. 分析結果を踏まえて • 目的: 良い企画を考える(企画会議で提案する) • 現状の実装: CSからデータを取り寄せ、自分で解釈する • オーバーヘッド: •

    受け取り後の分析コスト • CSとの一往復(欲しい内容の指示、出力結果の受信)  70 この構造を修正することで、 コストの総和を下げられないか?
  12. ソフトウェアで見るフィードバック $productId = $request->get('product_id'); $result = $this->productApiClient->request($productId); $statusCode = $result->getStatusCode();

    if ($statusCode >= 500) { return $this->error('Please try again later'); } elseif ($statusCode >= 400) { return $this->error('Unable to load product', 400); } $body = $result->getBody()->getContents(); $data = json_decode($body, true); if ($data['status'] === 'invalid') { return $this->error('Product unavailable', 404); } return $this->success(['product' => $data['detail']]);  
  13. ソフトウェアで見るフィードバック $productId = $request->get('product_id'); $result = $this->productApiClient->request($productId); $statusCode = $result->getStatusCode();

    if ($statusCode >= 500) { return $this->error('Please try again later'); } elseif ($statusCode >= 400) { return $this->error('Unable to load product', 400); } $body = $result->getBody()->getContents(); $data = json_decode($body, true); if ($data['status'] === 'invalid') { return $this->error('Product unavailable', 404); } return $this->success(['product' => $data['detail']]);   $this->productApiClient->request($productId); API Clientで 商品を取ってくる
  14. ソフトウェアで見るフィードバック $productId = $request->get('product_id'); $result = $this->productApiClient->request($productId); $statusCode = $result->getStatusCode();

    if ($statusCode >= 500) { return $this->error('Please try again later'); } elseif ($statusCode >= 400) { return $this->error('Unable to load product', 400); } $body = $result->getBody()->getContents(); $data = json_decode($body, true); if ($data['status'] === 'invalid') { return $this->error('Product unavailable', 404); } return $this->success(['product' => $data['detail']]);   $statusCode = $result->getStatusCode(); if ($statusCode >= 500) { return $this->error('Please try again later'); } elseif ($statusCode >= 400) { return $this->error('Unable to load product', 400); } HTTPステータスから エラーを判定&処理を中断する
  15. ソフトウェアで見るフィードバック $productId = $request->get('product_id'); $result = $this->productApiClient->request($productId); $statusCode = $result->getStatusCode();

    if ($statusCode >= 500) { return $this->error('Please try again later'); } elseif ($statusCode >= 400) { return $this->error('Unable to load product', 400); } $body = $result->getBody()->getContents(); $data = json_decode($body, true); if ($data['status'] === 'invalid') { return $this->error('Product unavailable', 404); } return $this->success(['product' => $data['detail']]);   $body = $result->getBody()->getContents(); $data = json_decode($body, true); if ($data['status'] === 'invalid') { return $this->error('Product unavailable', 404); } 応答本文から エラーを判定&処理を中断する
  16. ソフトウェアで見るフィードバック $productId = $request->get('product_id'); $result = $this->productApiClient->request($productId); $statusCode = $result->getStatusCode();

    if ($statusCode >= 500) { return $this->error('Please try again later'); } elseif ($statusCode >= 400) { return $this->error('Unable to load product', 400); } $body = $result->getBody()->getContents(); $data = json_decode($body, true); if ($data['status'] === 'invalid') { return $this->error('Product unavailable', 404); } return $this->success(['product' => $data['detail']]);   return $this->success(['product' => $data['detail']]); 商品情報を返す
  17. 本来の目的と非本来的なコスト $productId = $request->get('product_id'); $result = $this->productApiClient->request($productId); $statusCode = $result->getStatusCode();

    if ($statusCode >= 500) { return $this->error('Please try again later'); } elseif ($statusCode >= 400) { return $this->error('Unable to load product', 400); } $body = $result->getBody()->getContents(); $data = json_decode($body, true); if ($data['status'] === 'invalid') { return $this->error('Product unavailable', 404); } return $this->success(['product' => $data['detail']]);   return $this->success(['product' => $data['detail']]); $productId = $request->get('product_id'); $result = $this->productApiClient->request($productId); return $this->error('Please try again later'); return $this->error('Unable to load product', 400); return $this->error('Product unavailable', 404); このあたりが「本題」で
  18. 本来の目的と非本来的なコスト $productId = $request->get('product_id'); $result = $this->productApiClient->request($productId); $statusCode = $result->getStatusCode();

    if ($statusCode >= 500) { return $this->error('Please try again later'); } elseif ($statusCode >= 400) { return $this->error('Unable to load product', 400); } $body = $result->getBody()->getContents(); $data = json_decode($body, true); if ($data['status'] === 'invalid') { return $this->error('Product unavailable', 404); } return $this->success(['product' => $data['detail']]);   } $statusCode = $result->getStatusCode(); if ($statusCode >= 500) { このあたりが 「本題以外」と言える } elseif ($statusCode >= 400) { $body = $result->getBody()->getContents(); $data = json_decode($body, true); if ($data['status'] === 'invalid') { }
  19. 本来の目的と非本来的なコスト $productId = $request->get('product_id'); $result = $this->productApiClient->request($productId); $statusCode = $result->getStatusCode();

    if ($statusCode >= 500) { return $this->error('Please try again later'); } elseif ($statusCode >= 400) { return $this->error('Unable to load product', 400); } $body = $result->getBody()->getContents(); $data = json_decode($body, true); if ($data['status'] === 'invalid') { return $this->error('Product unavailable', 404); } return $this->success(['product' => $data['detail']]);   「本題」は 全体の半分に満たない!
  20. ProductApiGateway: fetchメソッド public function fetch(int $productId): ProductApiResult { $result =

    $this->productApiClient->request($productId); $statusCode = $result->getStatusCode(); if ($statusCode >= 500) { return ProductApiResult::error('Please try again later'); } elseif ($statusCode >= 400) { return ProductApiResult::error('Unable to load product'); } $body = $result->getBody()->getContents(); $data = json_decode($body, true); if ($data['status'] === 'invalid') { return ProductApiResult::error('Product unavailable'); } return ProductApiResult::success($data['detail']); }  
  21. ProductApiGateway: fetchメソッド public function fetch(int $productId): ProductApiResult { $result =

    $this->productApiClient->request($productId); $statusCode = $result->getStatusCode(); if ($statusCode >= 500) { return ProductApiResult::error('Please try again later'); } elseif ($statusCode >= 400) { return ProductApiResult::error('Unable to load product'); } $body = $result->getBody()->getContents(); $data = json_decode($body, true); if ($data['status'] === 'invalid') { return ProductApiResult::error('Product unavailable'); } return ProductApiResult::success($data['detail']); }   $statusCode = $result->getStatusCode(); if ($statusCode >= 500) { return ProductApiResult::error('Please try again later'); } elseif ($statusCode >= 400) { return ProductApiResult::error('Unable to load product'); } $body = $result->getBody()->getContents(); $data = json_decode($body, true); if ($data['status'] === 'invalid') { return ProductApiResult::error('Product unavailable'); } 中身は コントローラーで やっていた処理と 大体同じ
  22. ProductApiGateway: fetchメソッド public function fetch(int $productId): ProductApiResult { $result =

    $this->productApiClient->request($productId); $statusCode = $result->getStatusCode(); if ($statusCode >= 500) { return ProductApiResult::error('Please try again later'); } elseif ($statusCode >= 400) { return ProductApiResult::error('Unable to load product'); } $body = $result->getBody()->getContents(); $data = json_decode($body, true); if ($data['status'] === 'invalid') { return ProductApiResult::error('Product unavailable'); } return ProductApiResult::success($data['detail']); }   ProductApiResult ProductApiResult::error('Please try again later'); ProductApiResult::error('Unable to load product'); ProductApiResult::error('Product unavailable'); ProductApiResult::success($data['detail']); 利用目的に合わせた 「ハイレベル」な表現で 結果を返す
  23. Controller before/After $productId = $request ->get('product_id'); $result = $this ->gateway

    ->fetch($productId); if (!$result->isOk) { $this->error($result->reason); } else { $this->success([ 'product' => $data['detail'] ]); }   $productId = $request->get('product_id'); $result = $this ->productApiClient ->request($productId); $statusCode = $result->getStatusCode(); if ($statusCode >= 500) { return $this->error('Please try again later'); } elseif ($statusCode >= 400) { return $this->error( 'Unable to load product', 400 ); } $body = $result->getBody()->getContents(); $data = json_decode($body, true); if ($data['status'] === 'invalid') { return $this->error( 'Product unavailable', 404 ); } return $this->success(['product' => $data['detail']]);
  24. 擬似コードにするとこんな感じ? class PdM { private Cs $cs; public function createKikakuIdea():

    Kikaku { $voiceCollection = $this->cs->getUserVoiceCollection(); // ͛͢ʔෳࡶͳͷͰ͜͜ʹ1,000ߦ͘Β͍ͷώϡʔϦεςΟοΫͳϩδοΫ͕ੜ͑Δ // $voiceCollectionΰχϣΰχϣɾɾɾͰɺ$kikakuΛಘΔ return $kikaku; } } }   ので
  25. 組織構造のアーキタイプ これらの要素を組み合わせて、組織の「構造」が作られる • 公式な組織 - 部門、部署 • 組織図上に現れる単位 • 準公式な組織

    - 時限的なプロジェクト、委員会 • 部署・部門と境界線を同一としない集約 • 会議体 / 会議 - 情報共有、意思決定のための集まり • アジェンダありきの単発・連続の集合 • 非公式なつながり - 偶発的な交流、(構造に用いる場合には)意図的な交流の仕組み • 業務上の結合に閉ざされない関係 • 組織パターン「冷水機」 スクラムパターン「おやつ神社」、Slackのtimesなども  111
  26. 「システム」 一般に対するアプローチ • システムには • 「要素」がある • 要素同士の「繋がり」がある • 繋がった者同士の「相互作用」がある

    • 分析した要素の1つ1つだけを見ても、システムの理解は得難い • 代わりに、「繋がり」「相互作用」を見る必要がある  143
  27. G・M・ワインバーグの「一般システム思考入門」 • 「観察」を行う主体もシステムの一部であるという立場 • ("ネオ・サイバネティクス"と通ずるものがありそう • システムは、(依存関係にある)対象を「観察」して、自身の変化・適応を自律 的に行うもの • また、フラクタルな構造を持ち、ミクロなシステムは全て上位の「環境」の一

    部である、という論も展開している • 「コード→パッケージ→ソフトウェア→インフラを含めたITシステム→ビジネス要求→ マーケットや現実世界→自然法則・宇宙」という感じに • 下位だけを見ると「変更」であっても、上位に対する「適応」にしか過ぎない  147
  28. ノーマンの「行為の7段階理論」 • ゴールの形成 → 意図の形成 → 行為の詳細化 → 行為の実行 →

    外 界の状況の知覚 → 外界の状況の解釈 → 結果の評価 • これも「フィードバックがあったときに、それを拾って、判断し、次 の行動に」という世界観と一致する  148