やっとむでぽん

 

2016-10-11

初級プログラマくらいの人向けに「プログラミングは論理的思考が必要っていうけど、なんぞそれ」みたいな、説明に使うための文章を書いてみています。

一歩ずつ進める

あなたが解きたい問題には、簡単なものもあれば難しいものもあります。

単純なものもあるし複雑なものもあります。とても簡単でとても単純な問題だったら、一発で解決できるかもしれません。しかしもう少し難しかったりもう少し複雑だったりすると、一発では解決できなくなってきます。こうした問題は少しずつ、一手ずつ解いていくことになります。

大きくて複雑な問題を一息で解決できれば、カッコいいかもしれません。

しかしそのやり方にはリスクがあります。一息でやっつけようとして、それが上手くいかないと、振り出しに戻ってしまい一歩も先へ進めません。ことによっては、最初よりも状況が悪化してしまうかもしれません。カッコよさよりも、問題解決という目的を大切にして、確実で効果のある進め方が必要です。小さな問題でも大きな問題でも、自分に合った歩幅で一歩ずつ進めましょう。ひとくちサイズずつ食べるという言い方もします。

一歩ずつ進めるアプローチにはメリットがたくさんあります。一歩で考えるのは小さな問題、元の問題から切り出した一部分になります。そうすると同時に考えないといけないことが減って、比較的簡単に解決できるようになります。問題はわかりやすく、解決方法もシンプルで、善し悪しも客観的に評価しやすくなります。解決方法が正しいかどうか、判断も容易です。考慮漏れや見落としが入り込む可能性も小さくなります。さらにより良い解決方法を求めて、方法自体を洗練させたり、他の案を検討したりもできます。

例題:
  • 皿洗い問題 汚れたお皿が10枚あります。洗って拭いて食器棚にしまわないといけません。
    • どういうやりかたをしますか?
    • 100枚だったらどうですか? 10000枚だったら?

自分の歩幅

一歩ずつ進めるといっても、どんなふうに一歩を決めればいいでしょうか。Webアプリの開発を例にすると、たとえば次のように6つに区切るという手があります(あくまで一例です)。

  1. 画面を実装して入力できるようにする
  2. 入力したデータをどう保持するか設計する
  3. 入力と既存のデータを組み合わせて保存する実装をする
  4. ユーザーが使いやすいよう画面を修正する
  5. 全体が矛盾なく整合しているかテストする
  6. 実行時間が遅くなったりしないか確認する

1ステップごとに、プログラムが少しずつ増え、完成に近づいていきます。2番は設計を検討するだけですし、5番や6番はテストやレビューが中心で、コードは書かないようなステップもあります。

こうした「一歩の大きさ」は様々です。ベテランならば1番から6番までまとめて一歩で片付けてしまうかもしれません。新人だったら、もっと細かく分けないとうまく進めないかもしれません。あるいはベテランでも、1画面に300項目あるような複雑な機能なら、念入りに小さな一歩で進めるでしょう。Rubyならまとめてできる人でも、C++では設計だけ先に考えるということもあります。問題の性質、人の経験やスキル、解決に使う道具やテクノロジーによって、妥当な一歩の大きさが変わってきます。自分が自信を持てる、これなら踏み外したり、うっかり間違えたりしないと思えるような一歩を、そのときどきで選んでください。

プログラミングの世界ではよく、設計・実装・テストという作業をしますが、こうした「作業の分解」も一歩ずつ進める一つの方法です。

  • 設計: 対象の問題全体を解決する方法を決める
  • 実装: 設計で決めた方法を具体化する
  • テスト: 問題が解決したか確かめる
例題:
  • 普通の一歩問題 普通に歩いているときの、一歩の大きさを測ってください。そのうえで、「自分が自信を持てる」一歩の大きさは、どのくらいだと思いますか?
    • 平坦な道路では?急な山道だったら?ツルツルに凍った氷の上なら?
    • 裸足のとき、履き慣れた靴のとき、ビーチサンダル、下駄、竹馬ではどうでしょう?
    • 空中100メートルで綱渡りをするとしたら、一歩の大きさはどのくらいですか?一歩でも進めますか?
  • 間違えない問題 プログラムを書くとき、何文字、あるいは何行までなら絶対に一箇所も間違えないで書けますか?
    • 確かめてください。
    • 自分で自信があると感じる度合いと、実際に間違えてしまう割合は、合っていますか?

前に進まない一歩

一歩ずつ着実に確実に前に進むという考え方ではあるのですが、前に進まない一歩というものもあります。進めていくうちに、わからないところが見つかることがあります。最初の想定と違う箇所が出てくることもあるし、今までやったことが間違っていたとわかることもあります。単に、自信が持てなくなることだってあります。そうしたときは、前進するのをいったんやめて、考え直さないといけません。状況を再確認したり、方針を見直したり、情報を収集したりするのも、前に進まない感じがありますがこれもまた一歩です。

大きな問題を少しずつ解決するのは、長い道のりを一歩ずつ進むようなものです。一歩ずつ進んでいると、先行きがわからなくなったり、見失ったりしやすくなります。全体として問題解決に近づいているかどうか、ときどき周りを見渡し、行く手を確認したほうがよいでしょう。本当に前進しているか、解決に近づいているか、事実の確認と仮説の検証をしながら進んでください。道を間違えていると困るので、ときどき行く手を探索して情報収集しましょう。むしろ、一歩ずつ進んでいるからこそ、問題点や不明点に気づくことができます。

コラム 前に進まない楽しい仕事

プログラミングというのは楽しいものです。 楽しいことをしていると、つい時間を忘れて没頭しがちです。 やっていることが面白くなって、もともとの目的を忘れてしまうことも、またありがちです。 コードの美しさにこだわったり、アルゴリズムのチューニングに熱中したり、モデリング中毒になったり、アニメーションの気持ちよさを追求したりと、ハマりどころはたくさんあります。

そういう点ももちろん大事なので、かけるべき時間はかけましょう。しかし今やらないといけないのか、どれくらい時間をかけるべきなのか、意識して確認しましょう。先にやるべきことは先にやる、後に回せることは後にするのが大事です。

いま、なんのために、どんな一歩を踏んでいるところなのかを忘れずに。

解としての一歩ずつ

