Codex app-serverで承認つきCLI実行を制御する最小実験【Pythonサンプルコード付き】

Codex app-serverで承認つきCLI実行を制御する最小実験【Pythonサンプルコード付き】 生成AI
Codex app-serverで承認つきCLI実行を制御する最小実験【Pythonサンプルコード付き】

その他のエッセイはこちら

AI にコード生成だけを任せる話は増えました。ですが、実務で本当に欲しくなるのは、何を触らせるかだけではありません。どの段階で触らせるか、どの操作に承認を入れるか、どこで止めるかまで含めた工程制御です。

今回のテーマは、そこを見える形で確かめることです。Codex app-server を Python から起動し、自然文の指示を送り、Codex 側が必要と判断したローカルコマンド実行に対してクライアント側で承認を返す、という最小構成を作ります。Codex app-server は、認証、会話履歴、approval、streamed agent events を扱う rich client 向けの統合インターフェースとして案内されています。protocol は双方向 JSON-RPC 2.0 で、既定 transport は stdio の JSONL です。 (オープンAI)

今回のコードは Python 製ですが、最終的に想定しているのは Python で完結する開発環境ではありません。最小実験で app-server の挙動を把握したうえで、工程制御や承認 UI は C# 側に寄せ、対象資産は C/C++ のソース、ビルド、テスト、実行ファイル、実機連携へ広げる前提です。

このレベルの検証だけを見るなら、VS Code の Codex 拡張を使う方が手早く進められます。ただ、今回の目的は便利に使うことではなく、AI に何を、どの段階で触らせるかをローカル側のクライアントから制御できる事実を自分の手で把握することです。

Codex app-server に対して Python クライアントが承認を返しながら CLI を実行する流れ
ローカル側が承認と工程制御を持ち、Codex 側が会話実行と command 提案を進める最小構成

3行要約

  • Codex app-server を Python から起動し、承認つきでローカル CLI を扱う最小サンプルを作ります。
  • 目的は便利なツール作りではなく、AI に何を、どの段階で触らせるかを制御できる事実を把握することです。
  • 最終的な適用先は、C# による工程制御層と、C/C++ のビルド・テスト・実機連携を含む構成です。

合わせて読むことおすすめの記事

使用シーンの動画

使用シーンを画面キャプチャした動画です。(4倍速再生。音声はでません)

Codex 承認付きコード生成、テスト、実行 4倍速

記事の前提と狙い

この記事で扱う範囲は次の通りです。

  • Codex app-server の最小構成
  • OpenAI API 直叩きとの構造差
  • 承認つき CLI 実行の動作確認
  • SDK や MCP との位置づけ整理
  • 将来の工程制御付きオーケストレータへの入口

完成版の自動開発環境をここで一気に作るわけではありません。ただ、最小構成でも prompt> から 1 turn を開始し、approval request を受け、コマンド実行を経て、最終応答のあとに再び prompt> へ戻る流れまでは確認できます。実際の手元ログでも、その一連の動きが確認できています。

OpenAI API直叩きとCodex app-serverの構造差

OpenAI API を直接使う場合でも、Responses API には agentic loop があります。1 回の request の中でモデルが複数ツールを呼べるので、クラウド側に何の実行制御もないわけではありません。 (オープンAI)

ただし、実装者の視点では見え方がかなり違います。OpenAI API を直接使う構成では、ツール公開範囲、工程遷移、承認タイミング、会話状態管理、build / test / deploy の進行管理をアプリ側が強く持ちやすいです。

これに対して Codex app-server は、認証、会話履歴、approval、event stream を含む統合面をまとめて持っています。thread/startturn/start を軸に会話を進め、approval request や streaming delta をイベントとして受け取れます。ローカル側は工程制御と承認判断を持ち、Codex 側は会話実行と command 提案を担う、という役割分担が見えやすくなります。 (オープンAI)

OpenAI API 直叩きと Codex app-server の構造差を示す模式図
OpenAI API 直叩きより、Codex app-server はローカル側と Codex 側の役割分担が見えやすい
PC上のローカルアプリが codex app-server と連携し、さらに codex app-server がクラウド上の Codex サービスと通信する構成図。C#アプリと codex app-server はローカル、クラウド Codex はクラウド側に配置されている。
ローカルアプリと codex app-server、クラウド上の Codex サービスの関係図

