BIGLOBEの「はたらく人」と「トガッた技術」

TDDが必要なのはどんな時?新人の質問からテスト駆動開発の理解を深める

開発部門(基盤本部)でエンジニアの育成を担当している高玉です。

BIGLOBEでは週に1日、集合型の新人エンジニア研修を開催しています。インターネットに公開された教育コンテンツを活用しながら、手を動かして学ぶ(Learn by doing)が特長です。

style.biglobe.co.jp

研修の後に必ずアンケートに回答してもらっているのですが、新人ならではの質問をもらえるのがいつも楽しみです。

さて、テスト駆動開発(Test Driven Development:TDD)について学んだ後にもらったのが、次の質問です。

  • Q 仕様がコロコロ変わるケースや、コードを触りながらわかっていくような非機能要件などには適用できなさそうだと思いました。どういったケースにTDDが用いられるのでしょうか。逆にどういったケースには向かないのでしょうか?

研修の様子をお伝えするために、この質問にどう回答したのかをご紹介していきます。

質問の背景を理解し、質問を分解する

若いメンバーからもらったせっかくの質問です。ただ反射的に答えるのではなく、一呼吸おいて、その質問が生まれた背景についても思いをはせつつ、質問を分解していきます。

留意すべき背景は、新人エンジニアたちは学生時代にテストコードを書く機会が無かった点です。経験が少なければ、異なった概念も同じものに見えてしまいます。そこで、TDDの質問ではあるものの、自動テストやテストコードの必要性について聞かれているかもしれない、と判断しました1

そこで、Q を次の2つの質問に分解してみます。

  • Q1. どういった場合にテストコードが必要になるのか?
  • Q2. テストコードをTDDを使って作るべきか?

ここで、Q1の「どういった場合に」ですが、元のQには「仕様がコロコロ変わるケース」と「コードを触りながら分かっていく非機能要件」の2つの状況が問われています。後者は、非機能要件といいつつ「技術検証」が埋もれているようです。そこで、Q1を、以下の3つの質問に分解してみました。

  • Q1a. 仕様がコロコロ変わる場合にテストコードは必要か?
  • Q1b. コードを触りながら分かっていく技術検証にテストコードは必要か?
  • Q1c. 非機能要件の検証にテストコードは必要か?

分析の結果、5つの質問に分解することができました。それぞれについて回答していきます。

Q1. どういった場合にテストコードが必要になるのか?

今後メンテナンスする必要のあるプロダクトコードには、テストコードが必要です。反対に、使い捨てのコードならテストコードも不要です。

ただし、使い捨てのコードであってもテストコードを書くことで作業が効率化される場合があります。例えば、ゴールを達成できたかどうかを何度も繰り返し目視で確認するような場合です。テストコードを書いて楽になるなら、ぜひ書いてくださいね。

Q1a. 仕様がコロコロ変わる場合にテストコードは必要か?

プロダクトコードをメンテナンスするなら必要です。

とはいえ、仕様が変わるたびにテストコードも書き直すのは、コストがとてもかかってしまいます。であれば、そもそも「仕様がコロコロ変わる」ことをどうやったら防げるかを考えてみてください。

どうして仕様が変わるのでしょうか?もしかすると、顧客のニーズを把握することなく、出たとこ勝負でプロダクトを開発しているのかもしれません。全社の新人研修で学んだデザイン思考を活かし、できるだけコストをかけずに(わざわざコーディングせずに)プロトタイピングをして、顧客のニーズを検証できないか工夫してみましょう。

なお「プロトタイピング」について、エンジニアがよくよく心がけたいことがあります。それは「捨てるつもりで書いたプロトタイプのコードが、そのままプロダクトコードになる悲劇が生まれやすい」という経験則です。

プロトタイプのコードは絶対に捨てても良いこと、プロダクトを開発するタイミングで新しくコードを作り直して良いことを、必ず確認してください。もしその約束が取り付けられないのであれば、色々と覚悟を決めてください。

Q1b. コードを触りながら分かっていく技術検証にテストコードは必要か?

技術検証に使ったコードを捨てても良いのなら、テストコードは不要です。

なお、技術検証が合格か、不合格かの確認に手間がかかるなら、自動化しましょう。その自動化に自動テストが役立つなら、テストコードを書きましょう。

Q1c. 非機能要件の検証にテストコードは必要か?

非機能要件(例えば、性能やセキュリティ)が満たされていることをどうやって確認していますか?その確認作業を自動化したほうが効率が良いのなら、自動化しましょう。

サービスを運用しはじめた後も、非機能要件を満たしているかどうかを常時確認する必要がありますか?もしその確認に自動テストが役立つなら、テストコードを書きましょう。

Q2. テストコードをTDDを使って作るべきか?

これは、その人の技術レベルや好みに依存します。

TDDは、達成したいことを小さなゴールに分解し(タスクばらし)、それら小さなゴールを達成するテストを書き(テストファースト)、そのテストをなんとかクリアできるプロダクトコードを書き、テストに守られた状態を作ってからプロダクトコードをキレイにします(リファクタリング)。このようにタスクばらし→テストファースト→リファクタリング、の順番でプロダクトコードとテストコードを書くのがTDDです。

BIGLOBEでは「プロダクトコードとテストコードを一緒に書いて、1つのプルリクエストにまとめて提出すること」をルールとして決めているチームが多いです。CI/CDのためにも自動テストが必須だからです。ただし、そのテストコードをTDDで作ることは強制していません。人によっては、リファクタリングする必要のないキレイなコードを一発で書けるからです。