そもそもプログラミングとは、問題を解くために、コードを1行ずつ書くことです。プログラムはまさしく、一歩ずつの積み重ねでできているわけです。アルゴリズムを勉強していると、解説はたいてい、1ステップずつの処理に分けて書いてあります。分割統治との組み合わせで、様々なアルゴリズムにおいて、扱いやすい一部分だけを取り出して処理するというアプローチが利用されています。問題解決の方法と進め方はどちらも、一歩ずつ進めるのと相性がいいのです。

一歩ずつのアプローチは、後から他の人が見たときも理解しやすくなります。理解するほうも、やはり一歩ずつ理解を進められるためです。一歩ずつの考え方を反映したプログラムやシステムの構造も、同様です。

例題
  • 問題分解問題 スマホアプリに入力したデータをサーバで集計して、運用者がGoogle Spreadsheetで見られるようにしたいとします。
    • まずこの問題を3つの問題に分解してください。
    • 3つに分けたそれぞれを、さらに小さな問題に分解してみてください。ここでも一歩ずつ進めましょう。一気にうんと小さくしようとせず、2つか3つくらいずつ、段階を追って分解してください。

https://github.com/yattom/text_for_programmer/wiki/%E4%B8%80%E6%AD%A9%E3%81%9A%E3%81%A4%E9%80%B2%E3%82%81%E3%82%8B

2016-10-06

初級プログラマくらいの人向けに「プログラミングは論理的思考が必要っていうけど、なんぞそれ」みたいな、説明に使うための文章を書いてみています。

抽象化

問題を解決するには、まず問題を把握しなくてはなりません。 しかし一歩ずつ進めるうえでの最初の一歩から悩むこともあります。 たくさんの要素を同時に考えないといけなかったり、多くの事柄が複雑に関連していて、小さな部分に切り出すのが難しいことがあります。 分割統治するための分割が、難しいわけです。 いちどに把握して検討できるものの数には限度があって、それ以上になるとまとめて考えることができなくなり、見落としや考慮漏れが発生します。

例として、週末にパーティーを開くために買い物をすることになったと考えてみましょう。 ほしいものを考えながら買い物リストを作っていたら、ものすごく長くなってしまいました。 全部で100個はありそうです。 近所のスーパーで買えるものもあれば、駅前の商店街で買いたいもの、デパートまで行かないといけないものもあります。 これを整理しないで買い物に出かけたら、スーパーと商店街を何回も往復したり、せっかくデパートまで行っても買い忘れをしたりしそうです。

そこで、買い物リストを分割することにします。 どこで買うかによって、リストを3つに分けます。 これで、スーパーの買い物リスト、商店街の買い物リスト、デパートの買い物リストの3つのリストができました。 これで買い物を無駄なく、忘れ物もなくできそうです。

いまの例では、100個以上の項目がある「買い物リスト」を、「どこで買うか」という観点で整理して3つの買い物リストにしました。 整理する前の問題は「リストを見ながらどこで買うか考えながら買い物をして回る」というもので、移動と買い物を同時に考える必要がありました。 100個以上も頭の中だけで整理していたら、移動の無駄が起きたり、買い忘れが起きてしまいます。 整理して3つの買い物リストに分けたおかげで、今度は問題も分割できました。 まず「買い物する場所に行く」という問題、そして「その場所で買い物をする」という問題の2つです。

「買い物リスト」を「どこで買うか」という観点で見直す、これが抽象化の一例です。 大きく複雑なひとかたまりの問題を、観点によって整理し、簡単に解けるいくつかの問題に分割するのです。 細かい点を見ないようにしたり、差異を無視して同じものと扱ったり、似た点があるものをグループ化したりするのが、抽象化です。

「抽象的」は「具体的」の反対の意味です。 抽象化とは、具体的の反対にすることなので、必ず情報が失われます。 このことから「抽象」は「捨象」とも言います。 情報を捨てるという意味です。 いま着目したい、重要視したいところだけを取り出し、それ以外の情報は無視して考える。 それが抽象化です。

例題:
  • 最寄り駅から家までの地図を書いてください。書き終わったら地図を観察して、どんな情報が載っているか、どんな情報は捨てたのか書き出してください。なぜその情報を選んだのか、考えてみてください。
  • 会社で泊まりがけの合宿をすることになり、宿泊場所の候補を探してほしいと頼まれてしまいました。いくつかの宿泊施設を選んで、それぞれの特徴や情報を整理して一覧表にしないといけません。どういう情報を載せますか?
    • 家族や仲間との遊びの旅行だったら、載せる情報は変わりますか?それはなぜですか?
  • ブラウザで適当なWebページを開いてください。そのページの構成や内容について、抽象化して説明してください。

抽象化のレベル

整理した3つのリストを、3枚のメモ用紙に分けて書いて、机の上に置いてみましょう。 ここからちょっと離れてみてください。 2メートルくらい離れると(すごく目のいい人なら5メートルくらい)、もうリストになにが書いてあるかはわからなくなるはずです。 それでもリストが3つあることはわかります。 左がスーパー、真ん中が商店街、右がデパートで買うもののリストです。 これくらい離れると、買うもの個々のことは気にせず、どこに買い物に行くかだけ考えられます。 デパート→商店街→スーパーの順に回るか? 商店街とスーパーは仕事帰りに寄って、デパートは土曜日にするか?

行く場所が決まったら、また机のところに戻りましょう。 そしてこれから最初に行く場所のメモを1枚取って、目の前に持ってきてください。 何を買うのかよく見えるようになったので、買い物の内容をチェックできます。 飲み物は3本で足りるか?料理の材料に忘れ物はないか?花を飾ろうか、いらないだろうか? このとき、他のリストは見えなくなっています。 一箇所の買い物だけに集中できて、その後どこへ行くのか気にしなくてすみます。

物事を抽象化すると、それぞれ異なる抽象化のレベルが見つかります。 この例では「リストを選ぶレベル」と「買い物で使うレベル」の2つできました。 それぞれのレベルでは、それぞれの問題に集中できます。 そしてあるレベルで考えたり判断したことは、他のレベルに影響しません。 いまは「スーパー」「商店街」「デパート」の3つに整理していますが、たとえば商店街のどの店で買うのか、デパートのどのフロアに行くのかを考えたくなるかもしれません そうすればレベルが増えて、商店街を回る順番や、デパートで寄るフロアを考えることもできるようになります。