SDKと直叩きの位置づけ

Codex をプログラムから扱う方法は app-server 直叩きだけではありません。OpenAI は Codex SDK を別の automation surface として案内しており、CI/CD、内部ツール、アプリ統合の用途を明示しています。公式 docs が主に説明しているのは TypeScript SDK です。 (オープンAI)

加えて、公式リポジトリには codex_app_server パッケージによる experimental な Python SDK もあります。README には Codex()thread_start()thread.run() のような高レベル API が載っています。つまり、日常的なアプリ実装の起点としては、こちらの高レベル API を検討する余地があります。 (オープンAI)

本記事では、そこを出発点にしません。狙いは protocol と制御境界の理解なので、あえて app-server を JSON-RPC over stdio で直接扱います。

認証の仕組み

今回の Python スクリプト自身は、OpenAI に直接ログインしていません。認証は codex app-server を起動した Codex 側が担います。

Codex の認証 docs では、Codex app、CLI、IDE Extension のいずれかで ChatGPT または API key でサインインすると、ログイン情報がキャッシュされ、次回以降に CLI や extension がそれを再利用すると説明されています。CLI と extension は同じ cached login details を共有し、保存先は ~/.codex/auth.json または OS の credential store です。 (オープンAI)

このため、いったん Codex CLI や IDE 拡張でログインしていれば、Python スクリプト側で別途認証処理を書かなくても app-server を起動できます。実際、このサンプルコードでも Python 側は codex.cmd を起動しているだけで、独自の認証処理は持っていません。

セットアップ

最小限のセットアップはこれで足ります。

Node.js のインストール

Node.js の公式ダウンロードページから入れます。Node.js には npm が含まれます。
https://nodejs.org/en/download/

node -v
npm -v

Codex CLI のインストール

npm i -g @openai/codex

Codex CLI は macOS と Linux が正式対応で、Windows support is experimental とされています。Windows ネイティブでも試せますが、より良い Windows 体験として WSL workspace も案内されています。 (オープンAI)

初回ログイン

codex

初回だけログインし、その後はキャッシュされた認証情報が再利用されます。 (オープンAI)

Python スクリプトの実行

python codex_appserver_minimal.py

WebSocket transport

今回は最小構成に寄せるため stdio transport を使っていますが、app-server は experimental な WebSocket transport もサポートしています。公式 docs では、既定 transport は stdio の JSONL、WebSocket は codex app-server --listen ws://127.0.0.1:4500 のように起動できる experimental transport と説明されています。将来、工程制御用のクライアントを別プロセスや別マシンへ広げるなら、stdio だけでなく WebSocket も比較対象になります。 (オープンAI)

smart approvals と guardian reviewer subagent

承認は必ずしも人手で 1 件ずつさばく必要はありません。Codex の config reference には features.smart_approvals があり、eligible な approval request を guardian reviewer subagent に回せると説明されています。今回のサンプルは input() でそのまま承認していますが、将来的には「読取り系は自動承認」「書込み系は人手確認」「ビルドとテストは条件つき自動承認」といったポリシーへ広げる入口になります。 (オープンAI)

Agents SDK + MCP との比較

App Server は唯一の統合方法ではありません。OpenAI Cookbook では、Codex CLI を codex mcp-server として起動し、Agents SDK から MCPServerStdio 経由で使う構成も案内されています。この構成では、Codex CLI が MCP server として Codex 関連ツールを公開し、Agents SDK 側がより高いレベルの orchestration を担います。 (OpenAI Cookbook)

比較するとこうなります。

構成向く用途この記事との関係
App Server 直叩きprotocol、approval、turn/thread、event stream の理解今回の主題
Codex SDKCI/CD、内部ツール、アプリ統合を高レベル API で扱う実装の次段階
Agents SDK + MCP多エージェント orchestration、ツール接続、手続きの分担代替の設計軸

つまり、App Server 直叩きは Codex の harness に近い層を理解するのに向いています。SDK はその上の抽象化で、MCP + Agents SDK はさらに別の統合面です。今回は工程制御の境目を把握することが目的なので App Server を選びましたが、実装対象によっては SDK や MCP ベースの方が自然です。 (OpenAI Cookbook)

最小サンプルの仕様

