WordPressショートコードのリファクタリングから学ぶ設計原則

WordPressショートコードのリファクタリングから学ぶ設計原則

目次

前編:問題は「技術」ではなく「構造」だった

WordPressのショートコードを開発していると、最初は小さな機能だったものが、気づけば巨大な関数へ成長していることがあります。

今回扱うショートコードも、まさにその状態でした。
もしかすると、あなたが今管理しているコードにも、同じ問題が静かに潜んでいるかもしれません。

当初の目的はシンプルでした。

しかし実際に試してみると、予想外の問題が発生しました。
Elementorの編集画面でDOM構造がおかしくなり、正常に表示されなくなったのです。

最初は当然、「ob_start() が原因なのではないか」と考えました。
しかし調査と検証を進める中で、別の事実が見えてきます。

問題は ob_start() ではありませんでした。
本当の原因は、もっと深い場所に存在していたのです。

関数が長いからいけないのか

大きな関数を見ると、多くの人はこう考えます。

「この関数は長すぎる。だから分割しよう。」

これは間違いではありません。
実際に、長い関数は可読性を下げます。

しかし今回のケースでは、「長いこと」そのものが問題ではありませんでした。

例えば、次のようなコードを考えてみます。

				
					function calculate_total_price() {
    ...
}
				
			

たとえ100行を超えていたとしても、その関数が価格計算だけを担当しているなら、そこまで大きな問題にはなりません。

逆に、次のような構造ならどうでしょうか。

				
					function process_order() {
    注文処理
    メール送信
    DB更新
    HTML出力
    ログ出力
    JavaScript出力
}
				
			

これは50行しかなくても問題になります。
重要なのはコード量ではなく、その関数が何を担当しているかです。

違和感の正体は、責務の混在だった

今回のショートコードを改めて整理してみると、実は非常に多くの責務が混在していました。
具体的には、次のような処理がすべて1つの関数の中に存在していたのです。

  • 設定値定義
  • カテゴリー取得
  • 下書き取得
  • POST処理
  • バリデーション
  • 画像アップロード
  • タグ付与
  • リダイレクト
  • エラーメッセージ表示
  • フォーム描画
  • wp_editor描画
  • モーダル描画
  • JavaScript出力
  • 成功メッセージ表示

つまり構造としては、

が混在している状態です。

ここで初めて、違和感の正体が見えてきました。
問題は「関数が長いこと」ではなく、「関数の責務が混在していること」だったのです。

なぜElementorと相性が良くないのか

この発見によって、Elementorで発生していた問題も説明できるようになります。

Elementorはショートコードを実行し、その結果を取得してプレビューを生成する仕組みを持っています。

つまりショートコードには本来、

				
					return "表示内容";
				
			

という、決まったタイミングで決まった内容だけを返す振る舞いが期待されています。

ElementorはDOMをリアルタイムに構築し直しながらプレビューを描画するため、出力のタイミングがずれると、その瞬間にDOM構造の整合性が崩れてしまうのです。

しかし今回のショートコードは、データ取得・投稿処理・リダイレクト・HTML出力・JavaScript出力・wp_editor生成が混在していました。

その状態で、

				
					ob_start();
...
return ob_get_clean();
				
			

を導入すると、出力タイミングや処理の責任範囲が複雑になります。
結果として、DOM構造の崩れや予期しない挙動が発生したのです。

つまり、ob_start() は問題の原因ではありません。
むしろ、既存の構造問題を表面化させた存在だったと言えます。

気づき|認識の転換点

ここで重要な認識の変化が起きました。

当初の理解は「ob_start() が原因ではないか」でした。
次の段階では「関数が大きすぎる」になりました。
しかし最終的には、「責務が混在している」へ到達します。

この違いは非常に大きいものです。
なぜなら、「関数が大きい」という評価は現象であり、「責務が混在している」という評価は構造だからです。

現象を見ていても本質には辿り着けません。
構造を見ることで初めて、本当の原因を発見できます。

この三段階の認識の転換——現象から構造へと視点が移っていく過程——は、今回のリファクタリング全体を貫く軸になります。

後ほど振り返ることになりますが、まずはこの段階を覚えておいてください。

前編まとめ

今回の問題は、当初考えていたような ob_start() の問題ではありませんでした。
また、単純に関数が長いことでもありませんでした。