具体的に近い方を「低レベル」、抽象度の高い方を「高レベル」と呼ぶこともあります。 これは誰かをほめたり、けなしたりしているわけではなく、単に抽象化の度合いを表している言葉です。 高い低いを、空を飛ぶ鳥や飛行機のような高度でイメージしてみてください。 地面に近い、低いところでは、いろいろなものが詳細に、具体的に見えます。 地面から遠く、高い高度まで上がっていくと、広い範囲が見渡せるようになります。 低レベルでは詳細度が上がりますが視野が狭まり、高レベルでは詳細は見えませんが視野が広くなります。 どちらが大事とか、偉いとかいう話ではありません。 それぞれのレベルで見えるものが違い、考えられることも、解決できることも違うという考え方です。

問題のどの部分を、どのレベルから解決していくのか意識すると、問題全体にどうアプローチしていくのか作戦が立てられるようになります。 そうすると抽象レベルを高く上がったり低く下がったり、行ったり来たりしながら問題に取り組むことになります。 自分がいまどのレベルで考えているのか、そこでどういう問題を解こうとしているのか、忘れないようにしましょう。

例題:
  • Googleマップで自宅周辺の地図を表示して、拡大・縮小してみましょう。4つくらいのレベルを決めて、それぞれ比較してください。(スクリーンショットを保存して見比べるとよいです。)
    • それぞれのレベルでは、何が見え、何が見えないですか?どんなことがわかりますか?
    • それぞれのレベルの地図は、どんな使い道に向くと思いますか?
    • 同じことを、衛星写真(Earth)でやったり、交通状況を切り替えたりして、比較してください。
  • ふだん使っているソフトウェアやアプリについて、4つの抽象レベルで、それぞれ100文字程度で説明してください。
    1. 今日(または最後に使ったとき)の具体的なやったこと、使い方
    2. 自分がなんのためにそのソフトウェアを使うのか、その目的
    3. 類似するソフトウェアと比較した特徴
    4. 90歳のおばあちゃん(または3歳の子供)にそのソフトウェアを説明する
    • それぞれの説明の違いはなにか、なぜそのように変えたのか、整理してください
    • 他にどんな抽象レベルがあるか、考えてみてください
      • 例: そのソフトウェアの開発者なら?その会社の社長なら?株式アナリストなら?ライバルなら?

観点が重要

ここまでの例や例題で見てきたように、抽象化はソフトウェア開発に限らず、あらゆる場面で使われています。 もちろんソフトウェア開発やシステム開発でも、問題解決のためさまざまな抽象化がおこなわれています。 システムは一般的に複雑なものなので、抽象化も様々な観点が必要となります。 設計書、仕様書などのドキュメントはほとんどが、なんらかの観点からシステムを抽象化して記述したものです。 (なおドキュメントの呼称や内容は、会社や現場、人によって大きく違うので、知っているのと違うと思うかもしれません。 あくまで一例です。)

  • ワイヤーフレーム
    • 観点は画面の種類や数と、表示する主要な情報、遷移の流れなど
  • フローチャート
    • 一連の処理がどこから始まりどこで終わるか、どう流れていくかという観点
  • 詳細仕様書
    • 対象としている範囲に限った、プログラミングに必要な情報をすべて含む観点
  • システム概要
    • システムに登場するもの最大粒度で表現する観点で、システムの境界を定め、対象と対象外を区別するのに使う

このように観点しだいで抽象化の結果も目的も変わってきます。 どういう観点を選べばいいのか、どういう問題にはどういう観点を使えばいいのか、これは知識と経験が必要になってきます。 また観点の選び方が悪いと、問題解決の役に立たないどころか、かえって事態を混乱させてしまうこともあります。

買い物リストの話で考えてみましょう。 どこで買うかという観点でリストを整理し、3つのリストを作って、「どこに行くか」と「その場所で何を買うか」を分離して考えられるようにしました。 ところで、もし花を買うとしたら、どこで買うといいでしょうか? 花はデパートでも買えるし、商店街にも花屋がありそうですし、スーパーでも売っています。 花を実際に見て値段も確認してから買おうと思ったら、どのリストに入れておくといいでしょうか。 もし3箇所見比べたかったら、「どこに行くか」を考えるとき考慮しないといけなくなります。 こうなると、せっかくリストを整理したのに、「花をどこで買うか」という新たな問題が発生してしまいます。

また別の観点もあるかもしれません。 たとえば、予算が決まっていて、ほしいものは全部買えないかもしれません。 リストが1つだけだったら、リストの中身を必要な順に並べ替えて、予算がなくなるまで上から順に買えばいいでしょう。 しかしリストが3つに分かれていると、どのリストをどこまで買うべきなのかわからなくなってしまいます。 全体の予算があると、「どこで買うか」という観点の抽象化は不適切なわけです。 先に予算の問題を解決しないと、適用できない観点だということになります。

観点と抽象化レベルによって、もともとの大きな問題を分割してそれぞれ個別に解いていけます。 しかし、分離しきれない問題が残ることもあって、これはレベルをまたいで考える必要があります。 観点が間違っていて、問題が複雑化してしまうこともあります。 もともとの問題を全部理解して把握できていれば、あとからこんなことに気づいたりしないですむかもしれません。 しかしそもそも、問題は複雑で理解も把握も難しいから、いろいろ工夫をしているわけです。 「考えればわかること」は「考えたからわかる」ので、最初からわかっていれば苦労しません。

そこで、抽象化がうまく働いているか、問題がきれいに分割できているか、進めながら確かめる必要が出てきます。 高レベルで問題を解決したら、それが低レベルでもちゃんと解決できているか調べます。 また、他のレベルに不要な影響を与えていないかも、要確認です。 こうした確認を通じて、設定した観点と抽象レベルが妥当なものだったかわかってくるのです。 抽象レベルを自由自在に行き来しながら問題を解決し検証するというのは、とても高度なスキルですが、これができるようになるとプログラマとしての問題解決能力が大きく広がります。


コラム: アーキテクトも実装する

ソフトウェア開発ではよく「上流の設計が、下流で矛盾を生む」という事態が起きます。 その理由のひとつが、こうした抽象化の誤りです。(理由は他にもたくさんありますが。) 上流SEや、アーキテクト、プロジェクトリーダーといった立場の人が、高レベルのことだけ考えて低レベルでの確認を怠ると、実装時に設計を考え直さないといけなくなったり、実装できない設計になったりしてしまいます。