今回のサンプルがやることはかなり単純です。

  • codex app-serverstdio で起動
  • initializeinitialized を送信
  • thread/start を 1 回だけ送信
  • prompt> ごとに turn/start
  • approval request を受けたら accept などを返す
  • turn 完了後に再び prompt> へ戻る

手元ではこの最小サンプルから、作業ディレクトリの読取りだけでなく、ビルド用 PATH を与えたうえでビルド処理を許可し、さらに生成済み実行ファイルの起動許可まで与えると、テスト実行まで進められることも確認しています。ここまでやるだけなら VS Code の Codex 拡張の方が楽ですが、本記事の主眼は「制御できる」という事実を把握することです。

短縮ログ

以下は、手元ログを個人情報とローカルパスを伏せた形で整えたものです。prompt 送信、approval request、command execution、最終応答、再度 prompt> に戻るまでの流れが分かる部分だけを残しています。

$ python codex_appserver_minimal.py
using codex: C:\Users\<USER>\AppData\Roaming\npm\codex.cmd

ready. empty prompt で終了

prompt> 今の作業ディレクトリを3行で説明して
作業ディレクトリの中身を確認して、何のプロジェクトかを3行で要約します。

[command started]
command: powershell -Command Get-Location

[command started]
command: powershell -Command Get-ChildItem -Force

[command started]
command: powershell -Command rg --files

[approval request]
method : item/commandExecution/requestApproval
command: powershell -Command rg --files
cwd    : <WORKSPACE>
decision [y=accept / s=session / n=decline / c=cancel] > y

[command completed]
exitCode: 0
output:
main.c
ring_buffer.c
ring_buffer.h
ring_buffer_test.exe
codex_appserver_minimal.py

このディレクトリは、小規模なCコードの作業フォルダです。
main.c と ring_buffer.c / ring_buffer.h が中心で、リングバッファ実装を含みます。
ring_buffer_test.exe があり、ビルド済み成果物も同じ場所に置かれています。

[turn completed]

prompt>

この短縮ログに対応する実ログでは、prompt> から始まり、複数の command starteditem/commandExecution/requestApprovalcommand completed、最終応答、そして再度 prompt> に戻る流れが確認できます。

サンプルコード

このサンプルコードでは、stdio で app-server を起動し、approval request に {"decision": ...} の形で応答しながら、1 本の thread に対して turn/start を繰り返します。最小構成ですが、会話実行、承認、コマンド実行、turn 完了待ちまで一通り確認できます。

import os
import sys
import json
import time
import shutil
import threading
import subprocess