本質は、1つの関数の中に複数の責任が混在していたことにあります。
そしてこの発見が、後のリファクタリング方針を大きく変えることになります。

次回は、なぜ一括リファクタではなく3フェーズで進めたのか、責務分離によって何が変わったのかを、実際のリファクタリング手順とともに解説します。

中編:責務分離はなぜ保守性と安定性を向上させるのか

機能ではなく責任で分ける

前回、問題の本質は「関数が長いことではなく、責務が混在していること」であると整理しました。
では、その問題をどう解決するべきなのでしょうか。

多くの場合、「長い関数だから分割する」という発想になります。
しかし実は、ここにも落とし穴があります。

なぜなら、分割の基準を間違えると、関数が増えただけで本質的な改善にならないからです。

例えば、

				
					save_part1()
save_part2()
save_part3()
				
			

のように、単純に行数で分割しても意味はありません。
責務は依然として混在したままです。

重要なのは、何を基準に分割するかです。

今回のリファクタリングでは、「機能単位」ではなく「責任単位」で分割することを選択しました。
結果として、次のような構造になります。

				
					custom_frontend_form()
├─ データ取得    → get_frontend_form_data()
├─ 投稿処理      → handle_frontend_form_submission()
└─ 画面表示      → render_frontend_form()
				
			

この時点で、それぞれの関数が「何を担当しているか」を明確に説明できるようになります。
これが責務分離です。

なぜ一括リファクタをしなかったのか

今回のChatGPTとの対話では、最初から完成形を作ろうとはしませんでした。

むしろ逆です。
あえて段階的に進めました。

Phase1

まずは投稿処理だけを切り離し、専用関数へ移動しました。

そして、実際に動作確認を行いました。

Phase2

次に、カテゴリー取得・下書き取得・承認待ち取得といったデータ取得部分を切り離し、専用関数へ移動しました。

そして再び動作確認を行いました。

Phase3

最後に、HTML・JavaScript・wp_editorといった表示部分を切り離し、render_frontend_form() へ移動しました。

そして最終的に、

				
					ob_start();
...
return ob_get_clean();
				
			

へ移行しました。
各フェーズの「動作確認」は、感覚的なチェックではありません。

ステージングサイトで実際にコードを動作させ、処理が問題なく完了するか、エラーで停止しないかを確認しています。

つまり、本番環境に近い状態で一つひとつのフェーズをテストしながら進めた、ということになります。

なぜ段階的に進めた方が安全なのか

ここには重要な設計原則があります。

一般的には、「改善したいなら、全部まとめて直そう」と考えがちです。
しかし、それは最も危険な方法でもあります。

例えば今回、最初から投稿処理・データ取得・HTML・JavaScript・wp_editorを同時に変更していたらどうなるでしょうか。

もし不具合が出た場合、原因を特定するのが極めて困難になります。

しかし、Phase1で確認、Phase2で確認、Phase3で確認、という形で進めると、問題が発生した場所を即座に特定できます。

つまり、リファクタリングそのものを小さな変更の集合へ分解しているのです。

これはコードだけの話ではありません。
システム設計全般に共通する考え方です。

責務分離によって何が変わったのか

今回の改善で得られた効果は複数あります。

保守性向上

以前は、画像アップロードを修正したいだけでも、巨大な関数全体を読む必要がありました。

現在は、handle_frontend_form_submission() を見るだけで済みます。
変更箇所が明確になりました。

デバッグ容易性向上

以前は、問題が起きた時に「処理なのか、表示なのか、JavaScriptなのか」を切り分ける必要がありました。

現在は、責務ごとに分離されているため、問題の発生箇所を追いやすくなっています。

安定性向上

以前は、表示処理と投稿処理が混在していたため、出力タイミングが複雑でした。

現在は、render_frontend_form() が表示だけを担当しています。
だから ob_start() も素直に機能するようになりました。

責務分離によって問題の見え方が変わる

今回最も大きな変化は、コードそのものではありません。
問題を見る視点です。

当初は「Elementorがおかしい、ob_start() が原因ではないか」と考えていました。
しかし、責務分離によって構造を整理した結果、見えてきたのは全く別の事実でした。