『組織パターン』には「アーキテクトも実装する」というパターンがあり、全体の方向性を決定するアーキテクトもプログラマーと一緒にコードを書くことの重要性を説いています。 また『人月の神話』の「第3章 外科手術チーム」では、生産性の非常に高い10名の開発チームには「執刀医」に当たるチーフプログラマーがいて、「自ら機能と性能を定義し、プログラムをデザインし、コーディングし、テストして文書を書く」と述べています。 ソフトウェア開発において実装というのはかなり具体的な活動、低レベルなものですが、ここまで下りてこないと本当には確認できない問題というのがたくさんあるのです。 (ちなみに、さらに低く、OSやメモリ、通信プロトコルや物理マシンといったレベルまで下がっていくこともできます。)

例題:
  • 何人かのグループを男女に分けるという場面をよく目にすると思います。どんな状況があるか、男女に分けることでどんな問題を解決しようとしているのか、いくつか書き出してください。
    • 男女に分けるという問題解決が、どういった観点からもたらされたのか、考えてください。
    • 男女に分けたせいで問題が起きることもあります。どんな観点だと、かえって状況が悪化すると思いますか。
  • いま利用しているプログラミング言語を、他の言語と比較しながら、なぜ自分がいまそちらを使っているのか説明してください。できれば他の言語は2つ以上選んでください。

https://github.com/yattom/text_for_programmer/wiki/%E6%8A%BD%E8%B1%A1%E5%8C%96

2016-03-28

C言語でTDDを学ぶ?TDDでC言語を学ぶ?

Facebook上で議論になったのですが、C言語でTDDを勉強していると、「よりよい設計」になりにくいという話になりました。

例として整数の区間の最初のほうの課題を考えてみます。

課題1-1

下端点と上端点を与えて閉区間を生成しよう

閉区間から下端点と上端点を取得しよう

TDDで実装したコードとテスト

    // プロダクションコード
    int upper_point;
    int lower_point;
    
    // テストコード
    TEST(Range, Endpoint) {
        lower_point = 3;
        upper_point = 8;
        EXPECT_EQ(3, lower_point);
        EXPECT_EQ(8, upper_point);
    }

問題:どうしたら関数を導入できるでしょうか?

元のお題はオブジェクト指向を想定しているところがあって、「生成しよう」でオブジェクトを作り、「取得しよう」でオブジェクトから値を取り出すという形になるんですが、C言語では「値を与えて取り出す」のならば、変数があればいいよねと。とりあえずグローバルでも構わない、というのは、お題(要求仕様)に何も書いてないから。(externがないのでモジュール内に閉じていますけれど。逆に、プロダクションコードとテストコードを別モジュールにしたら、グローバルになってしまいますね。)

TDDのやり方に律儀に従うなら、新たなテストによって現在の(グローバル変数だけで生成は関数不要という)設計の限界が明らかになり、よりよい、シンプルな設計が導かれます。区間のお題の続きに「閉区間が別の閉区間と等しいか」があるので、複数の区間を扱うにはグローバル変数では困るよね……と思いきや、こうなります。

    // プロダクションコード
    int upper_point;
    int lower_point;
    
    int range_equal(int lower_point2, int upper_point2) {
        return lower_point == lower_point2 && upper_point == upper_point2;
    }

    // テストコード
    TEST(Range, Endpoint) { /* 略 */ }

    TEST(Range, Equal) {
        lower_point = 3;
        upper_point = 8;
        EXPECT_TRUE(range_equal(3, 8)); // [3,8]と[3,8]は等しいはず
        EXPECT_FALSE(range_equal(1, 9)); // [3,8]と[1,9]は等しくないはず
    }

うん、グローバルに設定したものとパラメータで渡すものを比較すればいいよね……間違ってはいない。

TDDでシンプルな解を追求する

TDDには(ひいてはXPには)「シンプルさ」という価値があります。TDDではシンプルな解をよしとします。ここには「(与えられた問題や制約をすべて充足するなかでもっとも)シンプル」という条件が隠れています。テストコードを眺めて、「比較対象を片方は変数に、片方はパラメータで渡すのは、シンプルでない」と思えば、以下のように書き直せます。

    // プロダクションコード
    int upper_point;
    int lower_point;
    
    int range_equal(int lower_point1, int upper_point1,
                    int lower_point2, int upper_point2) {
        upper_point = upper_point1;
        lower_point = lower_point1;
        return lower_point == lower_point2 && upper_point == upper_point2;
    }
    
    // テストコード
    TEST(Range, Endpoint) { /* 略 */ }
    
    TEST(Range, Equal) {
        EXPECT_TRUE(range_equal(3, 8, 3, 8)); // [3,8]と[3,8]は等しいはず
        EXPECT_FALSE(range_equal(3, 8, 1, 9)); // [3,8]と[1,9]は等しくないはず
    }

しぶといな、グローバル変数。

TDDが自動的に良い設計を保証するわけではありません。「TDDを使ってよりよい設計に到達する」のと「TDDを使えばよりよい設計になる」の間には、深い深い溝があります。TDDを使うと設計品質が悪くなるという調査もあります。少なくとも、何が「良い設計」であるか、言語やライブラリやフレームワークのどんな特性を利用すれば「良い設計」になるか、知識がなければ設計として実現できません。

元々のお題にはありませんが、このようなグローバル変数を使った設計に対しては、別の課題を設定してあげるという手を思いつきました。グローバル変数を避けるべき理由として「いつの間にか値が書き換わって危険」という知識があります。range_equal()を呼ぶと書き換わってしまうので、自明でない挙動をする。そこで「比較しても値が書き換わらないようにする」という課題が考えられます。それをテストで表現すれば、TDDの流れで設計を改善できます。

    // テストコード ※失敗する
    TEST(Range, Equal_DoesNotChange) {
        lower_point = 3;
        upper_point = 8;
        range_equal(0, 5, 3, 8);
        EXPECT_EQ(3, lower_point);  // range_equal()を呼んでも変化しないはず
        EXPECT_EQ(8, upper_point);
    }

このテストは失敗するので、グリーンになるように実装を直します。目指すのはグローバル変数の除去ですが、そうすると実装を直す過程で上端・下端を生成したり取得したりというテストが、成立しなくなってしまいます。おまけに今書いたばかりのテストも、中身がなくなってしまいます(EXPECT_EQで比較するものがなくなってしまう)。

    // プロダクションコード
    int range_equal(int lower_point1, int upper_point1,
                    int lower_point2, int upper_point2) {
        return lower_point1 == lower_point2 && upper_point1 == upper_point2;
    }
    
    TEST(Range, Endpoint) {
        // なくなってしまった
    }
    
    TEST(Range, Equal) {
        EXPECT_TRUE(range_equal(3, 8, 3, 8)); // [3,8]と[3,8]は等しい
        EXPECT_FALSE(range_equal(3, 8, 1, 9)); // [3,8]と[1,9]は等しくない
    }
    
    TEST(Range, Equal_DoesNotChange) {
        // なくなってしまった
    }