class AppServerClient:
    def __init__(self):
        # codex コマンドの場所を探す
        # Windows では codex.cmd で入ることが多い
        codex = shutil.which("codex.cmd") or shutil.which("codex")
        if not codex:
            appdata = os.environ.get("APPDATA", "")
            candidate = os.path.join(appdata, "npm", "codex.cmd")
            if os.path.exists(candidate):
                codex = candidate

        if not codex:
            raise RuntimeError(
                "codex が見つかりません。"
                "先に Node.js を入れて npm を使えるようにし、"
                "`npm i -g @openai/codex` を実行してください。"
            )

        print(f"using codex: {codex}")

        # codex app-server を stdio で起動
        # Windows では UTF-8 明示がないと日本語が壊れやすい
        self.proc = subprocess.Popen(
            [codex, "app-server"],
            stdin=subprocess.PIPE,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            text=True,
            encoding="utf-8",
            errors="strict",
            bufsize=1,
        )

        # response 待ち用
        self._next_id = 1
        self._responses = {}
        self._responses_lock = threading.Lock()

        # turn 完了待ち用
        self._turn_events = {}
        self._turn_events_lock = threading.Lock()
        self._current_turn_id = None

        # 画面出力が混ざらないようにする
        self._io_lock = threading.Lock()

        # stdout / stderr を別スレッドで読む
        self._stdout_thread = threading.Thread(target=self._read_loop, daemon=True)
        self._stdout_thread.start()

        self._stderr_thread = threading.Thread(target=self._stderr_loop, daemon=True)
        self._stderr_thread.start()

    def _safe_print(self, *args, end="\n"):
        with self._io_lock:
            print(*args, end=end, flush=True)

    def _safe_write(self, text):
        with self._io_lock:
            sys.stdout.write(text)
            sys.stdout.flush()

    # app-server の stderr を読む
    def _stderr_loop(self):
        for line in self.proc.stderr:
            line = line.rstrip("\n")
            if line:
                self._safe_print(f"[server stderr] {line}")

    # app-server から来る JSONL を処理
    def _read_loop(self):
        for raw_line in self.proc.stdout:
            line = raw_line.strip()
            if not line:
                continue

            try:
                msg = json.loads(line)
            except Exception:
                self._safe_print("[raw stdout]", raw_line.rstrip("\n"))
                continue

            # サーバー発 request
            if "id" in msg and "method" in msg:
                self._handle_server_request(msg)
                continue

            # 通常の response
            if "id" in msg:
                with self._responses_lock:
                    self._responses[msg["id"]] = msg
                continue

            # notification / event
            self._handle_notification(msg)

    # notification を処理
    def _handle_notification(self, msg):
        method = msg.get("method")
        params = msg.get("params", {})

        # turn 開始時に現在の turn を覚える
        if method == "turn/started":
            turn = params.get("turn", {})
            turn_id = turn.get("id")
            if turn_id:
                self._current_turn_id = turn_id
                with self._turn_events_lock:
                    self._turn_events.setdefault(turn_id, threading.Event())
            return

        # モデルの文字ストリームはそのまま表示
        if method == "item/agentMessage/delta":
            delta = params.get("delta", "")
            if delta:
                self._safe_write(delta)
            return

        # コマンド開始時の表示
        if method == "item/started":
            item = params.get("item", {})
            if item.get("type") == "commandExecution":
                self._safe_print("\n[command started]")
                self._safe_print(f"command: {item.get('command')}")
                return

        # コマンド完了時の表示
        if method == "item/completed":
            item = params.get("item", {})
            item_type = item.get("type")

            if item_type == "agentMessage":
                self._safe_write("\n")
                return

            if item_type == "commandExecution":
                self._safe_print("\n[command completed]")
                self._safe_print(f"exitCode: {item.get('exitCode')}")
                output = self._cleanup_output(item.get("aggregatedOutput"))
                if output:
                    self._safe_print("output:")
                    self._safe_print(output)
                return

        # turn 完了通知
        if method == "turn/completed":
            turn_id = params.get("turnId")
            if turn_id:
                with self._turn_events_lock:
                    evt = self._turn_events.setdefault(turn_id, threading.Event())
                    evt.set()
            self._safe_write("\n")
            return

        # turn/completed が来ない場合の保険
        if method == "thread/status/changed":
            status = params.get("status", {})
            if status.get("type") == "idle" and self._current_turn_id:
                with self._turn_events_lock:
                    evt = self._turn_events.setdefault(
                        self._current_turn_id,
                        threading.Event()
                    )
                    evt.set()
            return

    # approval request を処理
    def _handle_server_request(self, msg):
        req_id = msg.get("id")
        method = msg.get("method")
        params = msg.get("params", {})

        if method and method.endswith("/requestApproval"):
            self._safe_print("\n[approval request]")
            self._safe_print(f"method : {method}")

            command = params.get("command")
            cwd = params.get("cwd")
            reason = params.get("reason")

            if command:
                self._safe_print(f"command: {command}")
            if cwd:
                self._safe_print(f"cwd    : {cwd}")
            if reason:
                self._safe_print(f"reason : {reason}")

            answer = input("decision [y=accept / s=session / n=decline / c=cancel] > ").strip().lower()

            if answer == "y":
                decision = "accept"
            elif answer == "s":
                decision = "acceptForSession"
            elif answer == "c":
                decision = "cancel"
            else:
                decision = "decline"

            self._send_raw({
                "id": req_id,
                "result": {
                    "decision": decision
                }
            })
            return

        # 想定外 request は cancel
        self._send_raw({
            "id": req_id,
            "result": {
                "decision": "cancel"
            }
        })

    # PowerShell のノイズを少し整える
    def _cleanup_output(self, text):
        if not text:
            return ""

        lines = text.splitlines()
        filtered = []

        skip_keywords = [
            "[Console]::OutputEncoding=[System.Text.Encoding]::UTF8;",
            "プロパティを設定できません。この言語モードでは",
            "CategoryInfo",
            "FullyQualifiedErrorId",
            "発生場所 行:",
            "+ ~",
        ]

        for line in lines:
            if any(keyword in line for keyword in skip_keywords):
                continue
            filtered.append(line)

        while filtered and not filtered[0].strip():
            filtered.pop(0)
        while filtered and not filtered[-1].strip():
            filtered.pop()

        return "\n".join(filtered)

    # JSON-RPC を 1 行送る
    def _send_raw(self, message):
        payload = json.dumps(message, ensure_ascii=False) + "\n"
        self.proc.stdin.write(payload)
        self.proc.stdin.flush()

    # request を送って response を待つ
    def send_request(self, method, params):
        request_id = self._next_id
        self._next_id += 1

        self._send_raw({
            "id": request_id,
            "method": method,
            "params": params,
        })

        while True:
            if self.proc.poll() is not None:
                raise RuntimeError("app-server が終了しました。stderr を確認してください。")

            with self._responses_lock:
                if request_id in self._responses:
                    return self._responses.pop(request_id)

            time.sleep(0.01)

    # notification を送る
    def send_notification(self, method, params):
        self._send_raw({
            "method": method,
            "params": params,
        })

    # 指定 turn の完了を待つ
    def wait_for_turn(self, turn_id, timeout=300):
        with self._turn_events_lock:
            evt = self._turn_events.setdefault(turn_id, threading.Event())

        done = evt.wait(timeout=timeout)
        if not done:
            raise TimeoutError(f"turn が時間内に完了しませんでした: {turn_id}")

    # 後始末
    def close(self):
        try:
            if self.proc.stdin:
                self.proc.stdin.close()
        except Exception:
            pass

        try:
            if self.proc.poll() is None:
                self.proc.terminate()
        except Exception:
            pass