それは「Elementorの問題」ではなく、「構造問題の表面化」だったのです。
つまり、問題を解決したというより、問題の見方が変わったと言った方が正確かもしれません。

構造を整理すると、現象の裏側にある原因が見えるようになります。
そして、原因が見えれば解決方法も見えてきます。

中編まとめ

今回のリファクタリングで重要だったのは、関数を分割したことではありません。
重要だったのは、責務を分離したことです。

そして、一括変更ではなく3フェーズで段階的に進めたことで、安全性と検証性を確保できました。

  • 責務分離 → 保守性向上
  • 責務分離 → デバッグ容易化
  • 責務分離 → 安定性向上

という成果が得られたのです。
しかし、この話はまだ終わりではありません。

実は今回の学びは、WordPressやPHPだけに通用するものではありません。
AIがコードを生成する時代になった今、責務分離には新たな価値が生まれています。

後編では、「AIが理解しやすいコードとは何か」という視点から、責務分離とAI時代の設計原則について掘り下げていきます。

後編:責務分離はAI時代の設計原則になる

本質はWordPressではない

ここまでの話を振り返ると、今回のリファクタリングは一見すると「WordPress」「ショートコード」「リファクタリング」という技術的な話に見えます。

しかし、本当にそうでしょうか。
もし今回の話がWordPress固有の問題なら、WordPressを使わない人にとって価値はありません。

しかし実際には、今回発見した構造はもっと広く応用できます。
なぜなら、問題の本質が「WordPress」ではなく「責務分離」だからです。

システムは機能で分けるな、責任で分けろ

今回の対話の中で見えてきたのは、非常に普遍的な設計原則でした。

当初のコードは、投稿処理・データ取得・表示・JavaScript・画像アップロード・バリデーションといった多くの機能を持っていました。

しかし、問題は機能が多いことではありません。
問題は、それらが同じ場所に存在していたことです。

ここで重要なのは、機能と責任は別物だということです。

例えば「投稿フォーム」という機能があったとします。
その中には入力・保存・表示・画像処理など様々な要素がありますが、それぞれの責任は異なります。

今回のリファクタリング後は、

  • 投稿処理は handle_frontend_form_submission()
  • データ取得は get_frontend_form_data()
  • 表示は render_frontend_form()

という形に整理されました。

結果として、それぞれが「何を担当する関数なのか」を明確に説明できるようになりました。
これが責務分離の本質です。

AIは責務分離されたコードを理解しやすい

ここで現代ならではの話が登場します。
AIです。

私は普段、ChatGPTやClaudeを活用してコードを書いています。
今回の対話もその一例でした。

そして実際に体感していることがあります。
それは、AIは責務分離されたコードを理解しやすい、ということです。

例えば、300行を超える巨大な関数があったとします。
AIへ修正依頼をすると、AIはまず全体を理解しようとします。

しかし、そこにはデータ取得・投稿処理・表示・JavaScript・画像処理が混在しています。
するとAIは、問題とは関係のない部分まで読まなければなりません。

つまり、認識対象が広すぎるのです。
結果として、修正箇所の誤認、副作用の発生、意図しない変更が起きやすくなります。

責務分離はAIの認識精度を上げる

一方、今回のように責務分離されているとどうなるでしょうか。

画像アップロードだけを修正したい場合、AIへ提示するコードは handle_frontend_form_submission() だけで済みます。

AIは「投稿処理担当、画像処理もここ、ここだけ見ればいい」と判断できます。
つまり、認識範囲が限定されるのです。

これは今回の対話でも、実際に起きたことでした。
責務が分離された関数を提示すると、AIからの応答が明らかに変わったのです。

修正箇所の確認や提示がより明確になり、構造化された具体的な提案が返ってくるようになりました。

言い換えると、AIの曖昧さ——コードを予想したり、想像で補ったり、存在しない処理を前提にしてしまうハルシネーション——が減ったことが確認できたということです。

範囲が広く責務が混在したコードでは、AIは「たぶんこの辺りだろう」という推測を交えて応答せざるを得ません。

範囲が狭く責務が明確なコードでは、推測の余地そのものが減ります。
結果として、認識精度が上がり、生成精度が上がり、修正の精度も上がっていきます。

ただし、これはAIに丸投げできるという話ではありません。
AIの提案がどれだけ構造化されていても、最終的に動作を保証するのは人間の確認です。