なくなってしまったテストは、消してしまうのがいいでしょう。万が一あとで復活させたくなっても、一度書いたことはきっと憶えているはずです(それにバージョン管理してますよね?)

よい設計を目指すTDD

さて、話を前に戻して、「比較しても変化しないこと」という課題は、グローバル変数の除去を意図して導入しました。なぜグローバル変数を除去したかったのか?不用意にグローバル変数を用いるのは「悪い」設計で、しかしTDDのサイクルの中でグローバル変数を止める自明な方法が見つからなかったためです。ここではいくつかの議論が考えられます。Facebook上の組み込みTDD勉強会(クローズドグループ)での議論を踏まえています。みなさんありがとうございます。

  • グローバル変数の利用のような初歩的な設計上の問題は、設計判断を持ち出すまでもなく常識的に取り除く(最初から使わない)べきだ。そうしたレベルならコーディング標準で制限できるし、そういうレベルのプログラマはしっかり教育したりレビューで改善すべきだ。
  • このタイミングで無理にグローバル変数を除去する根拠はない。お題を進めて仕様を拡充していけば、自然にグローバル変数を止めたくなるときが来るはず。そのときにこそ、最適な設計判断ができるのであって、いま強引に変えるのは拙速で、YAGNIでムダだ。
  • 特にC言語ではデータ構造の自由度が(純粋なオブジェクト指向プログラミングに比べると)高く、初期に十分検討しないと後でリファクタリングするのに苦労する。TDDでもC言語を使うときは事前設計をもっと念入りにやったほうがいい。
  • 「シンプルにする」というTDDの方針だけでは、必ずしもよい設計に到達できない。十分な知識と広い視点で、より望ましい設計を考え、そちらに向かって(やや強引であっても)TDDで進んでいけばいい。

いずれの言い分も妥当ですし、環境や状況や人によって重視すべき事項は変わってきます。私自身は最後の考え方がわりと好きで、よりよい設計を仮説として(できればテストに書き)、TDDで実証するというアプローチを選ぶことが多いです。

グローバル変数の話は、これでいったん片付いたことにして、データ構造を整理することを考えてみます。上端と下端は常にペアで使うものなので、ひとまとめにしたいですね。方法はいくつか考えつきます。

  • 構造体を作る
  • 長さ2の配列を作る
  • 1個のINT32の上位16bitと下位16bitに格納する
  • など

どれが望ましいでしょうか。C言語の適用範囲を考えると。いずれも妥当な解法になり得ますが、ここでも「シンプルに」、つまり「与えられた仕様を満たす範囲でできるだけシンプルに」考えたいです。メモリや処理時間の制約がなければ、構造体が素直でしょうか。

    // プロダクションコード
    struct range {
        int lower_point;
        int upper_point;
    };
    
    int range_equal(struct range range1, struct range range2) {
        return range1.lower_point == range2.lower_point && range1.upper_point == range2.upper_point;
    }
    
    TEST(Range, Endpoint) {
        // 復活!
        struct range range1;
        range1.lower_point = 3;
        range1.upper_point = 8;
        EXPECT_EQ(3, range1.lower_point);
        EXPECT_EQ(8, range2.upper_point);
    }
    
    TEST(Range, Equal) {
        struct range range1;
        range1.lower_point = 3;
        range1.upper_point = 8;
        struct range range2;
        range2.lower_point = 3;
        range2.upper_point = 8;
        EXPECT_TRUE(range_equal(range1, range2)); // [3,8]と[3,8]は等しい
        range2.lower_point = 1;
        range2.upper_point = 9;
        EXPECT_FALSE(range_equal(range1, range2)); // [3,8]と[1,9]は等しくない
    }

構造体を導入したコードを見ると、どうもテストコードが煩雑で、これなら前のほうが良かったように見えますね。rangeを簡潔に生成する方法を導入すれば改善できそうですが、本稿では省略します。

知らない解法を使えるか

また話を少し戻して、構造体導入の判断を考えてみます。構造体を使おうと思うには、少なくとも構造体を知らないといけない。カジュアルにグローバル変数を使っちゃうプログラマーが構造体を知っているか、知っていても適切に使えるかは疑問です。

知らない解法を使うことは誰にもできません。ここでは構造体を取り上げていますが、「知ってはいるけどちゃんと使ったことのない○○○(任意の手法・技法)」と考えれば誰でも遭遇し得る状況です。こうした状況で、TDDは無力なのでしょうか。きちんと勉強した熟達プログラマしかTDDは活用できないのでしょうか。

その逆で、TDDは新しい技術を使いこなせるようになったり、未知の技術を発見する役に立ちます。それ自体がシンプルな解の探究になるためです。いまある問題に対してよりシンプルな解に到達するには、以下の4レベルを上がっていく必要があります。

  1. 動く
  2. よりシンプルに書き直す
  3. 他の書き方を複数試して、一番シンプルなものを選ぶ
  4. さらにシンプルな書き方を勉強してくる

グローバル変数の例ではレベル2を取り上げました。構造体を知っているけれどうまく使えないのであれば、レベル3にチャレンジする必要があります。先のコードでは構造体を導入したものの、かえって煩雑になってしまいました。

よりよい設計、よりシンプルな解法を見つけるには、レベル3の試行錯誤が必要になってきます。試してみる、他のアプローチで試してみる、リファクタリングでいろいろなパターンを見比べてみる。その中で一番よいと思えるものを残す。TDDの回転は一直線ではありません。行きつ戻りつしながら少しずつ上がっていくのです。

試行錯誤で最善と思える結果が出ても、まだよくなる可能性があるかもしれない。そう思えたときが、レベル4になってまったく新しい解法を探しに行くタイミングです。レベル3までをきちんとこなしていれば、問題について十分に理解できているはずです。いまある解法の効用と限界も知っているでしょう。それならば、新しい解法を見つけても適切に評価できます。中途半端な解法に飛びついたり、不適切な解法を盲目的にコピーしてしまうこともありません。

レベル3までは問題を深く理解するためのステップで、レベル4が本当の問題解決だと見ることもできるかもしれませんね。

2015-12-13

Scrum Master as a facilitator

This entry is a part of Facilitator’s Advent Calendar.