def main():
    client = AppServerClient()

    try:
        # app-server 初期化
        init_result = client.send_request(
            "initialize",
            {
                "clientInfo": {
                    "name": "mini-cli-approval-demo",
                    "version": "0.1.0",
                },
                "capabilities": {},
            },
        )
        print("initialize:", json.dumps(init_result, ensure_ascii=False, indent=2))

        client.send_notification("initialized", {})

        # まず thread を1本作る
        thread_result = client.send_request(
            "thread/start",
            {
                "model": "gpt-5.4",
                "cwd": os.getcwd(),
            },
        )
        thread_id = thread_result["result"]["thread"]["id"]

        print("\nready. empty prompt で終了\n")

        while True:
            prompt = input("prompt> ").strip()
            if not prompt:
                print("bye")
                break

            turn_result = client.send_request(
                "turn/start",
                {
                    "threadId": thread_id,
                    "input": [
                        {
                            "type": "text",
                            "text": prompt,
                        }
                    ],
                },
            )

            turn_id = turn_result["result"]["turn"]["id"]
            client.wait_for_turn(turn_id, timeout=300)

            print("\n[turn completed]\n")

    finally:
        client.close()


if __name__ == "__main__":
    main()

コードの全体像

読むときは、まず次の4点を押さえると全体像をつかみやすくなります。

ブロック役割
__init__()codex の場所を探し、codex app-server を起動
_read_loop()response / notification / server request を振り分け
_handle_server_request()approval request に accept / decline を返す
main()thread/start を 1 回実行し、その後は prompt> ごとに turn/start

approval まわりで大事なのは、返答の JSON 形です。item/commandExecution/requestApproval には、単なる "accept" ではなく {"decision": "accept"} の形で返します。app-server の protocol は JSON-RPC 2.0 で、turn/start の input には type: "text" のような variant を直接渡します。 (オープンAI)

turn 完了待ちにも少し工夫があります。turn/completed だけでなく、thread/status/changedidle に戻るケースも保険として見ています。これで最終応答が出たのに次の prompt> へ戻らない状態を避けやすくしています。

手元検証で確認できたこと

この最小実験だけでも、かなり多くのことが見えました。

まず、作業ディレクトリの要約だけでなく、ローカルコマンドの読取りと実行が承認つきで進むことが確認できました。読取りだけでなく、ビルド用 PATH を与えたうえでビルド処理を許可し、生成済み実行ファイルの起動許可まで与えると、テスト実行まで進められることも確認しています。実際の提供ログでも、ring_buffer_test.exe の実行を承認し、push / pop / wraparound の結果確認まで進んでいます。