今回のリファクタリングでも、各フェーズでの動作確認はAIの提案を信頼の根拠にするのではなく、ステージングサイトでの実動作によって裏付けています。

責務分離はAIの精度を引き上げますが、人間の検証を不要にするものではない、という前提は変わりません。

AIが読みやすいコードは人間も読みやすい

ここで一つ、当たり前のようでいて見落とされがちな事実があります。
責務分離されたコードは、AIが理解しやすいだけではなく、人間も理解しやすくなるということです。

半年後の自分がこのコードを読む場面を考えてみます。

巨大な関数を見ると、「これは何をしているんだろう、どこを修正すればいいんだろう」という理解コストが先に発生します。

しかし、責務分離されていれば、関数名を見るだけで役割が分かります。
AIが理解しやすい構造は、人間にとっても優しい構造である。

この一致は偶然ではなく、「読み手が誰であっても、認識範囲を絞れる構造が理解されやすい」という、もっと根本的な原則の表れだと言えます。

AI時代のコード管理

これからの時代、AIは開発の補助ではなく、共同作業者になっていきます。
その時に重要になるのは、AIへ適切な指示を出せる構造です。

責務が混在したコードは、人間にとっても説明しにくく、AIにとっても理解しにくい。

一方、責務分離されたコードは、「この関数だけ修正してください」「この部分だけ改善してください」という指示が可能になります。

つまり、責務分離は保守性向上のためだけではありません。
AIとの協業効率を高めるための設計でもあるのです。

今回のリファクタリングが教えてくれたこと

今回の改善は、

  1. Phase1で投稿処理を分離して動作確認
  2. Phase2でデータ取得を分離して動作確認
  3. Phase3で表示を分離して ob_start() を導入

という流れで進みました。

一気に変更するのではなく、小さな変更を積み重ねました。
その結果、問題の原因を特定しながら安全に改善できました。

そして最終的に見えてきたのは、単なるWordPressのノウハウではありませんでした。
見えてきたのは、システム設計そのものの原理です。

振り返ると、今回の対話は

  1. ob_start() が原因か」という現象から始まり
  2. 「関数が大きすぎる」という現象を経て
  3. 「責務が混在している」という構造

へ到達するプロセスでした。

この三段階の認識転換が、3フェーズという実装の段階性にもそのまま重なっていたことに気づきます。
問題を捉える視点の深まりと、解決を進める手順の深まりが、同じ形をしていたのです。

【まとめ】

今回の対話を通して分かったことがあります。

問題は ob_start() ではありませんでした。
問題は、関数の長さでもありませんでした。

本質は、責務の混在です。

そして責務分離によって、保守性向上、デバッグ容易化、安定性向上、AI理解性向上、AIとの協業最適化が実現しました。

AI時代のコード設計において、責務分離はこれまで以上に重要になります。
なぜなら、それは人間だけでなく、AIにとっても理解しやすい構造だからです。

最後に今回の記事の結論を一文で表すなら、
システムは機能で分けるのではなく、責任で分けることで安定する。

これが、WordPressショートコードのリファクタリングから得られた、最も大きな学びでした。

リファクタリングから学ぶ設計原則のFAQ

本当の問題は関数の長さではなく、1つの関数に複数の責務が混在していたことです。

いいえ、ob_start()自体ではなく、責務が混在した構造が問題を表面化させていました。

いいえ、長さではなく、担当する責任が混在しているかどうかが重要です。

データ取得・投稿処理・画面表示などを、それぞれ役割ごとの関数へ分ける設計方法です。

小さな変更ごとに動作確認を行い、不具合の原因を特定しやすくするためです。

保守性・デバッグのしやすさ・安定性が向上し、コード管理が容易になります。

責務が明確なコードはAIも理解しやすく、より正確な修正や提案につながるためです。

投稿をシェアする

この記事を書いた人

コメントフォーム (メンバー限定)

0 コメント
新しい順
古い順 いいね順
髙橋克慶

髙橋克慶

Nexus AI 代表

Web制作・デザイン・マーケティング・コンサルティング等の経験を積み、ChatGPTコミュニティ Nexus AIを立ち上げる。AI技術を活用して、コミュニティ運営に役立てている。

目次

ノウハウ資産