In a software development process called Scrum, there is a Scrum Master. Scrum emphasizes trust between team members and though it, they create values in collaboration, search for the best possible outcome sometimes with a conflict, and aims for team's growth and success of the product. Scrum Master's job is to remove frictions in communications, point out obvious violations from Scrum's rule (there are very few rules), and commit to the team so that they can achieve the best performance in a given situation. Being a facilitator is not enough to become a Scrum Master, Scrum Masters often play facilitator roles.

In the published ScrumMaster pattern, Scrum Master has at least 8 functions.

  • CATALYST: In order to ensure that the high amount of collaboration needed for Scrum to succeed happens.
  • The ScrumMaster keeps the team adhering to their DEFINITION OF DONE by taking on the role of DONEMASTER.
  • SHEEPDOG: Continually encourage the team follow its processes; watch for and correct deviations.
  • The ScrumMaster can become the KNIGHT OF THE MIRRORS, reflecting the team's behavior back to them so that they understand their strengths and weaknesses.
  • COACH: Challenge the team to improve, and give advice on how to improve.
  • CHEERLEADER: give the team encouragement and positive feedback.
  • FIREWALL: protect the team from unnecessary external distractions.
  • The ScrumMaster may also help the PRODUCT OWNER become more effective, assuming the role of PRODUCT OWNER TRAINER.

Among the list, Catalyst and Knight Of The Mirror is the place where Scrum Master and facilitator cross. Scrum Master facilitates meetings (in Scrum, they are called events or ceremonies.) A team new to Scrum is not familiar with Scrum meetings, so an experienced Scrum Master usually guides through them. Scrum Master is responsible not only the agenda but also energizing discussions, exposing diverse opinions, and to keep time boxes.

At the same time Scrum Master facilitates the growth of the team. For example, usually daily standups are the first meetings which team receives the responsibility to run a meeting by themselves. At first Scrum Master guide the meetings and dictate what to talk about. Once team understands why and how to run the meeting, Scrum Master stands back to let team do by themselves. Sooner or later, team will run other meetings also. Scrum Master might help facilitating, or might leave the team do it 100%.

Outside meetings, there are live communications and discussions everywhere in and around any Scrum teams. Scrum Master watch them carefully and grasps the direction teams are going, notices strong and weak points, or finds potential problems. While Scrum Master facilitates and helps the communications, it's also Scrum Master's job to let the knowledge transparent to the team so that the team understand their behavior clearly. When team have a problem which they cannot solve by themselves, Scrum Master helps.

On the other hand, the team is getting better and better at those communications with suggestions from Scrum Master. The relationship between the team and stakeholders get stronger. That means Scrum Master will have fewer and fewer things to point out, though the observation continues. Problems (or impediments) will be shifted from inner-team to outer-team.

These are only a few examples to show that Scrum Master as facilitator has two roles; one is to facilitate a Ba, the other is to let the team absorb the first role. Meetings, communications, problem solving, they are all managed most effectively when managed by the team. While it is satisfying to be pleased by providing values and effects from Scrum Master, Scrum Master should never bind the team with them, even with good will.

It is said that the ultimate goal of Scrum Master is that the Scrum Master is no longer needed. Scrum Master, working with the team, doing the best to support the team, becomes honorable useless when the team absorb Scrum Master's methods, techniques, and even ideologies. It won't happen soon, will be difficult even after enough time. Yet it must be something any Scrum Master should have in mind.

My way of doing so is, not to do, not to say. It requires patience. Sometimes get blamed. Perhaps it backfires. Still, I believe they can think and speak and act and achieve better without my interference. Even if they fail, I believe they will learn from it. A Ba can provide experience to the people. But learning and growth happen beyond a single Ba. Facilitators are said, well, they facilitate. Facilitating a long term growth is also part of the facilitator's scope, or so I think. Anyway it's easier to replace the idea of "They can attain the best when left themselves" with "I won't be much of help to them anyway!" And latter's more realistic.

With the idea in my mind, I provide not much of help, but sometimes a bit of help, so that the speed of learning from failures can be maximized. That is the way toward the uselessness, perchance.

Thank you for reading so far. Gaoryu-san, or id:DiscoveryCoach, invited me for this year's Facilitators Advent Calendar. I want to express my thanks for the invitations and the interesting chance to write about my view of facilitator.

スクラムマスターというファシリテーター

スクラムというソフトウェア開発プロセスには、スクラムマスターという役割がいます。スクラムではチームメンバーが信頼に基づいた協力関係の中で価値を創出し、ときには対立もしながらよりよい成果を模索し、チーム全体の成長とプロダクトの成功を目指します。その中でスクラムマスターは全体が円滑に進むようにし、明白なルール違反を指摘し(ルールの数はごくわずかです)、対立を解消し、チーム全体が与えられた状況で長期的に最善のパフォーマンスを出すことにコミットしています。ファシリテーターならスクラムマスターができるわけではありませんが、スクラムマスターはファシリテーターの役割を果たします。

以前に紹介したパターンによると、スクラムマスターには以下の8つの役割があります。

  • 触媒: スクラムに欠かせない大量のコラボレーションを発現させる。
  • 完成マスター: スクラムマスターはチームに完成の定義(Definition Of Done)を守らせる。
  • 牧羊犬: チームが常にプロセスを守るよう支える。監視して、ズレを補正する。
  • 鏡の剣士: スクラムマスターは鏡の剣士となり、チームの行動を投影してみせる。チームは自分自身の長所と弱点に気づく。
  • コーチ: チームに改善を促し、改善のためのアドバイスを与える。
  • チアリーダー: チームを元気づけ、ポジティブなフィードバックをする。
  • ファイヤウォール: 不必要な外部の影響からチームを保護する。
  • プロダクトオーナートレーナー: スクラムマスターはプロダクトオーナーがより上手になるのを手伝う。

この中では触媒と鏡の剣士が、スクラムマスターとファシリテーターの交叉する部分になっています。スクラムマスターはミーティング(スクラムではイベントやセレモニーと呼ばれる)のファシリテーションをします。スクラムに不慣れなチームは上手な進め方が分からないので、経験あるスクラムマスターがガイドすることになります。進行はもちろん、議論の活性化や、多様な意見を表出させたり、タイムボックスを守るのもスクラムマスターになります。

同時にスクラムマスターはチームの成長を促します。特に朝会(15分以内の毎日全員が参加するミーティング)は、進行をスクラムマスターからチームメンバーに委譲する最初のセレモニーとなることが多く、進め方を全員が把握したらスクラムマスターは後ろに下がって、チームにファシリテーションを任せます。他のミーティングも、多かれ少なかれチームが進行するように段々なっていきます。スクラムマスターはファシリテーションを手伝うこともあるし、100%チーム任せにすることもあります。