次に、この程度の実験であっても制御の境目がかなり明確になります。ローカル側は工程制御と approval を握り、Codex 側は会話実行と command 提案を担う。この見え方は、既製の拡張を使うだけでは把握しにくい部分です。

VS Code拡張との位置づけ

ここまでやるだけなら、VS Code の Codex 拡張の方が楽です。app-server 自体も、Codex が rich client を動かすための統合面として提供されています。 (オープンAI)

ただ、今回の目的は便利に使うことではありません。知りたかったのは次の事実です。

  • approval はどのタイミングで発生するか
  • approval request の payload はどう見えるか
  • どの通知を turn 完了とみなすか
  • どの単位でローカル実行を制御できるか
  • 将来 build / test / deploy / 実機確認へどう広げられるか

工程制御付きオーケストレータを作る前提なら、この境目を自分のコードで理解しておく意味は大きいです。

まとめ

Codex app-server を Python から起動し、approval を返しながら CLI 実行を進める最小構成は、思った以上に多くのことを見せてくれます。認証は Codex 側のキャッシュを再利用でき、会話は threadturn で進み、ローカル側は approval request に応答して実行可否を握れます。SDK、WebSocket transport、smart approvals、MCP server という別の統合面もすでに存在するので、直叩きは本番コードの第一候補というより、制御境界を理解するための観測用プロトタイプとして位置づけるのが自然です。 (OpenAI Cookbook)

今回のコードは Python 製ですが、最終的な適用先は C# による工程制御層と、C/C++ のビルド・テスト・実機連携を含む構成です。その前段として、どこで承認を挟めるか、どこまでローカル側で制御できるかを把握する入口としては、かなり良い最小実験でした。

  • 承認付き CLI 実行の最小実験で、制御の境目を可視化
  • ローカル側は工程制御、Codex 側は会話実行と command 提案
  • 次の拡張先は build、test、deploy、実機確認

FAQ

Codex app-server は何に向くか

Codex app-server は、Codex が rich client を動かすための統合インターフェースです。認証、会話履歴、approval、streamed agent events を扱えるため、自前の UI や制御層を持った統合に向きます。 (オープンAI)

OpenAI API 直叩きとの違いは何か

OpenAI API の Responses API も agentic loop を持ちますが、Codex app-server は auth、history、approval、event stream が前提です。そのため、ローカル側の工程制御と Codex 側の会話実行を分けて捉えやすい構造です。 (オープンAI)

直叩きではなく SDK から始めるべきか

本番のアプリ実装や CI/CD 連携の起点としては、SDK から入る方が自然な場面が多いです。今回の直叩きは、approval や turn/thread の境目を理解するための選択です。 (オープンAI)

Windows でも動くか

動きますが、Codex CLI の docs では Windows support is experimental とされています。より安定した利用には WSL workspace も候補です。 (オープンAI)

最小サンプルの次に何を足すべきか

build 実行、テスト実行、成果物確認、deploy 承認、実機確認の順で広げるのが自然です。承認については features.smart_approvals を使った自動化の余地もあります。 (オープンAI)

参考文献

参考文献

OpenAI Developers, App Server – Codex
https://developers.openai.com/codex/app-server

OpenAI Developers, SDK – Codex
https://developers.openai.com/codex/sdk

OpenAI Developers, Authentication – Codex
https://developers.openai.com/codex/auth

OpenAI Developers, CLI – Codex
https://developers.openai.com/codex/cli

OpenAI Developers, Configuration Reference – Codex
https://developers.openai.com/codex/config-reference

OpenAI Developers, Migrate to the Responses API
https://developers.openai.com/api/docs/guides/migrate-to-responses

OpenAI Cookbook, Building Consistent Workflows with Codex CLI & Agents SDK
https://cookbook.openai.com/examples/codex/codex_mcp_agents_sdk/building_consistent_workflows_codex_cli_agents_sdk

OpenAI GitHub Repository, codex/sdk/python
https://github.com/openai/codex/tree/main/sdk/python

Node.js, Download Node.js
https://nodejs.org/en/download/

設計・オーケストレータの考え方

[入門]ドメイン駆動設計 ――基礎と実践・クリーンアーキテクチャ

