読者です 読者をやめる 読者になる 読者になる

クラウドワークス エンジニアブログ

日本最大級のクラウドソーシング「クラウドワークス」の開発の裏側をお届けするエンジニアブログ

クラウドワークス勉強会「レガシーコード改善の戦略と戦術」(後篇:戦術&懇親会)

f:id:koichiroo:20160112053829j:plain

こんにちは!開発の所(@ctokoro_me)です。

クラウドワークス勉強会「レガシーコード改善の戦略と戦術」前篇(戦略)に続き、後篇(戦術&懇親会)をお送りします。

 

「レガシーコード改善の戦略と戦術」

講師:和田 卓人(@t_wada)

タワーズ・クエスト株式会社 取締役社長、プログラマ、テスト駆動開発者。

学生時代にソフトウェア工学を学び、オブジェクト指向分析/設計に傾倒。 その後様々な縁に導かれソフトウェアパターンやXP(eXtremeProgramming)を実践する人たちと出会い、後のテスト駆動開発の誕生を知る。 テスト駆動開発によって「完璧主義の呪い(完璧な設計を得るまではコードを書けないし良いシステムも出来ないという強迫観念)」から解かれてからは、文章や講演、ハンズオンイベント等を通じてテスト駆動開発の啓蒙に努めている。 今日もグリーンバンド(テスト駆動開発者の証)を左手に着け、テストと共にコードを書いている。 『プログラマが知るべき97のこと』『SQLアンチパターン』(オライリージャパン)監修。

戦術編

どこからやるか

テストが無いコードベースに対して、どこから手をつけていけばよいのでしょうか? いくつかの切り口があります。

  • 重要度の高さ順

    例えば金銭が絡むロジックや個人情報を扱う部分など、業務上の重要度が高い箇所から攻めていくやり方です。機能改修等を行う上で最も壊してはならないところをまず守るという点で優れた切り口であり、経営層/ビジネスサイドからの理解も得やすそうです。

  • 困っている順

    既に大きく複雑になってしまっているクラス/モジュール等からテストを書いていくやり方です。リファクタリングの欲求が強い箇所だと考えられるので、成果を感じやすいやり方だと思います。

  • 新規機能開発

    新しく追加される機能とその機能に関連する部分から固めていく、という方法です。既存の業務に統合しやすいという点で導入しやすい方法でしょう。

  • バグが出た順

    一見すると場当たり的に見えますが、複雑性が高い/品質が低いコードは往々にして固まっているので、実は投資対効果が高い方法です。

  • 静的コード解析

    静的コード解析^1ツールを用いて警告が多いクラスやスコアの低い箇所から攻めていく方法です。 ruby/railsであれば Rubocoprails_best_practices で計測したり、言語/フレームワークによっては商用のものを導入してもよいでしょう。

チーム/プロジェクトの状態に応じて、どの切り口から攻めるか決めていきましょう。

だれとやるか

一度に多くの人がテストを書くことは現実的に難しいでしょう。 書ける人を育てるのではなく自ら育つような方針を取りましょう。 例えば、ペアプログラミング^2を導入してテストを書く手法が自律的にチーム内に広がり、教えられる人が自然に増えていくようなやり方をしましょう。

また、導入の旗手として若手のホープに任せるのか・ベテランを投入するのかなど、人選も重要となるでしょう。

こだわるな

テストを導入していくことに際して、今まで出来ていなかったからこそ導入したい事がたくさんあるかもしれません。ですが、最初から全部をやろうとしてはいけません。

必ずしもテスト駆動開発(TDD)/テストファースト^3でなくても構いません。最初はテストの実行速度やカバレッジにこだわる必要もありません。 また、「ユニット」テスト^4であることにこだわりすぎるのもよくありません。ユニットの依存を追い出すためのStub/Mockの作成/メンテナンスコストが激増したりします。

こだわろう