ミーティング以外でも、スクラムでは始終活発なコミュニケーションが起きています。スクラムマスターはその様子を見て、チームが進む方向を把握したり、長所短所に気づいたり、問題の芽を見つけたりします。スクラムマスターはコミュニケーションが上手くいくよう手伝いながら、そうした知識をできるだけ透明にチームに伝えて、チームが気づかずにいた点を直せるよう仕向けます。チーム内で対処しきれない問題があれば、スクラムマスターが代わって解決します。

いっぽうチームはスクラムマスターからの指摘を受けながら、スクラムの進め方やコミュニケーションの取り方に上達していきます。連携すべきステークホルダーとの関係性も、徐々に確かなものになっていきます。そうするとスクラムマスターは、今までと同じように観察していても、指摘することは減ってきます。問題(スクラムの言葉では障害物、インペディメント)の解決も、チーム内の問題から、チーム外へと重心が移っていきます。

ここでは一部しか例を挙げませんが、このように、ファシリテーターとしてのスクラムマスターは、ひとつの場をファシリテートする役割と同時に、その役割をチームが吸収できるよう成長させる役割を持っています。チームのミーティング、チームのコミュニケーション、チームの問題解決、いずれもチーム自身で対応できるのが一番効果的であると信じて、その方向に向かってゆくのもスクラムマスターの仕事です。

スクラムを導入した開発チームにおいて、スクラムマスターの最終ゴールとは、自分が不要になることだとも言われています。チームと一緒に働きながら、スクラムマスターとしての全力でチームを支援し、そうした支援のやり方やテクニック、思想までチームが吸収してくれれば、名誉ある用済みになれる。スクラムマスターとして、自分の価値や効用を提供できれば、喜んでもらえるし満足度もありますが、それにチームを縛ってはならないと思います。

私が気をつけているのは「手を出さない」「口を出さない」ことです。我慢も辛抱もいるし、非難されることもあるし、逆効果のこともあります。それでも、みんな自分で考えて発言して行動できるし、私が関与するよりもきっと良い結果を出せるはず、失敗するかもしれないけどそこから学ぶことだってできる、そう信じています。経験は場で起きるかもしれないけれど、学びと成長は場を超えていきます。ファシリテーターって、促進する人だとすると、長い時間をかけた成長を促進するのもやはりファシリテータだと思いたい。まあ、「参加者に任せれば最高の結果が出るはず」よりは「私なんか大して役に立たないし」と思うほうが、簡単だしきっと現実に近いんですけれども。

そう思えば、大して役に立たないけどちょっとは役に立つことをしながら、みんなが失敗しながら学んでいく速度を最大化する、やがては役立たずの烙印を押されるその日に向かっていくこともできるのかな。

最後になりましたが、ファシリテーター Advent Calendar 2015 - Adventarには id:DiscoveryCoach ことがおりゅうさんに誘っていただきました。場違いなエントリかもしれませんが、読んでいただいてありがとうございます。

2014-10-27

TDDの経験と現状のアンケート

「TDD(テスト駆動開発)ってどのくらい使われてるんですか?」と聞かれることがあります。それはですね、俺だって知りたいわー!というわけで、「TDDの経験と現状について」というアンケートを作りました。

10/23の段階で83件の回答がありました。ありがとうございます。TDD人気ありますね。中間報告として、これまでの回答を公開したいと思います。始めた時期と現在の状況のグラフです。

f:id:yach:20141027153905p:image:leftf:id:yach:20141028161537p:image

回答全体のサマリはこちらで見られます(回答したときに見られるのと同じです)。なお、こちらは随時更新されるので、本エントリの内容と一致しないかもしれません。

https://docs.google.com/forms/d/1pb29VBqO-kd10ks_x9oqvkMUy5rDW4nMoDnBPVM85yc/viewanalytics

※アンケートはまだまだ受付中です。こちらからどうぞ→ http://goo.gl/forms/fbWsZmH3El

分析してみる

2年以上やっている人が50%以上、最近始めた人は意外と少ない(1年以内が10%)という印象です。現状は、「よく使っている」は「以前ほどではない」も含めると約50%です。大雑把に言えば、長く使っている、使い続けている人が相当数いると言えそうです。(ただしアンケートに回答してくれた方が、業界全体(母集団)を偏りなく代表しているという根拠はないので、「いるね」という以上のことは言えませんけれど。)

今回このようなアンケートを採っているのは、TDD本当に使ってるの?意外と辞めちゃってたりしない?と思ったためです。TDDについて(特に今年は)議論がありますが、議論は議論として、現場はどうなのか。

もうひとつ、自分のことを振り返ると、以前ほどTDDしてないなあと思います。それは、コード書いてないから……ごめんなさいごめんなさい。でも書くときでも、やっぱり以前のような100%TDDはやってない。なぜだろうか。思いついた理由は2つです。

  1. 何でもかんでもTDDではなく、効果的に使うポイントがわかるようになった。
    • 例: Webアプリはエンドツーエンドでテストするよね(Selenium Driverとか)
    • 例: 静的データ構造はTDDあんまり意味ないよね
  2. TDDは実は学習のツールだった。今は学習することが減った(コーディングについて)。
    • 例: TDDのおかげでデザインパターンが使えるようになったが、今はTDDなくても使える
    • 例: SQLAlchemyってどう使うんじゃ。TDDスタイルで実験してみよう。

他の人はどうだろう?特に2番の理由ってあり得るんだろうか?この検証が、アンケートのもう一つの狙いです。

期間と使用度合いの相関は?

TDDを始めてからの期間と、現在の使用度合いの関係を調べてみました。こちらが結果のグラフです。

f:id:yach:20141028161538p:image:right

注目したいのはオレンジと赤の部分です。「たまにしか使わない」「以前ほどではない」が、2〜5年のところで増えています(1年以内はデータ数が8件と少ないのでとりあえず無視)。5年以上では「現在もよく使う」が増えていますが、オレンジの「たまにしか使わない」も増えています(グラフだと見難いが、18%→24%→26%)。TDDの経験年数が長いと、「たまにしか使わない」人が増える傾向があるようです(わりと乱暴ですけど)。