Amazon.co.jp : [入門]ドメイン駆動設計 ――基礎と実践・クリーンアーキテクチャ

「何を触らせるか」より「どの段階で触らせるか」を設計する話と相性がいい。
DDD の基礎から実践、さらにクリーンアーキテクチャまで扱う構成になっている。
設計の言葉を揃えたいときの入口としてちょうどいい。

データモデリングでドメインを駆動する

Amazon.co.jp : データモデリングでドメインを駆動する

工程制御を単なるツール操作ではなく、業務や開発フローの構造として捉えたいならかなり合う。
分散、非同期、疎結合な基幹系システムを視野に入れた説明で、DDD やマイクロサービスとの関係も扱っている。

Pythonで最小実験を作る本

独習Python 第2版

Amazon.co.jp : 独習Python 第2版

今回のような「まず小さく動かして protocol を理解する」用途なら、一冊目としてかなり素直。
2025年5月発売の第2版が案内されている。
基礎から手を動かして確認したいとき向け。

パーフェクトPython[改訂2版]

Amazon.co.jp : パーフェクトPython[改訂2版]

Python を単なる入門で終わらせず、型ヒント、asyncio、ライブラリ活用まで含めて広く押さえたいならこちら。
コルーチンやサードパーティライブラリまで含む広い守備範囲が説明されている。
app-server の event 処理や並行処理の見通しをよくしたいなら相性がいい。

C#で工程制御層を作る本

独習C# 第5版

Amazon.co.jp : 独習C# 第5版

最終的に C# で工程制御や承認 UI を持たせる前提なら、まずこれが軸になる。
C# 10.0 対応で、言語仕様を体系的かつ網羅的に学べる本として位置づけられている。
オーケストレータ本体を C# で書くなら最初の一冊として無難。

独習ASP.NET Core

Amazon.co.jp : 独習ASP.NET Core

C# で Web UI を持つ工程制御画面まで見据えるならこちら。
2024年刊で、ASP.NET Core アプリの構造やミドルウェアまで含む構成になっている。
承認画面やログ閲覧 UI を Web で作る想定なら合う。

C/C++資産と組み込み寄りの本

Effective Modern C++

Amazon.co.jp : Effective Modern C++

対象資産が C++ 寄りに広がるなら、品質の底上げに効く一冊。
C++11/14 を前提に効果的かつ堅牢、移植性の高いコードを書くための 42 項目を扱う本として案内されている。
生成コードや周辺ツールを C++ で扱う可能性があるなら押さえておきたい。

テスト駆動開発による組み込みプログラミング

Amazon.co.jp : テスト駆動開発による組み込みプログラミング

build、test、実行ファイル起動、実機確認まで伸ばすならかなり相性がいい。
制約のある組み込み環境での TDD、C 言語への SOLID 原則の適用、レガシーコードへのテスト追加まで扱う本とされている。
組み込み寄りの現実解として強い。

ハード寄り・低レイヤ理解を足す本

RISC-Vで学ぶコンピュータアーキテクチャ 完全入門

Amazon.co.jp : RISC-Vで学ぶコンピュータアーキテクチャ 完全入門

実機連携や組み込みソフトまで視野に入れるなら、低レイヤ理解を補強する本として合う。
RISC-V、Verilog HDL、FPGA による動作確認まで含む広い範囲を扱っている。
実機寄りフェーズ向け。

作って学ぶコンピュータアーキテクチャ

Amazon.co.jp : 作って学ぶコンピュータアーキテクチャ

LLVM と RISC-V を通して低レイヤ理解を深める本。
組み込み寄りで、単にアプリ層の orchestration だけでなく、下のレイヤまで見たいときに向く。

まず買うならこの順

  1. [入門]ドメイン駆動設計
    記事の「工程制御」という主題にいちばん効く。
  2. 独習C# 第5版
    最終的な工程制御層を C# で持つ前提とつながりやすい。
  3. パーフェクトPython[改訂2版]
    今回の最小実験コードを育てる土台として使いやすい。
  4. テスト駆動開発による組み込みプログラミング
    build / test / 実行ファイル起動の先にある実務感が強い。
  5. 独習ASP.NET Core
    承認 UI や操作画面を Web 化したくなったタイミングで効く。

コメント

タイトルとURLをコピーしました