ですが、テストを書く上で必ずこだわるべきポイントが2つだけあります。

  1. 再現性、繰り返し可能( Repeatable )

    環境への依存がなく、誰がいつ何度実行しても同じ結果になること。これが無いとテストの信頼性が担保できません。

  2. 独立していること ( Independent )

    テスト同士の依存性(≒順序性)がないこと。テスト間に依存性がある場合、テスト単体での実行ができません。さらに将来的にテスト実行速度向上のための並列実行も不可能になります

また、できればテストコードのDRY^5も意識できたら良いでしょう。テストのメンテナンスコストを下げることにつながります。^6

他の指標は、それからでも遅くありません。

「レガシーコード改善ガイド」

f:id:koichiroo:20160112054738j:plain

レガシーコード改善ガイド (Object Oriented SELECTION)

「何はなくとも読むべし」という和田さんオススメの一冊です。 この本は「レガシーコードとはテストがないコード」という定義を世界に与えた程の影響があった本です。

レガシーコード改善にいざ着手すると、「複雑なコード/仕様にテストが書けないからリファクタリングしてからテストを書きたい。しかし、テストがないとリファクタリングが難しい」というジレンマにぶつがりがちです。

この本では、そのように複雑化したレガシーコードの内側を切り出し、テストに書き出すための方法が詳しく解説されています。その中でも、下記2つを意識するとよいでしょう。

  1. 「仕様化テスト」(characterization test)

    「仕様化テスト」とはソフトウェアの現在の振る舞いを明らかにし、コードを変更しても振る舞いが変わらないことを確認するためのテストです。^7 仕様がわからないということは、何が正しい挙動がわからないこということです。

    こういう状況の場合、「どうあるべきか」は捨てて仕様化テストとして現在の挙動を写しとり、「現状と変わっていない」ことをテスト可能な状態にしましょう

  2. 「絞り込み点」(pinch point)を探す

    「絞り込み点」とは、影響スケッチ(※)における集約された部分に相当する、ある機能群をテストするための理想的な場所です。^7

    ※影響スケッチ(effect sketch) :ソフトウェアを変更した時に、影響を受ける変数やメソッドの戻り値を表現する手書きの小さな図。どこにテストを書くべきかを判断するときに役立つ。^7

    「絞り込み点」を探してテストを書くことで、効率的に幅広い変更を担保するテストを書くことができます。詳細は書籍をご参照ください。

設計の可動域を確保する

テストが無いということは、既に設計が悪い兆候です。 設計/実装を変えるためにテストを書くことが前提であり、実装自体のテストを書かないことが大切です。

粗い粒度のテストを書かないと、コード内での責務の移動(=設計の可動域)を担保できません。 テストがカバーする範囲に遊びを持たせ、カバー範囲内をリファクタリングしていきましょう。

インプット(リクエスト)・事前状態(DB等)・アウトプット(レスポンス)・事後状態(DB等)の4点を抑えたテストを拡充していくことで、カバー範囲の高い全体(連結)テストのカバレッジを上げることが出来ます。 この全体テストで外側から攻めていき、そのテストの内側にユニットテスト等を仕込んでいくのも有効なやり方です。 状況に応じて、このようなカバー範囲の広いE2Eテスト(Capybara, Appiumなど)も使いこなしていきましょう。

見える化

割れ窓理論^8が示すように、割れた窓(汚いコード)を放置するといずれ全ての窓が割れていきます。たとえ自分が書いたコードでなくても、汚いコードを放置しないようにしましょう。そのためにメトリクスを取って、数字化・定量化してする「見える化」を進めましょう。例えば、SimpleCovCoverallsを使ってテストカバレッジ率等を常に把握することができます。

ツールを使うとたくさんの警告が出たりしますがしきい値を調整したり、最初は一部の警告を無視する設定を加えるなど、うまく使いこなしていきましょう。また、メトリクスの分子/分母の数字(ex.カバレッジ率60%など)ではなく、時間を追って変化する数字の相対値に着目することも、モチベーションを保つためのコツです。

静的解析を使いこなす