さらに、理由も簡単に見てみました。「実装しなくなった」「今の仕事ではTDD困難」など環境要因を除くと、TDDを使う・使わない理由が少し見えてきます。2年以上の回答から、代表的なものをいくつか紹介します。

  • 現在もよく使っているが、以前ほどではない
    • 自明である事とそうでないものの区分けができるようになった。
    • コーディングを進めていき、不安な箇所をTDDする等TDDとのつきあいかたが変わった。
    • 言語スイッチが増え、他言語のユニットテストを調べなければ使えず、コストが高くなった。
    • 最初は勉強や技能の向上を意図して、意識的にTDDを行うようにしていたため使用機会が多かった。現在はではTDDの得意な所、不得意な所がわかるようになり、使い所のみで使うようになっている。
    • 教科書通りにやると疲れるから、最低限のテストコードで、最大限の効果を出すようになっただけ
  • 現在はたまにしか使わない
    • 概ね"TDD is Dead"に書かれている通りです。Railsで開発(略)という事情もあるかもしれませんが、以前ほどUnitTestをTestFirstで書くといったことはなくなりました。今は、(略)UnitTestを書くのは仕様が複雑なModelの処理の場合のみ(略) ただ、教育的な意味合いで、初めはしっかりUnitTestを書いてください、と指導することはあるので、スキルの土台としてのTDDは必要という認識です。
    • TDDを用いたときの設計の勘所が自然と意識できるようになったこと
    • 統合テストを重視するようになり、そちらにテストを書くというコストをかけるようになったため
    • 全てをTDDのサイクルに乗せるとスケジュール的な無理が生じる場合が有る。
    • ソフトウェアの寿命が短くなって頻繁に変更しない部分にまでテストを書くメリットがなくなったので抜き気味にやっている。
    • TDDが有効な場ではTDDで開発しますし、そうでないなら別のアプローチを使います。適材適所。
  • 現在はまったく使わない
    • どんなクラスのI/Oにすれば良いか定まらないまま実装を始めてしまうため。区切りがよくなるまで(I/Oが定まるまで)xUnitは書いていない。
    • いざ製造を始めると設計不備による仕様変更が多発するため、テストを先に作っておくと手戻りが多すぎる。
    • モックの記述が非常に面倒。モジュール中のどのメソッドが何回目に呼ばれて何を返すとか、製造終了まで分かる訳がない。
    • テストが動けばいいやというコーディングスタイルが蔓延し、バグが多発した。

(なお回答の生データのリンクを末尾に載せています。ここにない理由含め、全回答がそのまま見られます。)

また、理由をカテゴリに分類して分布を調べてみました。

理由 2〜5年前 5年以上前
環境 6 (75%) 2 (25%) 8 (100%)
適所 4 (40%) 6 (60%) 10 (100%)
棄却 1 (50%) 1 (50%) 2 (100%)
不明 6 (100%) 0 (0%) 6 (100%)

対象は、現状が「以前ほどは使わない」「たまにしか使わない」「まったく使わない」の回答です。表のパーセント表記は、横方向に加えると100%になるような計算です。分類は以下の定義で、主観で判断しました。

環境
TDDができない製品、職場環境である。実装をしなくなった。など
適所
いつ、どういうときにTDDをするか判断する(結果TDDしないこともある)。
棄却
TDDを理解した上で、現状では役に立たないと判断している。
不明
それ以外、分類できない、回答なし

以上、中間報告でした。アンケート自体はまだまだ回答を受け付けています。データが増えればまたお知らせしたいと思います。

※アンケートはまだまだ受付中です。まだの方はぜひどうぞ!→ http://goo.gl/forms/fbWsZmH3El

またTDDの現状について情報やコメントがあれば、教えてください。

詳細なアンケート結果(閲覧のみ; 随時更新)

https://docs.google.com/spreadsheets/d/1jJX102o7i46WT1TF7TsQwcvAM4EHLNqOhlpBboTAikk/pubhtml?gid=1987591198&single=true

※今回の報告は、10/23までのぶんを対象に分析しました。

Survey of TDD - experience and current usage

This is an interim report of a survey of TDD - experience and current usage. The survey asks only 3 questions.

  1. When you started to do TDD?
  2. How often do you use TDD now?
  3. Please tell us your reason about the answer for question 2.

Here is the summary from about 90 responses.

f:id:yach:20141027153905p:image:leftf:id:yach:20141028161537p:image

You can access more detailed summary here. Raw responses are also available.

Next chart shows an analysis across experience and current usage.

f:id:yach:20141028161538p:image

Orange part (seldom / occasionally) increase beyond 1 year (18%→24%→26%) as experience extends.

We picked up a few reasons from responses. (Translation from Japanese by the author.)

  • regularly but not much as before
    • changed how to use TDD. e.g. I write code non-TDD and when in doubt I try TDD.
    • I have to use many new languages and cost to examine each language's unit testing increased.
    • At first I used TDD as a method to learn and earn excellent skills. Currently I know pros and cons of TDD and only use TDD when it's good.
    • Doing TDD to the letters exhausts me. I gain maximum value with minimum effort.
  • seldom / occasionally
    • Same as "TDD is Dead" dictates. I write unit tests only for complicated models. Others are covered with end-to-end tests. For newbies I ask them to learn TDD as a basic skill.
    • I learned good designs TDD produces. Now I can do good design without TDD.
    • I write integration tests.
    • Doing everything TDD takes too much time to be on schedule.
    • Longevity of software has been shortened. No much gains in writing tests for code which rarely changes. I do TDD but not for everything.
    • I do TDD when it works and don't when not.
  • no usage now
    • Because I start implementation before deciding what I/O the class will have. I don't use xUnit until I/O are fixed.
    • Test-first costs too much because there are many spec changes after starting implementation.
    • Writing mocks are too cumbersome. It's impossible to foretell which methods in which module is called in what order.
    • People write code that just pass tests. Many bug ensues. So I stopped using TDD.

We categorized the reason for responses with more than 2 years experience AND less than "regularly" used (= regularly but not much, seldom / occasionally, none.)

reason 2-5 years > 5 years total
environment 6 (75%) 2 (25%) 8 (100%)
only when effective 4 (40%) 6 (60%) 10 (100%)
discarded 1 (50%) 1 (50%) 2 (100%)
unknown 6 (100%) 0 (0%) 6 (100%)
environment
products or working environment are unsuitable to TDD. I no longer code. etc.
only when effective
I choose to do TDD only when it's effective and I know how to choose.
discarded
I understand TDD but I found it not having significant value, so I don't do TDD
unknown
no answer, others.
 
ページビュー
420726