シフトレフトは施策ではない --- 品質の作り込みは最初から始まっている
1. はじめに
ここ数年、「シフトレフト」という言葉を耳にする機会が増えました。
テスト、QA、セキュリティ、運用――
何かにつけて「シフトレフトしよう」という言葉が使われます。
一方で、正直なところずっと違和感もありました。
話を聞いていると、人によって指しているものが違います。
テストを早く書くことを指す人もいれば、
QAを設計段階から巻き込むことだと言う人もいる。
レビュー工程を前倒しすることだ、という説明を聞いたこともあります。
どれも間違ってはいないと思うのですが、どれもしっくりこなかったです。
なぜなら自分自身は、
ウォーターフォールでもアジャイルでも、
シフトレフトという言葉を意識せずに、同じようなことをずっとやってきたからだと気づきました。
しかも、そのやり方で
「大きく炎上した」「後工程で破綻した」
という経験はほとんどありませんでした。
最近になって気づいたのは、
自分が自然にやっていた開発の進め方に、
後から「シフトレフト」という名前が付いただけ
なのではないかということです。
この記事では、
実務の中で何をやれば炎上を防げるのか
という観点で、シフトレフトの本質を、誤解を恐れずに整理してみたいと思います。
2. 自分の耳に入ってきた「シフトレフト」の説明と、腑に落ちなかった理由
シフトレフトという言葉に違和感を覚えた理由は、
定義そのものというよりも、
人から説明されたときの言い回しに腑に落ちないものが多かった
という点にあります。
ここからは、
自分が実際に耳にしてきた説明の中で、
「それは違う気がする」と感じたものを挙げます。
これが一般論なのかどうかは分かりません。
あくまで、自分の経験の中での話です。
例1:テストを早く書くことがシフトレフト
「テストを後工程に回さず、早い段階で書こう」
という説明を聞くことはよくありました。
確かに、
テストを後回しにするよりは、
早く書いたほうが良いのは事実です。
ただ、その説明を聞くたびに、
自分の中では次の疑問が残りました。
- 仕様が曖昧なままでもテストは書けてしまう
- モデルが破綻していてもテストは通ってしまう
- 境界条件が定義されていなくても、確認項目は作れてしまう
この状態で書かれたテストは、
「破綻を防ぐもの」ではなく、
「破綻していることを確認するもの」
にしかなりません。
テストを左に寄せても、
破綻の原因が左側に残ったままでは意味がない
という違和感がありました。
例2:QAを設計段階から巻き込むことがシフトレフト
「QAを早い段階から関与させるのがシフトレフトだ」
という説明も、何度も聞きました。
これも、一部は正しいと思っています。
QAの視点が早く入ることで、
見落としに気づける場面は確実にあります。
ただし、
仕様そのものが曖昧な状態でQAを巻き込むと、
QAができることは限られます。
結果として、
- 曖昧さを指摘する
- 未定義な点を列挙する
といった作業に終始しがちです。
自分にとって重要だったのは、
QAに渡す前に、後工程で問題になる曖昧さを潰せているかどうか
でした。
例3:レビュー工程を前倒しすれば良いという説明
「レビューを早い段階でやれば手戻りが減る」
という話も、よく聞きました。
ただ、形式的にレビューのタイミングを早めても、
- 曖昧な設計書
- 前提条件が書かれていない仕様
- 境界が定義されていないモデル
がレビュー対象であれば、
レビューの質は上がりません。
実際には、
- 「ここはどういう前提ですか?」
- 「このケースはどうなりますか?」
といった確認が増えるだけで、
曖昧さが前倒しされるだけ
という印象を持つことが多かったです。
これらの説明に共通して感じていたのは、
「工程を左に動かすこと」自体が目的になっている
という点でした。
だから自分は、シフトレフトを
- テストを早く書くこと
- QAを早く巻き込むこと
- レビューを前倒しすること
といった個別の施策ではなく、
後で高くつく問題の芽を、
原因が生まれる前に潰すための考え方
として捉えるようになりました。
3. シフトレフトの本質
シフトレフトという言葉は、テストやQAの話として語られることが多いですが、
本質はもっと単純で、
「後で直すと致命的に高くつくものを、
その原因が生まれる前の段階で潰す」
これだけです。
ソフトウェア開発では、工程が進めば進むほど修正コストが跳ね上がります。
RDBのスキーマが固まったあとに整合性が破綻すれば、修正は地獄になり、
APIのインタフェースを後から変えれば、結合したすべてのサービスに影響します。
また、モデリングの誤りは長く影響します。
これらは「どこでミスが起きたか」という話ではなく、
「どこでミスを見つけられなかったか」
という問題です。
- モデルの整合性が曖昧なままAPI実装に入る
- フロントエンドとバックエンドで用語がズレたまま進む
- 想定しているデータ構造がチーム間で共有されていない
- 不確実性のある技術要素を検証せず、本番実装に突入する
こういった「前工程で潰せたはずの問題」が後工程に流れ込むと、
修正コストはドミノ倒しのように膨らんでいきます。
つまりシフトレフトとは、
後工程の爆発的な修正コストを「仕方ない」で処理する文化から、
前工程で破綻の芽を刈り取る文化へ移行するための思想
にすぎません。
言い換えれば、ウォーターフォール時代から自然にやってきた、
- モデリングで不整合を洗い出す
- 仕様の曖昧さを詰める
- 結合で死ぬポイントを事前に想定する
- リスクのある技術を少人数で即PoCする
といった行為に、後から名前が付いただけだと捉えています。
(用語の由来はさておき、ここでは実務上の意味に絞ります)
本来のシフトレフトは、
テストを早く書くことでも、
QAを早期に巻き込むことでもなく、
「破綻の種を、できるだけ早く見つけ、
破綻する前に潰す」
これを徹底するための考え方です。
4. 自分が自然にやっていた“前工程の潰し方”
後で高くつく不確実性は実装前に潰す。そのために自分がやっているのはモデリングとPoCですが、「できる人がやれば強い」で終わりやすいので、後半では属人化の理由と最低限残すべき成果物も整理します。
4.1 最初にモデリングする
ここからは、自分が前工程で自然にやっていたことを、
より具体的に書いていきます。
やることは2つだけです。
- リソースとイベントを洗い出す
- それらを動かしたときに破綻しないか検証する
まず最初に行うのが モデリング です。
ここで言うモデリングは、
実装クラスやデータベース設計、API設計の話ではありません。
これまで多くのプロジェクトで見てきたのは、
テーブル定義やAPI仕様といった「成果物」は存在するが、
それらがどのような思考の結果として生まれたのかは分からない、
という状態でした。
DB設計やAPI設計は、
意思決定の結果を強く圧縮した形です。
しかし、自分が本当に知りたかったのは、
その圧縮前――
設計者の頭の中にあった構造 でした。
- 何をリソースと捉えたのか
- 何をイベントとして切り出したのか
- どんなユースケースを想定していたのか
- どこで迷い、何を捨てたのか
自分にとっての「概念モデル」とは、
その圧縮前の思考を、
他人が追体験できる形で外に出すためのものです。
まずは「何が存在し、何が起き、どうつながるか」を実装前に言語化します。具体的には、リソースとイベントを切り出すところから始めます。
4.1.1 リソースとイベントを抽出する
最初に行うのは、
システムの中で 状態を持ちながら存在し続けるもの を洗い出すことです。
たとえば、
- 従業員
- アカウント
- 注文
- 商品
といったものが該当します。
自分はこれらを リソース と呼んでいます。
※ここで挙げているのはあくまで一例で、イベントの結果として新たなリソース(例:請求書)が生成されるケースも含みます。
また、この段階では、
テーブルやクラスとしてどう表現するかは考えません。
業務やシステムの中で、
長く存在し、状態が変化し続ける対象は何か
という観点で整理します。
次に、それらのリソースに対して
どんな出来事が起きるのか を考えます。
ここで意識しているのは、
CRUD操作や内部処理ではなく、
現実世界で意味を持つ出来事として切り出すこと です。
たとえば、
- 従業員が入社する
- 従業員が退職する
- 注文が確定する
- 請求書が発行される
- 請求書が発送される
- 支払いが完了する
といったものです。
これらは単なる
「作成される」「更新される」といった操作ではなく、
何が起きたのかを第三者が理解できる事実 です。
自分は、こうした出来事を
リソースとは分けて イベント として扱います。
この段階では、
イベントがどのように実装されるかや、
どこで処理されるかまでは考えません。
まずは、
- どのリソースに対して
- どんな出来事が起きうるのか
を漏れなく洗い出すことを重視します。
ここまでが、
「モデルを作る」作業 です。
ちなみに、これは自分がモデリングを学んだときに教わったことですが、
リソースの数に対してイベントの数が極端に少ない場合は、
作ったモデルに欠陥がある可能性を疑った方がよい とされています。
実際にモデルを見直してみると、
「起きているはずの出来事を、状態変更としてまとめてしまっている」
「本来分けるべきイベントを見落としている」
といったケースは少なくありません。
自分自身も、
イベントがあまりに少ないモデルになった場合は、
「まだ切り出せていない出来事があるのではないか」
と立ち止まって見直すようにしています。
4.1.2 モデルが成立しているかを検証する
4.1.1 でリソースとイベントを洗い出したら、次に行うのが モデルの検証 です。
ここで言う検証とは、テストを書くことでも、コードを動かすことでもありません。
洗い出したリソースとイベントを動かしたときに、
システム全体として破綻しないかを考える作業 です。
モデルの検証では、次の 3 点を重点的に確認します。
① イベントの前提と結果が一貫しているか
各イベントについて、必ず次を自問します。
- このイベントは、どのリソースのどの状態で起きるのか
- このイベントが起きたあと、何が「確定した事実」になるのか
- 途中で失敗した場合、どこまで状態が進むのか
ここが曖昧なまま進むと、
イベント自体は正しく発火しているのに、全体として整合性が取れないモデル になります。
特に意識しているのは、
「すでに起きた事実」と「これから起きる予定」を混ぜないこと です。
② イベントが連鎖したときに破綻しないか
モデルは単体で見るとほぼ必ず「正しそう」に見えます。
問題になるのは、それらが 連鎖したとき です。
- あるイベントのあと、次に何が起きるのか
- イベントの順序が前後したらどうなるか
- 想定していないイベントが割り込んだらどうなるか
これを頭の中でシミュレーションし、
途中で説明できなくなる箇所がないか を確認します。
説明が破綻する部分は、モデルがまだ成立していない箇所です。
③ 再実行・再送が起きても壊れないか
実システムでは、通信の再送・処理のリトライ・遅延したイベントの到着は避けられません。
そのため、次を検証段階で必ず確認します。
- 同じイベントをもう一度処理したらどうなるか
- 二重に処理されても状態は壊れないか
再実行できないモデルは、後工程で必ず破綻します。
検証の補助としてユースケース図を書く
この検証を行う際、自分は補助として ユースケース図を毎回描きます。
目的は「想定しているユースケースが、本当にこのモデルで説明できるか」を確認するためです。
- このユースケースは、どのイベントの組み合わせで成立するのか
- 無理な前提や飛躍が含まれていないか
ユースケース図に落とすことで、頭の中のモデルを一度、客観的に扱えるようになります。
モデリングと検証は往復する
モデルは一度で完成することの方が少ないです。
実際には「作る → 動かして検証する → 矛盾や不足に気づく → 修正する」という往復を繰り返します。
この往復を 実装前にやり切ること が、後工程での手戻りを減らす自分なりのシフトレフトです。
検証のメモとして最低限残すもの(モデルに書いておけばいい)
- リソース: 何が“存在し続ける”ものか / 代表的な状態(区分値)
- イベント: 何が“起きた事実”か / 起きる前提 / 起きた後に確定すること
- 破綻ポイント: 連鎖・順序入替・二重発火・遅延到着で説明できなくなる箇所
4.1.3 UIは要求を具体化するが、そのままでは「事実」ではない
— UI起点で進んでしまったときの“リカバリ”として、モデルに翻訳する
自分は通常、UIより先にモデルを固めます。
ただ現実には、すでにUIがある程度作り込まれた状態から議論が始まることも多いです。
その場合にやるべきなのは、UIをそのまま仕様として採用することではなく、
UIが含んでいる前提を「業務上の事実(イベント)」に翻訳してから前に進むことです。
UI起点で情報整理を行うこと自体は有効です。
UIは要求を具体化する力が強く、議論の入口として優れています。
ただ、UIは「こう見せたい/こう操作したい」という表現であって、
そのままでは業務上の事実(モデル)ではありません。
UIの説得力が強いほど、前提や境界が曖昧なままでも先に進めてしまい、
あとからモデルや実装に歪みが入ります。
そこで、UIを見ながら次を必ず確認します。
- これは業務上の「事実(イベント)」として成立しているか
- 既存のイベントの組み合わせで説明できないか
- 新しいイベントが必要なら、それは現実世界の出来事として妥当か
この翻訳ができないUIは、後工程で必ず高くつきます。
UIの議論を、実装に耐える“事実の構造”に落とし込むためのゲートとして、モデルを使います。
4.2 方式設計に対する確証を PoC で得る
実はモデリングが終わると、自分の中ではすでに
どのような方式で実装されそうか
というイメージがほぼ浮かんでいます。
- どこが同期で、どこが非同期になるか
- どの境界で責務を分けるか
- どのリソースがどこに保存されるか
- どのイベントがどこをトリガーに発生するか
この時点では、
細かい実装までは決めていませんが、
アーキテクチャとしての骨格 はほぼ見えている状態です。
PoC の目的は「方式設計の実在性」を確かめること
自分がPoCを行う理由は明確です。
頭の中で描いた方式設計が、
現実の制約の中でちゃんと駆動するのか。
これを確認するためです。
ここで言う制約とは、例えば次のようなものです。
- 実際のライブラリやミドルウェアの挙動
- データの流れ方や遅延の発生タイミング
- 失敗時に残る状態や副作用
- 再実行やリトライが成立するかどうか
設計としては筋が通っていても、
現実の環境では成立しない方式は少なくありません。
だからこそ、
「理論上は正しそう」な状態で本実装に入らない
ことを強く意識しています。
PoC で確認するのは「正しいか」ではなく「採用できるか」
PoCで自分が見たいのは、
その方式が「正解かどうか」ではありません。
この方式を採用して、
安心して先に進めるかどうか。
- 想定した流れで処理は実際に通るか
- 想定外の挙動が起きたときに理解できるか
- 失敗した状態から立て直せそうか
- 運用時に人が判断できる情報が残るか
これらが確認できれば十分です。
逆に、
ここで不安が残る方式は、
どれだけきれいに設計しても
後工程で足を引っ張る可能性が高いです。
なぜこのタイミングで PoC を行うのか
PoCを行うのは、
モデリングが終わり、
方式設計のアタリが付いた この段階 です。
- 全体構造が見えている
- 検証すべき論点が明確
- 影響範囲を限定したまま試せる
この状態でPoCを行えば、
「この方式は無理そうだ」という結論が出ても、
設計を素直に引き返せます。
一方で、
この確証を得ないまま実装に進むと、
問題が表面化するのは
結合や運用といった、
最もコストが高いフェーズになります。
だから自分は、
頭の中で方式を描く
→ PoCでその方式が現実に駆動することを確認する
→ そこで初めて本実装に進む
という流れを取っています。
これも、自分が自然にやっていた
シフトレフトの一つです。
ただし、このやり方には弱点もあります。
それは、
自分の過去の成功体験や慣れた技術に引きずられた方式設計を、
無意識のうちに選びやすい という点です。
頭の中で描くアーキテクチャは、
どうしても自分が経験してきた設計や、
過去にうまくいった構成をベースに組み立てられがちです。
その結果、
他にも選択肢があるにもかかわらず、
検討の幅が狭くなってしまうことがあります。
そのため、方式設計の段階では、
意識的に第三者の視点を入れることが重要 だと考えています。
自分とは異なる経験や前提を持つ人にレビューしてもらうことで、
設計の思い込みや偏りに早い段階で気づけるからです。
これもまた、
後工程での手戻りを防ぐための
シフトレフトの一部です。
5. なぜこのやり方は属人化しやすいのか
ここまで書いてきたやり方は、
自分にとっては「自然にやっていたこと」でした。
ただし、
これをチームや組織全体で再現しようとすると、
途端に難しくなります。
理由はシンプルで、
このやり方が構造的に属人化しやすい からです。
思考の多くが頭の中で完結してしまう
モデリングや検証、方式設計の初期段階では、
- 何をリソースと捉えるか
- 何をイベントとして切り出すか
- どこで破綻しそうか
といった判断を、
かなり頭の中で行っています。
これらはコードにも、
設計書にも、
すぐには表れません。
その結果、
- 気づいたら設計が固まっていた
- なんとなくうまく進んでいる
という状態になりやすく、
プロセスが見えないまま成果だけが出る
という構図になります。
これが属人化の一つの正体だと思います。
属人化を防ぐのは、正直かなり難しい
ここまで書いてきたやり方は、
属人化を防ぐために意識すれば再現できる、
という話に聞こえるかもしれません。
ただ、正直に言うと、
自分自身もそれが十分にできているとは思っていません。
実際には、
- モデリング
- 検証
- 方式設計
の多くを、
1人で一気にやってしまうことの方が圧倒的に多いです。
毎回誰かと一緒に進めるのは、
時間や状況を考えると現実的ではありません。
一緒にできたときは、確かにうまくいった
それでも、
これまでに数は多くありませんが、
誰かと一緒にこの工程を行えたケース がありました。
ある基盤マイクロサービスを作ったときは、
まさにその例です。
- モデルを一緒に考え
- どこが怪しいかを議論し
- 方式設計の判断も共有できた
結果として、
- なぜその設計になったのかを説明しやすく
- 認識のズレも少なく
- 属人化もしにくい
状態を作ることができました。
この経験から、
本来は複数人で行う方が健全 だといえます。
それでも現実は、1人で進めてしまう
一方で、
多くのケースでは、
そこまでの時間や余裕が取れず、
結局は1人で進めてしまいます。
そうすると、
- 思考の過程は頭の中に残り
- 成果物だけが外に出る
という状態になりがちです。
後から見た人にとっては、
- なぜこの設計なのか分からない
- 変更してよいのか判断できない
という状況を生みやすくなります。
それでも「モデリングできる人を増やしたい」と思っていた
属人化を完全に防げているわけではありませんが、
それでも
モデリングできる人を増やしたい
という意識はずっと持っていました。
属人化の難しさは、教育の場でも同じ形で現れました。
新卒研修でモデリングを教えたことがありますが、講義名が「Database Design」になっていました。
事前に「Database設計ではなくモデリング」と伝えても、講義中の質疑や課題では話題がER図や正規化に回収されがちでした。
モデリングは、放っておくと実装設計(特にDB設計)に吸い寄せられます。だから「今やっているのは何のための作業か」を言葉で固定しないと、再現性が落ちます。
だからこそ、言葉として残したかった
この経験を通して、
モデリングは
- 教えればすぐ身につくものでもなく
- 仕組み化すれば完全に解決するものでもない
と実感しました。
それでも、
「何を考えているのか」
「どこで立ち止まっているのか」
を言葉として残しておけば、
後から追体験することはできます。
この文章も、
その延長線上にあります。
シフトレフトは才能ではなく、順番の話
ここまで振り返ってみると、
特別なテクニックを使っているわけではありません。
やっていることは一貫していて、
- モデルを作る
- そのモデルを疑い、検証する
- 方式として現実に成立するかを確認する
- その上で実装やUIに進む
という 順番 を守っているだけです。
この順番が崩れると、
- UIや実装が先行する
- 後からモデルを合わせにいく
- 無理が溜まり、どこかで破綻する
という流れになりやすくなります。
属人化を減らす最小セット
属人化をゼロにするのは難しいです。自分も毎回できていません。
ただ、次の3点だけは残せると、後から追える確率が上がります。
- リソース一覧(状態を持つもの)と、その代表的な状態遷移
- イベント一覧(何が起きたか)と、前提条件・副作用・冪等性の方針
- “迷った論点”メモ(捨てた案と捨てた理由)
おわりに
この文章を書いた理由は、
「自分のやり方が正しい」と主張したかったからではありません。
これまでの経験を振り返る中で、
- なぜ後工程で問題が起きやすいのか
- なぜ最初に考えておくべきことがあるのか
- なぜ「後で直す」がうまくいかないのか
を、
自分なりに言語化しておきたいと思っただけです。
実際には、
ここで書いたやり方を常に理想通り実践できているわけでもなく、
多くの場面では1人で進めてしまっています。
それでも、
- モデルを作る
- そのモデルを検証する
- 方式として現実に成立するかを確認する
という順番を意識するだけで、
後から大きく崩れるケースは確実に減りました。
シフトレフトという言葉が使われなくても、
この考え方自体は特別なものではありません。
そもそも品質への活動は、
「シフトレフトするもの」ではなく、
最初から始まっているもの です。
後工程で問題が起きるのは、
設計や実装が遅れているからではなく、
考えるべきことを考えないまま先に進んでしまう
場面があるからです。
どこで何を考えるのか、
どこまで決めてから次に進むのか。
それを少しだけ意識するだけで、
設計や実装の進み方は大きく変わります。
この文章が、
誰かが立ち止まって考えるきっかけになれば、
それで十分だと思っています。
Discussion