普段プログラマの私達がテストと呼んでいるものの多くは動的テストです。動的とは、対象のコードを動かして検証を行う、ということです。動的テストとは異なり、対象のコードを動かさずに検証を行うテストが静的テストです。コードの行数や重複率、循環的複雑度等を計測してコードの質やバグ潜在リスクを検証します。レガシーコードの多くはそもそも動的テストができないような構造になっているので、対象を動かさずに検証する静的テストの重要度が増します。

静的解析には PMD, Rubocop, CoverityといったツールやCode Climate のようなサービスがあります。

コードベース全体に対して静的検証を行なって全体の質のばらつきや特に複雑なところを検出し、特に複雑な部分の改善にあたっては、さらにその部分に絞った静的検証を継続的に行い、時間を追った複雑度の変化等を追跡します。

コードレビュー

コードの品質を上げるために、コードレビューを行うためのインフラを整備していきましょう。

そのために GitHub, GitLab, GitBucket 等を導入したり、WIP PR^9 を取り入れることで、仲間のコードを見る/見せる文化を育てましょう。

シンプルさ

シンプルさは信頼性の前提である

by Edsger Wybe Dijkstra 計算機科学者・構造化プログラミング提唱者

Edsger Wybe Dijkstra

「シンプルさ」を貴びましょう。迷ったらシンプルな方を選ぶべきです。 「simple と easy は異なる」(Ricky Hickyの講演「シンプルさの必要性」より)ということも抑えておくとよりベターです。

「シンプルさ」は、UNIX思想にも垣間見ることができます。

モジュール化の原則

"ぶざまな姿をさらさずに複雑なソフトウェアを書く唯一の方法は、全体としての複雑さの度合いを下げることだ"

"つまり、適切に定義されたインターフェイスで結び付けられた単純な部品からシステムを作り上げるのだ。 こうすれば、ほとんどの問題は局所化されるし、全体を壊さずに部品だけを改良することも不可能ではなくなる。"

--『The Art of UNIX Programming』より

シンプルなものを組み合わせて複雑性に対処すべきです。 たとえばシェルスクリプトのように、移植性の高いモジュール化された部品(コマンド)を組み合わせて、複雑な処理を実現する方針を取り入れましょう。

参考までに、UNIX思想における原則を引用しておきます。

モジュール化の原則 : クリーンなインターフェイスで結合される単純な部品を作れ。

明確性の原則 : 巧妙になるより明確であれ。

組み立て部品の原則 : 他のプログラムと組み合わせられるように作れ。

分離の原則 : メカニズムからポリシーを切り離せ。エンジンからインターフェイスを切り離せ。

単純性の原則 : 単純になるように設計せよ。複雑な部分を追加するのは、どうしても必要なときだけに制限せよ。

倹約の原則 : 他のものでは代えられないことが明確に実証されない限り、大きなプログラムを書くな。

透明性の原則 : デバッグや調査が簡単になるように、わかりやすさを目指して設計せよ。

安定性の原則 : 安定性は、透明性と単純性から生まれる。

-- 『UNIXという考え方―その設計思想と哲学』より

エレガント

シンプルであることは「美しさ」にもつながります。

"Elegance is a combination of power and simplicity."

エレガンスは、力と単純性が結合して生まれる。

"Elegant code is not only correct but visibly, transparently correct."

エレガントなコードは、ただ正しいだけではなく目に見える透明な形で正しい。

--『The Art of UNIX Programming』より

 

ロジックも見た目も複雑なコードを、シンプルで美しいコードに置き換えていきましょう。

Keep It Short and Simple

-- KISSの原則

ボーイスカウトルール

一つ一つは小さくても、積み重なれば信じられないほど大きくなります。 日々の改善を促すために「ボーイスカウトルール」を適用してみましょう。

"モジュールをチェックインする際には、必ずチェックアウト時よりも美しくする"

by Robert C. Martin - アジャイルソフトウェア開発者 -

f:id:koichiroo:20160112053944j:plain

つまり「 fetch した時より push は綺麗にする」のです。

背中を見せる

やったことがないことに取り掛かるのは誰でも大変です。 なので、サンプルコード/チュートリアル/デモを用意し、最初はコピペでもいいので真似してもらう土台を作りましょう。