とはいえ、コードを書いたり、レビューでもまれる経験が少なければ、そんなキレイなコードをいきなり書くことは難しいでしょう。TDDを(自転車の練習で使った)補助輪として活用してください。

なお、ペアプログラミングをする時だけ、テストファーストで書くという人もいます。ペア同士がこれからやるタスクをハッキリさせる効果があるためです。

ちなみに「TDDは好みではない」のと「TDDすら実践する技能がない」は大きく違います。脚力がなければ、自転車をこげないのと同じで、基本的なコーディングスキルがなければ、何をしても、いつまでたっても苦しいものです。時には codewars.com などのプログラミング学習サイトでもくもくとお題に取り組み、コーディングスキルを地道に鍛えることも必要です。

「けど『TDD』で検索すると、否定的な意見も多いですよね?」

そうなんです。けれど、その根底にあるのは「品質を落とせば開発スピードが上がるのでは?」という幻想にある、と私は学びました。TDDの伝道師である@t_wadaさんのスライドからです。

「質とスピードはトレードオフの関係にある」は大きな誤解

質とスピード(2022春版、質疑応答用資料付き) / Quality and Speed 2022 Spring Edition - Speaker Deck speakerdeck.com

  • スピードを求めると、ソフトウェアの内部品質が犠牲になる。システムの中がどれだけ整理整頓されているか?の指標であり、外からは分からない。
  • 具体的には、保守性が犠牲になる(さらに保守性とは、テスト容易性、理解容易性、変更容易性のことを指す)。
  • 内部品質が悪化すると、すこし時間をおいてから、外部品質(ソフトウェアの外から分かる機能や使い勝手など)が悪化し、結果的にビジネスが失敗する。
  • 逆に開発スピードを落とし、時間的余裕を与えたとしても、上手くいかない場合がある。それは、エンジニアの技術力が低い場合。技術力が低ければ、内部品質を担保できない。
  • 正しい因果関係は「内部品質を高く保てば、リリースが早くなる」という事実。

では、どうやって内部品質を高く保てばいいのでしょう。同じく和田さんの講演「組織にテストを書く文化を根付かせる戦略と戦術」では、次のように述べています。

組織にテストを書く文化を根付かせる戦略と戦術(2020秋版) / Strategy and Tactics of Building Automated Testing Culture into Organization 2020 Autumn Edition - Speaker Deck

speakerdeck.com

  • いくらテストをしたところで、内部品質は良くならない。結局はエンジニアの技術力と、できあがるプロダクトコード次第。
  • とはいえ、テストがあることで品質の劣化に気が付くことができる。体重計を使うことでダイエットしやすくなるのと同じ。
  • だから、テストがある組織は、開発スピードを劣化させる前に、品質の劣化に気が付き対処することができる。
  • 逆に言うと「テストを書く時間がないのではなく、テストを書かないから時間がなくなるのです」

一般的には「質とスピードはトレードオフの関係にある」ことが信じられていますが、それが実は誤っているという事実はなかなか受け入れられないものです。さらに「エンジニアの技術レベルを高めなければ、開発スピードは上がらない」という身もふたもない現実も、同様に受け入れがたいものでしょう。

ただ、この事実を真摯に見つめ、精進する覚悟を決めるしかない、と私は思います。

以前、リファクタリングを通じて内部品質(保守性・変更容易性)を高めるコードの例を示したことがあります。この例は、内部品質を高めると、開発スピードが上がる例にもなっています。

style.biglobe.co.jp

リファクタリングにより内部品質を高めたコードの方が行数は多くなります。しかし、機能追加の際に読む必要のある行数をわずかながら抑えることができているのです。一般的に、コードを書く時間より読む時間の方が10倍多いと言われているので、このわずかの差が大きな効果を生み出します。

まとめ

新人エンジニア研修でもらった質問に、回答していく過程をご紹介しました。新人に「質の高いコードをスピーディーに書けるようになることを目指そう」「TDDのような道具を上手く活用して、自分の実力をつけていこう」と提案しました。もし、それさえもきついようなら、キーボードのタイピングスピードや、基礎的なコーディング力が不足しているかもしれません。codewars.com のような学習サイトで地道に鍛えていくことも必要です。

質問をしてくれた新人も、この回答に納得してくれた様子です。自動テストの重要性と、TDDの補助輪としての価値を分けて解説したこと、および、TDDを強要ではなく提案として示した点が好評でした。こうした質疑応答がきっかけとなり、研修をブラッシュアップできるのはとてもありがたいです。

新人エンジニア研修では「『分かる』と『できる』は違う」ことを常に気をつけています。よっぽどの天才でない限り、学んですぐに実践できるようにはなりません。特にコーディングに関しては、これまでの人生で書いてきたコードの量と、書いたコードに対するフィードバックの質が、大きく影響していると考えています。

時には地道な訓練も必要になることを忘れずに、Learn by doing で経験を積みながら、高品質なコードを書けるエンジニアを目指して一緒に成長していこうと思います。


  1. この質問を受けた当時は@t_wadaさんの雑誌記事「保守しやすく変化に強いソフトウェアを支える柱 自動テストとテスト駆動開発、その全体像」Software Design 2022年3月号|技術評論社がまだ世に出ていませんでした。自動テスト、テストファースト、TDDの関係性をとても分かりやすく整理した素晴らしい記事です。