自社固有のロジックは、旧システムを止めずに書き換えられる。
2-03 から 2-11 までで、汎用の道具は揃った ── 土台、門番、文書、コード、メール、会議、公開 Web。残っているのは自社固有のロジック、つまり基幹システムの中身だ。その基幹はたいてい古い。Java や C# で書かれ、Oracle や SQL Server に乗り、誰も全部は理解していない。これを並行稼働で書き換え、自社固有のロジックを FastAPI で一箇所の API として出す。手法は基幹を止めるまでの段取り、道具は FastAPI。順に見ていく。
「壊すな」「触るな」は、コストが高かった時代の助言だ
長いあいだ、基幹システムを担当する人間に与えられてきた助言は、こうだ。
「壊すな」「触るな」「動いているものに手を入れるな」「既存資産を活かせ」。
これは、書き換えのコストが高すぎた時代の助言だった。書き換えに何年も、何億もかかる時代には、「動いているものは触らない」が確かに正解だった。
時代が変わった。
AI が業務ロジックを Python に翻訳できる。AI が SQL の意図を Markdown で書き出せる。 AI がテストデータを生成できる。AI がドキュメント無しのコードからルールを抽出できる。書き換えのコストが、桁で下がった。
下がったコストの前で、まだ「触るな」と言うのは、新しい現実を無視している。残す理由は、もう無い。
並行稼働は、リスクを実測で潰す
書き換えのコストが下がったとはいえ、リスクはゼロではない。新システムが旧システムと完全に同じ動きをする保証は、どんな手法でも与えられない。
そこで、並行稼働だ。
新システムを AI で作る。旧システムはそのまま動かす。同じ入力を両方に流す。出力を比較する。
Java/C#
Oracle/SQL Server"] New["新システム
FastAPI
PostgreSQL"] Diff{"毎日 突き合わせる"} Fix["新を直す
旧は触らない"] Kill["旧を止める"] Input --> Old --> Diff Input --> New --> Diff Diff -->|差分があるので原因を追う| Fix Fix --> New Diff -->|差分ゼロが続き新が正しいと分かった| Kill classDef good fill:#e8f5e9,stroke:#7a9a6d,color:#3a4d34 classDef bad fill:#fef3e7,stroke:#c89559,color:#5a3f1a class Old bad class New,Kill good
二つの出力が一致するなら、新システムは正しい。一致しないなら、どちらかが間違っている。たいてい、旧システムに昔から残っていたバグが先に見つかる。ドキュメントに書かれていなかったバグだ。
これを、先に決めた期間だけ続ける。差分がゼロになり、想定外のケースもカバーされたと確認できたら、旧システムを止める。
並行稼働は、書き換えのリスクを 実測 で潰す手段だ。机上の検証ではない。仕様書のレビューではない。本番環境で、実データで、動かして確かめる。
旧システムを止める日は、先に決める
並行稼働の期間は、始める前に決める。月次の処理なら「差分ゼロの月が三回続いたら止める」、日次なら「差分ゼロの日が決めた日数続いたら止める」のように、止める条件を数字で書いておく。
その条件を過ぎても止められないなら、そもそも新システムが正しくない。新システムを直す。並行稼働を「ずっと」やってはいけない。
組織の中には「旧システムを念のため残しておく」という心理が働く。ここに問題がある。残し続けると、こうなる。
- 運用コストが二重になる
- 担当者が分散する
- トラブル時にどちらの責任か揉める
- 新機能が両方に必要になり、書く量が二倍になる
- いつか旧を止める決断が、永遠に先送りされる
並行稼働は手段であって、目的ではない。新が正しいと分かったら、旧を止める。
止められないなら、最初から書き換えるべきではなかった。やる時はやる。
「既存資産を活かしつつ AI で補う」── これは、結局、旧システムが残り続けることを許容する考え方だ。新しい機能は外側に積み上がり、中身は古いまま。何年経っても、組織は AI ネイティブにならない。中途半端な共存は、組織を硬直させる。「補う」が許されるのは、書き換えコストが本当に高すぎた過去の話だ。今は違う。
ベンダー製品も、同じ手で止める
Oracle、SAP、Salesforce、Microsoft の業務製品 ── これらは「製品」を売っているのではない。「製品を使い続けないといけない状況」を売っている。
並行稼働で抜ける段取りは、こうなる。
- 製品からデータを毎日エクスポートする(製品はそのまま動かす)
- AI で書いた新システムが、エクスポートを処理して同じ業務を回す
- 同じ業務指標(売上、在庫、顧客状態)を、両方で計算する
- 数字が一致したら、製品契約を更新しない
- 製品から「全データエクスポート」を最後にもらって、新システムに完全移行
契約更新の時期に間に合わせる。これは戦略的なスケジュールだ。更新日から、並行稼働の期間と意思決定の時間を逆算して、始める日を決める。
ベンダーは「移行リスク」「データ整合性」「ベテラン担当者の引き止め」── あらゆるカードで引き止めにかかる。並行稼働で同じ出力が出ていれば、それらは全部反論できる。証拠が手元にある。ライセンス料は契約書に書いてある。それが止まる。
業務知識は、一気に Markdown に出す
並行稼働の準備として、業務知識を Markdown に出す。一気にやる。
これまでの常識では、業務知識のドキュメント化は数ヶ月から数年がかりのプロジェクトだった。担当者が空き時間に少しずつ書く。半分も書き終わらないうちに、書いた人が異動する。途中で頓挫する。結局、書かれない。
時代が変わった。
旧システムのコード、コメント、SQL、運用手順書、過去の障害報告 ── これらを全部 AI に渡す。「業務ロジックを抽出して Markdown で整理しろ」と頼む。初版は、人が空き時間に書くのとは桁の違う速さで出る。
完璧でなくていい。初版は、たたき台で十分だ。足りない分は、並行稼働中に出力差分として現れる。差分を一つずつ潰していけば、業務知識は完成する。
これが、並行稼働の隠れた効用でもある。*書かれていなかった業務ルールが、並行稼働で全部炙り出される*。机上で書かれた仕様書には現れない、実運用で初めて分かるルール ── これが、AI による初版 Markdown と、並行稼働の出力差分の両方から、引き出される。文書を自分の側に取り戻す話は 2-07 で扱った。基幹の業務知識もまた、同じく読める素材に落ちる。
業務ルールは現場にある ── 現場がテストを書く
書き換えを誰がやるか。
これまでの常識は、こうだった。IT 部門、SI ベンダー、コンサルタントが、現場から仕様を聞き取って、コードを書く。書き終わったら現場が受け入れテストをする。これは、書き換えに必要な知識が分散していた時代のかたちだ。コードを書く能力は IT 部門に、業務ルールの知識は現場に、それぞれ偏在していた。
今は違う。
コードを書く能力は、AI が持っている。残るのは業務ルールの知識だけだ。そして、業務ルールを最も深く知っているのは、毎日その業務を回している現場の人間である。現場の人間が、AI にコードを書かせる。これで完結する。途中の伝言ゲームが要らない。
並行稼働で重要なのは、出力差分を見つけることだ。新システムが旧と同じ出力を出すか ── これを判定するためのテストデータが要る。このテストデータを作るのに、最も向いているのが現場の人間だ。
「7 月の請求は 10 日締めだが、お盆休みを考慮して翌月 5 日まで延長する」── このルールを知っている現場の人間が、AI にこう頼む。「7 月のお盆延長を考慮した請求テストデータを作って」。AI が作る。実際に旧システムを通して、期待出力を生成する。これがテストデータになる。
机上の仕様書には書かれていなかったルールが、テストの形で実体化する。業務知識が、現場からテストへ、そしてコードへと流れる。このテストは IT 部門では書けない。ルールは現場にある。ルールを知らない者がテストを書くから、書き換えは失敗してきた。
委託は止める
ここまで来ると、結論は明白だ。
基幹システムの書き換えを、IT ベンダーやコンサルタントに委託する必要は無い。
委託の伝統的な根拠は二つだった。(1) コードを書く能力が外側にしかない。(2) 業務ルールの知識を外側に伝える必要がある。(1) は、AI が解決した。(2) は、そもそも外に伝える必要が無くなった。現場と AI で完結する。
ベンダーへの委託費は、基幹システム書き換えで最大のコスト項目だ。これが要らなくなる。業務ルールを知っている現場の人間が、AI にコードを書かせ、AI にテストを書かせ、並行稼働で実測する。書き換えは、外注するものではなく、内製で行うものに変わる。業務を知る者が、AI を使って、自分の業務を書き換える ── これが新しい現場の作法だ。
これは IT 部門の役割の縮小ではない。IT 部門は、現場と AI のチームを支える基盤 (インフラ、データベース、デプロイ環境、セキュリティ)を整える役割に集中できる。重複した「業務ロジックの仲介」役を抜ける。
DB も並行稼働で置き換える
論理層を FastAPI に書き換えるのと同じ要領で、DB も並行稼働で置き換える。
データベースは残す。しかし、ベンダー方言は捨てる。SELECT、JOIN、GROUP BY、ウィンドウ関数 ── 標準 SQL は 1970 年代から動いていて、この先も動く。AI も完全に書ける。残すのは標準 SQL、捨てるのはベンダー方言 ── この線引きが要だ。
Oracle の PL/SQL、Microsoft SQL Server の T-SQL ── これらは捨てる。ベンダー固有の方言だ。データベースに業務ロジックを埋め込むことで、ベンダーロックインの最後の砦を作ってきた。PL/SQL のストアドプロシージャに埋め込まれた業務ロジックは、Python に書き直す。AI に PL/SQL を渡せば、業務ルールを抽出した上で Python に翻訳して出す。業務ロジックが、不可視のストアドからコードに戻る。可読性が上がり、バージョン管理でき、テストできる。
DB そのものは PostgreSQL に移す。これも並行稼働だ ── 旧 DB から PostgreSQL へ毎日同期し、新システム(FastAPI)は PostgreSQL を、旧システムは Oracle / SQL Server を読み書きする。出力突き合わせで両系の整合性を確認し、安定したら旧 DB を停止する。 Oracle / SQL Server を捨てることが、ベンダーロックインからの卒業証書になる。
DDL の方言変換、Azure SQL や pgloader を使った具体的な移行手順は、2-03 で詳しく扱った。本章では「標準 SQL は残し、方言とロジックは抜く」という判断だけ押さえておけばいい。論理層を Python にするだけでは、ロックインは半分しか抜けない。 PostgreSQL への移行が、抜ける最後のステップだ。そして、止まるライセンス費は契約書にある。新システムの開発費と並べれば、書き換えない理由が無いことが分かる。
ロックインから抜ける方法は、いつも同じだ
ここまで見てきた話は、すべて同じ構造だ。
- Java / C# のロジック層 → FastAPI(Python)に置き換える
- Oracle / SQL Server → PostgreSQL に置き換える
- PL/SQL のストアドプロシージャ → Python の関数に置き換える
- SAP / Salesforce → 自前システムに置き換える
- IT ベンダーやコンサルタントの委託 → 現場と AI に置き換える
これらは別々の問題ではない。同じ手で全部抜ける ── 並行稼働で書き換える。
旧を止めない。新を並行で作る。同じ入力を両方に流し、出力を突き合わせる。差分が消えたら、旧を止める。契約更新の時期に間に合わせる。ロックインは、「触れない・抜けられない」と思わせる心理的な装置だ。並行稼働は、その心理を物理的に解除する。触らずに、横に新しい本流を作る。新が動けば、旧が要らないことが誰の目にも明らかになる。
書き換えた基幹は、土台と門番の上に乗る
新システムの論理層 ── つまり書き換えた基幹ロジック ── は、FastAPI で一箇所の API として出す。なぜ API にするのか。基幹ロジック(在庫・受発注・料金計算…)を、画面ごとに書き散らさず、一箇所にまとめるためだ。公開 Web(2-11)のフォームも、社内アプリも、同じ API を呼ぶ ── 重複が消える。Python(FastAPI)なら AI が速く書け、型と自動ドキュメント(OpenAPI)が付く。
API は、2-03 の PostgreSQL を読み書きし、2-05 の門番(PocketBase)のトークンで本人確認する。新しい基盤は要らない ── すでにあるものに乗せる。
# FastAPI ── 門番のトークンを確かめ、土台(DB)を引く
from fastapi import FastAPI, Depends
app = FastAPI()
@app.get("/orders")
def orders(user=Depends(verify_token)): # 2-05 の門番が誰かを確かめる
return db.query("SELECT * FROM orders WHERE user_id=%s", [user.id]) # 2-03 の DB
基幹をいきなり全部 API 化するのではない。よく使う処理から、一本ずつ。AI と対話して書き、現行と突き合わせて確かめる(2-04 の VBA→Python と同じやり方だ)。これは並行稼働の論理そのものを、一本の API の粒度で回すことに他ならない。重い処理は裏で Python が捌き、結果だけ返す。
コードは 2-06 の Forgejo に置き、2-11 の公開 Web や社内アプリから呼ぶ。並行稼働で書き換えた基幹ロジックが、最後にこの一本の API として着地する。
最初の一本は、予約の受け口でよい
いきなり基幹から始めなくてよい。2-09 で送った予約の受け口は、この章の形をそのまま小さく持っている。決めは五つだ。
- 受付の枠と予約は、2-03 の PostgreSQL に置く
- 予定は、2-09 の Radicale に CalDAV で書く。Thunderbird とスマホに、そのまま出る
- 確認と前日の連絡は、2-08 のメールで送る。会議なら 2-09 の Jitsi のリンクを添える
- 身元は、2-11 のとおり門番のワンタイムで確かめてから受ける
- 画面は、公開 Web(2-11)の静的な頁から、この API を呼ぶ
@app.post("/bookings")
def book(slot_id: int, user=Depends(verify_otp)): # 2-11 ── ワンタイムで確かめた人だけ
db.execute("INSERT INTO bookings ...", [slot_id, user.email]) # 2-03
caldav.put(event_for(slot_id, user.email)) # 2-09
mail.send(user.email, confirmation(slot_id)) # 2-08
土台・門番・メール・カレンダーの上に、関数一つが乗る。これが「立てた物の上に書く」ということだ。基幹の一本も、同じ形で大きくなる。
例: 月次決算処理を並行稼働で置き換える
月末に動く決算処理バッチを考える。
旧: COBOL や Java で書かれた、長く動いているバッチ。中身は誰も完全には理解していない。月末に動く。失敗すると経理が止まる。
第一週: 旧バッチの入力(前月の取引データ)と出力(決算サマリ)を一年分エクスポートする。これを正解データとする。
第二週: AI に旧コードと運用ドキュメントを渡し、FastAPI 上の Python で同等処理を書かせる。一年分のデータを流し、出力が正解と一致するか確認する。一致しないところを潰す。
第三週〜第六週: 旧システムが動く本番タイミングで、同じ入力を新システムにも流す。毎月、出力を突き合わせる。差分があれば原因を特定して修正。
決めた月: 差分がゼロの月が決めた回数続いたら、責任者が決断する。「来月から新システムで運用」。旧バッチは止める。
担当者の負担は、並行稼働中だけ二重になるが、それが終わると軽くなる。そして、業務ロジックがコードと Markdown の両方で読める状態になる。
例: SAP の出荷管理から抜ける
中規模製造業の出荷管理を SAP で動かしている会社。
- データ層: SAP から夜間バッチで出荷データを Parquet にエクスポート (2-03 の Parquet と DuckDB)── SAP は触らない、読み取りのみ
- 新ロジック層: Polars と DuckDB で在庫照合・出荷判定・運送業者の振分けを Python で書く(AI が現場ヒアリングと既存 SAP の設定画面のスクショから初版を生成)
- API・画面層: 現場用の出荷指示は FastAPI(この章)で API 化し、HTML で画面を出す。社内 LAN だけで動かすので 2-02 の一台でホストできる
- 突き合わせ: 毎日、SAP の出荷結果と新システムの出荷結果を比較。差分があれば原因を AI と一緒に追う。SAP 側に「実は文書化されていなかったルール」が何度も見つかる
- 決めた日: 差分ゼロが決めた日数続いたら、新システムを本番に昇格、SAP は次の契約更新前に止める
結果はこうなる。ライセンス費が消える。業務ロジックが Markdown と Python に出る (SAP の「業務コンサルタント」が二度と要らない)。カスタマイズが現場で当日できる (これまでは SAP ベンダーに依頼して待っていた)。
これは 2-07 の「中身を自分の側に取り戻す」の基幹システム版だ。一度に捨てず、並行で抜ける。「SAP を一度に捨てない、並行稼働で抜ける」。
数字は、自分の契約書と見積もりにある
書き換えるかどうかを決めるときに効く数字は、二つしかない。どちらも自分の手元にある。
- いま払っているライセンス料 ── 契約書に書いてある。製品を止めれば、これが止まる
- 書き換えの見積もり ── 外注するなら、ベンダーから見積もりを取る。内製なら、並行稼働の期間に担当者が割く時間だ
この二つを並べれば、自分の会社の数字で判断できる。一般の相場や他社の事例は要らない。並行稼働で見つかる未文書化のルールの数も、書き換えてみれば自分の数字が出る。
小さな業務システムを丸ごと見たければ、公開リポジトリに実例がある ── seminar-kit
(aiseed-dev/seminar-kit。研修・セミナー管理: xlsx 様式が唯一のフォーム定義、未処理はメールボックス)と
mfg-kit(aiseed-dev/mfg-kit。製造業の見積・受注)。どちらも FastAPI と生 SQL と Flet の、この章の作法で立っている。
作らない領域が、外側の線を決める
線引きも、仕様の一部だ。会計・給与・税は、作らずに買う。国の制度に直結する領域は「自社固有」の対極 ── 全社会共通の汎用であり、1-05 の OSS ファーストと同じ理由で、実績ある既製品を使う側に置く。書き換えるのは自社固有のロジックだけ、という本章の原則の、これが外側の線である。
帳票は、出口の一形態だ。請求書や納品書は、FastAPI がデータを出し、テンプレートに差し込んで PDF にする ── 2-07 の入口・中身・出口の「出口」であり、AI が書ける定型コードだ。帳票専用の製品は要らない。
承認フローも、製品ではない。申請 → 承認 → 確定は、業務ルールの数行(誰が、どの金額から、誰の承認で)と、状態を持つ API の数十行と、通知のメール(2-08)でできている。ワークフロー基盤を買うのは、コードが書けなかった時代の解だ。
最後に一つ、旧システムを畳む前の確認がある ── 法定保存だ。帳簿や請求書には保存年限があり、年限は業務ルールの Markdown に一行書く。並行稼働を終えて旧を止める前に、保存対象のデータが自分の DB とファイルに揃っていることを確かめる。監査の記録も、手元の箱ならログが全部残っている ── 製品は要らない。決まりごとだけが要る。
画面を作る前に、デザインの基本を四つだけ押さえる
書き換えた基幹は、API として着地した。残るのは画面だ。道具は Flet(API と同じ Python の宣言的 UI、2-04)でいい。問題は道具ではない ── フロントエンドエンジニア以外は、デザインを教わったことが無い。だから AI が出した画面の良し悪しを判定できず、「もっと良い感じに」としか言えなくなる。
描く技術は要らない。要るのは、見て、名指しできる語彙だ。基本は四つしかない。
- 近接 ── 関係のあるものは近くに、関係の無いものは離す。項目がバラバラに見える画面は、この距離が崩れている
- 整列 ── 端を揃える。左揃えなら全部左揃え。揃っていない画面は、それだけで雑に見える
- 反復 ── 同じ役割には同じ形を繰り返す。画面ごとにボタンの色や見出しの形が違うのは、反復の欠如
- 対比 ── 重要なものは大きく・濃く、それ以外は小さく・薄く。全部が同じ大きさの画面は、何も伝えない
加えて二つだけ。色は 3 色まで(背景と文字とアクセント 1 色)、フォントは 1〜2 種。迷ったら、足すのではなく減らす。
この語彙があると、AI への指示が変わる。「もっと良い感じに」ではなく、「ラベルと入力欄の近接が弱い」「ボタンの色が反復していない」「合計金額の対比を強くしろ」── そのまま直せる指示になる。デザインの知識は、描く技術としてではなく、判定の語彙として効く。
そして、四原則の先も、デザインの大部分は感性ではなくルールでできている。余白は一定の刻みで取る(8 の倍数など)。文字サイズは数段階に決めて使い回す。文字と背景のコントラストには基準値がある(アクセシビリティ規格)。ボタンはボタンに見える形にする。取り消せない操作には確認を挟む ── どれも決まりごとであって、センスではない。
ルールなら、書ける。業務ルールを Markdown に出したのと同じ要領で、デザインのルールも 1 枚の文書に固定できる ── 余白の刻み、文字サイズの段階、色 3 つ、部品の形。画面を頼むたびにこの 1 枚を渡せば、AI が全画面に同じルールを適用する。反復は、自動で守られる。
ルールの中で、いちばん強い一本がグリッドだ。画面を等幅の列(ふつうは 12 列)に切り、部品は必ず列の境界に合わせて置く ── 決まりはそれだけだが、この一本で整列と反復の大半が自動で守られる。Web やアプリのデザイナーには、グリッドは「窮屈だ」「退屈だ」と嫌われがちだ ── 独創を売る仕事から見れば、格子は檻に見える。だが業務画面に要るのは独創ではなく、どの画面を開いても同じ場所に同じものがある予測可能性で、それを最も安く供給するのがグリッドである。しかも列数も余白も数字で書けるから、AI に最も正確に伝わるデザイン指示になる。利点は見た目だけでもない ── グリッドはレイアウトを固定する。部品の置き場所が中身より先に決まっているから、描画側は届いたデータに合わせて配置を計算し直す必要がなく、表示は速く、読み込み中に画面がガタつくこともない。規律が、そのまま性能になる。
「固定して、中身が収まらなくなったら困るのでは」という心配は、紙の記憶だ。紙は面積が決まっているから、収まらない中身は本当に困る。画面は違う ── そもそも紙より小さく、全部を一度に見せることは端からできない。だから広げる仕組み(スクロール、折りたたみの開閉)が標準で備わっていて、固定するのは置き場所だけ、増えた中身は必要なときに広げればいい。固定で困る場面は、画面には持ち込まれない。
それに、現場はグリッドをよく知っている。Excel のセルを小さな方眼に切り、枠に合わせて帳票を組む ── いわゆる神エクセル(Excel 方眼紙)だ。IT 業界はこれを「機械に読めないデータの最悪例」と呼んできた。だが、「読めない」は正確ではない。神エクセルは、Python が普通に読む ── 中身は最初から構造化されたファイルの中にあり、数十行のスクリプトで枠から取り出して表に起こせる(そのスクリプトを書くのが、いまは AI の仕事だ。2-15 の整備が、まさにこの作業だ)。方眼の本当の効能は、別の所にある。 方眼に合わせる限り、素人でも組めて、素人でも直せる ── 枠を一つ動かす、列を一本足す、それで帳票が変わる。現場が自分で作って自分で直せる道具は、開発の発注を要らなくする。誰に教わらなくても、現場は帳票を組むときに方眼を敷いた ── 置き場所を格子に合わせるという勘どころは、ずっと正しかったのだ。グリッドデザインは、その勘をそのまま受け継ぐ ── 枠は画面のグリッドへ、中身はデータベースへ。素人の修正に強いという方眼の効能も、そのまま画面に持ち込まれる。中身を移すのは「読めないから」ではない。毎回スクリプトで枠から掘り出すより、一度データベースに置くほうが安い ── それだけの理由だ。デザイナーには檻に見え、現場には方眼紙に見えた ── 同じ格子の話である。だから、素人はグリッドレイアウトを使う。本章の読者に、これ以上ふさわしい既定はない。
ルールの 1 枚の先頭には、まずこの 1 行を書く ── 「12 列グリッド、余白は 8 の倍数、入力フォームは 6 列幅」。
ルールでできている領域だから、AI はデザインができる。よくある画面の型、部品の形、余白の取り方 ── 定石は学習済みで、頼めば標準的で破綻のない画面を出してくる。ルールの 1 枚すら、たたき台は AI に書かせていい。
ただし、独創的なデザインは無理だ。AI の出力は、学習した平均に収束する。見たことのない見た目、ブランドを定義する新しい様式は、AI からは出てこない。だが、業務画面に独創は要らない ── 現場が迷わず使えるのは、むしろ見慣れた標準形のほうだ。独創が要る場面(ブランド、看板、売り物のデザイン)では、核になるイメージを人間が持ち込む。デザイナーがいなくても業務画面が成立するのは、そこに独創が要らないからである。
そして、独創が要らないのはデザインに限らない。
プロのデザイナー以外は、汎用のデザインパターンを使う。プロのプログラマー以外は、OSS のライブラリを組み合わせて使う。プロの作家以外は、平易な文章を書く。独創は職業であって、既定ではない。
独創は、それを売る職業の持ち物だ。売り物でない場面では、共有された定石がいちばん速く、安く、壊れない ── そして定石こそ、学習の中心にあるから、AI が最も確実に再現する。OSS の部品を組む(1-05)、業務ルールを平易な Markdown に書く(本章)、画面をグリッドに合わせる ── 全部、同じ一つの作法である。
実務で突き当たる課題も二つあるが、どちらも同じ型 ── ルール ── で受けられる。
一つ目は、画面の多様化だ。PC・タブレット・スマホ、画面幅はばらばらで、機種ごとに画面を作っていたらきりがない。これもルールで受ける ── 幅の段階を 2〜3 決め (狭い = 1 列、広い = 2 列と側欄、など)、段階ごとの並べ方を先の 1 枚に書き足す。グリッドを敷いてあれば、この書き足しは「広い幅は 12 列、狭い幅は 4 列」という列数の減らし方の 1 行で済む。Flet は同じコードがデスクトップにも Web にもモバイルにも出るから、多様化への対応は「機種ごとに画面を作る仕事」ではなく、ルールを数行足す仕事になる。
二つ目は、日本語フォントだ。和文フォントは数千の字形を抱えるため重く、選択肢が少なく、良いものは有償 ── これが長年の課題だった。いまは違う。モリサワの UD 書体(読み間違えにくさを目的に設計されたユニバーサルデザイン書体)の一部が無料で使える ── BIZ UD ゴシック / 明朝は Windows に標準搭載され、Google Fonts からも配信される。ルールの 1 枚には「本文は BIZ UD ゴシック、無ければシステムフォント」と 1 行書けば済む。本サイトの本文も、この方式(端末にあれば UD 書体、無ければシステム書体)で組んである。
図やスライドまで含めたデザインの作法は、次章(2-13)が扱う。
デザインの大部分は、感性ではなくルールだ。ルールなら、書いて渡せる ── 業務ルールと、同じように。
公開の前に、四つの点検をする
動く部分を外に出す前に、点検を四つ回す。2-11 の「ビルド → 確認 → デプロイ」の「確認」の段で、毎回やる。結果は設計書に残す。探す作業は AI に手伝わせてよいが、表と突き合わせて決めるのは人だ。
四つは、特殊な事故の型ではない。OWASP Top 10 の 2025 年版は、1 位が権限の不備、 4 位が暗号の不備、5 位が注入、10 位が例外の扱いの誤りだ(OWASP)。 MITRE の CWE Top 25 の 2025 年版では、権限確認の欠落が 4 位(前年の 9 位から上がった)、他人の ID を指定して取れる不備が 24 位にある(MITRE)。どちらも 2026-10-07 に確認した。四つの点検は、この上位にそのまま対応する。
一つ目 ── 全部の API の権限を確かめる
- FastAPI の全ルートと、PocketBase の全コレクションを一覧にする。設計書の表に、ルートごとに「誰が呼べるか」(公開・ワンタイムで確かめた人・ログインした人・本人・管理者)を書く。表と実装を突き合わせ、表に無いルートは消す
- 新しいルートを足したら、既存のルートも全部点検し直す。認証はルーター単位でまとめて掛け、付け忘れたときに「拒否」の側に倒れる形にする
- ID を受け取る API は、そのレコードの持ち主がトークンの本人かを比べる。他人の ID を指定して取れないことを、テストで確かめる
- PocketBase のルールは list・view・create・update・delete の五つを全部確かめる。空文字は「誰でも」、null は「管理者だけ」。本人に限るなら
owner = @request.auth.id - ワンタイム(2-11)は、番号の有効期限、一度使った番号の拒否、試行回数の制限を確かめる
- Flet の画面でボタンを隠すのは、権限の確認に数えない。拒否するのは API の側だ
- 回避用のルートが残っていないか探す。
bypass、debug、test、devの名前と、if DEBUGで認証を外す分岐 - 例外が起きたとき、許可の側に倒れていないか確かめる。トークンの検証で例外が出たら、通さず拒否する
二つ目 ── 入力の扱い
- SQL は文字列を結合して作らず、パラメータで渡す。f-string や
+で SQL を組んでいる箇所を検索する - PocketBase の filter に利用者の入力を直接埋め込まない。SDK のパラメータ付き filter を使う
- HTML への出力は、
innerHTMLとdocument.writeを使わずtextContentに。Jinja2 は自動エスケープを有効にし、|safeを検索する。AsciiDoc や Markdown を HTML にするときは、生の HTML の通過を無効にする(2-10) - 外部コマンドは、
subprocessにshell=Trueで入力を渡さず、引数はリストで渡す - アップロードされたファイル名やパスの引数は正規化し、決めたディレクトリの外に出ないことを確かめる(
../対策) - 検証はサーバー側でする。FastAPI の入力は Pydantic で型・長さ・範囲を決める。Flet 側の検証は数えない
- 入力の URL を取りに行く処理は、行き先を許可リストで限る
- CORS は、
allow_origins=["*"]と認証情報の送信を同時に許可しない。エラーを消すために*にしない
三つ目 ── 秘密情報
- 鍵・パスワード・トークンを、コード・配る Flet アプリ・JS に書かない。配った物に入れた鍵は、誰でも取り出せる。外部 API の鍵が要る処理はサーバー側に置く
.env、*.db、バックアップ、エクスポートしたファイルは.gitignoreに入れる。コミットの前に、入る物の一覧を確かめる- 公開する前に、全履歴を gitleaks などで走査する。履歴に入っていた鍵は、履歴を消すのではなく鍵を取り替える
- 初期パスワード、デモ用アカウント、サンプルの鍵が残っていないか確かめる
- パスワードは自前で保存せず、門番(PocketBase)に任せる
- 配る Flet アプリにトークンを置くなら、OS の安全な保管場所(鍵束)に置く。Web 版は寿命の短いトークンにして、ブラウザに長く置かない
- Base64 は暗号ではない。暗号化に使う関数が実在するか、失敗したときに平文のまま続けていないかを確かめる
- ログにトークンや秘密情報を出さない。AI のエージェントが読める場所(作業ディレクトリ)に
.envを置かない(2-17)
四つ目 ── 残っている TODO
TODO、FIXME、XXX、HACK、仮、暫定、demo、temporary、for now、skip、bypassと、コメントアウトされた認証・権限のチェックを探す- 見つけた物は、設計書の「未完了」の表に移す。内容・場所・公開前に必須かどうかを書く
- 認証・権限・暗号・入力検証に関わる物が一つでも残っていたら、公開しない
点検は、ビルドと本番のあいだに置く。四つのうち一つでも通らなければ、出さない。
確かめ方
この章は、次の七つができていれば済みだ。
- 旧システムと新 API に同じ入力を流すと、出力が一致する
- 差分ゼロの日が、決めた期間ずっと並んでいる(月次なら、差分ゼロの月が連続する)
- 業務ルールが Markdown で読める。現場の担当者が読んで、正しいと言える
- 社内アプリの画面も公開 Web のフォームも、同じ API を呼んでいる。同じロジックが二箇所に無い
- 法定保存の対象データが、自分の DB とファイルに揃っている
- 旧システムを止めたあと、月次の業務が新システムだけで一周した
- 公開の前の四つの点検が通っていて、結果が設計書に残っている
diff old_output.csv new_output.csv # 旧と新の出力を突き合わせる
curl -s https://api.example.com/docs # FastAPI の自動ドキュメントが返る
人が持つ物
人が渡す値
- 業務ルールの中身 ── 締め日、例外処理、承認の金額の線引き。Markdown に確定させる
- 正解データの範囲 ── 旧システムから何ヶ月分の入力と出力を出すか
- 並行稼働を終える条件 ── 差分ゼロが何ヶ月続いたら旧を止めるか
- ベンダー製品の契約更新日 ── 逆算して並行稼働を始める日
- 新 API が使う DB の接続情報と、門番(2-05)のトークンの設定
- デザインのルール 1 枚に書く値 ── 列数、余白の刻み、色 3 つ、本文フォント
AI が「やる前に言う」操作
- 旧システムを止める
- 本番の処理を新システムに切り替える
- ベンダー製品の契約を解約する、更新しない
- 本番の DB に新システムから書き込む
- 旧システムや旧 DB のデータを消す
確かめた版と日付
- FastAPI、PostgreSQL、Flet、Polars、DuckDB、Parquet ── 版は指定していない
- 移行の道具は pgloader(2-03)
- 手順を書いたのは 2026-07-15、見直したのは 2026-10-06
- 版が上がっていたら、AI に公式の手順を確かめさせてから進める
まとめ
基幹システムと「うまく付き合う」のは、もう古い。
- 並行稼働 ── 新を FastAPI で作り、旧と並べて動かし、出力を実測で突き合わせる
- 旧を止める ── 止める条件を先に決め、差分が消えたら止める
- 業務知識 ── 一気に Markdown に出す。初版はたたき台で十分、残りは差分が教える
- 現場がテストを書く ── ルールを知る者がテストを書く。委託は止める
- 着地点 ── 2-03 の DB と 2-05 の門番に乗る、一本の API。最初の一本は予約の受け口
- 画面 ── デザインはルールだ。1 枚に書いて、画面を頼むたびに渡す
やる時はやる。中途半端な共存は、組織を硬直させる。AI で書き換えのコストが桁で下がった時代に、残す理由は無い。
次章では、画面で使ったのと同じ手を、図と資料に広げる。構造図もスライドも配布する資料も、文字とコードから出す。