大事なのは「テストのある生活を体験してもらう」こと。 その次にテストのメンテナンスを学んでいきましょう。

最後に "Social Change starts with YOU"

"できるからやるのではない やるからできるようになる

あなたが書けるようにならなければ、誰も書けるようにはならない"

by Takuto Wada -テスト駆動開発の伝導師-

f:id:koichiroo:20160112054008j:plain

 

まとめ

和田さんによる「レガシーコード改善の戦略と戦術」講演は以上になります。 前篇(戦略)でどのように考えながら導入すべきなのかに続いて、後篇(戦術)では具体的な方法や重視すべき指針が示されました。

講演後の質疑応答でも、和田さんと弊社エンジニアとのより具体的で活発なやりとりが続き、わずか2時間程でしたが大変内容が濃い勉強会となりました。和田さん、本当にありがとうございました!!

後篇(戦術)をまとめると、

どこからテストを書き始めるかについていくつかの切り口がありますが、チーム/プロジェクトによって適切なものを決めていきましょう。皆で一斉に始めるのではなく、ペアプログラミング等を通じて一人ずつ・自ら育つような方針で進めましょう。

テストを書き進めるにあたりTDDやカバレッジなどに最初からこだわる必要はありません。ですが、テストの「再現性」と「独立性」だけはこだわって進めましょう。

実際に複雑なコードの内側に手を入れるために「レガシーコード改善ガイド」に書かれている「仕様化テスト」と「絞り込み点」に対するテストを書くことをおすすめします。また、外側の全体(結合)テストを書き進めた後に、内側のユニットテストを攻めていくいく方法もあります。設計の可動域の確保も意識し、必要に応じてE2Eテストも取り入れていきましょう。

コードの「見える化」を進めるためにメトリクスを取り、静的解析でコードの質を検証・変化を追跡し、コードレビューを取り入れてコードの品質を上げていきましょう。

既に複雑になっているコードにモジュール化の原則を適用し、シンプルなものを組み合わせて複雑性に対処しましょう。それは「エレガント」なコードにもつながります。

サンプルコードやデモを用意し、まずは真似してもらって「テストのある生活を体験して」もらいましょう。ボーイスカウトルールも取り込んで、日々の改善も促していきましょう。

"あなたが書けるようにならなければ、誰も書けるようになりません。"

まずあなたからテストを書いて、レガシーコード改善を始めていきましょう!

   

本講演のスライドと登場した書籍を、こちらでまとめてご紹介いたします。

和田さんとおいしい懇親会

貴重なご講演を頂いた和田さんへ御礼と交流を兼ねて、弊社エンジニア主催で懇親会を開催いたしました。

スパークリングワインで「乾杯」!

f:id:koichiroo:20160112054036j:plain

f:id:koichiroo:20160112054100j:plain

f:id:koichiroo:20160112054123j:plain

f:id:koichiroo:20160112054148j:plain

     

美味しいワインと料理と共に、話も箸も弾みます。

f:id:koichiroo:20160112054235j:plain

f:id:koichiroo:20160112054257j:plain

f:id:koichiroo:20160112054208j:plain

f:id:koichiroo:20160112054318j:plain

     

昔からの友人同士である弊社CTO大場と和田さんとの一枚です!

f:id:koichiroo:20160112054342j:plain

f:id:koichiroo:20160112054408j:plain

f:id:koichiroo:20160112054433j:plain

f:id:koichiroo:20160112054455j:plain

f:id:koichiroo:20160112054524j:plain

   

※今回の懇親会ではこちらのレストランを利用させていただきました。 美味しい料理とワインを堪能させていただき、この場を借りてエンジニア一同より感謝の意を述べさせていただきますm(__)m

八百屋カフェ SHIBUYA (やおやかふぇ) - 渋谷/バル・バール [食べログ] 八百屋カフェSHIBUYA

     

クラウドワークスでは内容の濃い勉強会が開催されるだけでなく、オシャレな会食にお出かけしたりもしています! 興味をもったエンジニアやデザイナーは、こちらでお待ちしています!

© 2016 CrowdWorks, Inc., All rights reserved.