eyecatch
Sat, Jun 25, 2016

ソフトウェアプロジェクトの7つの大罪

このエントリでは、Yegor Bugayenkoによる記事、Seven Deadly Sins of a Software Projectを紹介する。 (Yegorから和訳と転載の許可は得た。) 以下はその全文の和訳だが、意訳超訳が混じっているので、もとのニュアンスを知りたければ元記事を読んでもいいし、読まなくてもいい。 (adsbygoogle = window.adsbygoogle || []).push({}); 保守性は近代ソフトウェア開発において最も重要な美徳だ。 保守性は基本的に、新規開発者が本格的な修正を始める前に必要な学習時間で測ることができる。 学習時間が長いほど保守性は低い。 必要な学習時間が無限に近いプロジェクトもあるが、これは文字通り保守不能だ。 私はソフトウェアを保守不能にする7つの基本的で致命的な罪があると考えている。 それらについてここに書く。 アンチパターン 不幸にも、我々が使っているプログラミング言語は柔軟すぎる。 可能なことが多過ぎ、禁止されていることは少なすぎる。 例えばJavaは、数千のメソッドを持った単一の「クラス」でアプリケーション全体を記述することに何の反抗もしない。 このアプリケーションは技術的にはコンパイルして実行できる。 しかしこれはゴッドオブジェクトと呼ばれるよく知られたアンチパターンだ。 つまり、アンチパターンは技術的には設計に取り入れることができるが、一般的には取り入れるべきではないとされている。 言語ごとに多くのアンチパターンがある。 プロダクトに使われているアンチパターンは、生きている有機体の中の腫瘍のようなものだ。 いったん成長し始めると止めるのは非常に難しい。 やがて体全体が死に至る。 やがてソフトウェア全体が保守不能になり、書き直さなければならなくなる。 ひとたびアンチパターンを使ってしまうと、その量は次第に増え、「腫瘍」は育つばかりだ。 これは特にオブジェクト指向言語(Java、C++、Ruby、Python)に当てはまる。 これらが手続き型言語(C、Fortran、COBOL)から多くを引き継いでしまっているからだ。 また、OOP開発者が手続き型で命令的な思考をする傾向にあるからだ。残念なことに。 ところで、既存の有名なアンチパターンのほかに、私は以下のものもダメなコーディング法だと考えている。 NULL参照 ユーティリティクラス 可変オブジェクト GetterとSetter オブジェクト関係マッピング(ORM) シングルトン Controllers、Managers、Validators Public Static メソッド キャスト 私ができる実践的な提案は、読んで学ぶということだけだ。 ここに挙げた本か私の著書「“Elegant Objects」が多分助けになるだろう。 常にソフトウェアの品質を疑い、「動く」ということだけで満足してはいけない。 ちょうど癌のように、診断が早ければ早いほど生き残る可能性が大きい。 追跡不能な変更 コミット履歴を見るとき、全ての個々の変更に対して、何を、誰が、なぜ変更したのかがわからないといけない。 さらに、これら3つの情報を得るのにかかる時間は秒単位で計測しないといけない。 殆どのプロジェクトがこのようにできていない。 以下に実践的な提案を示す。 常にチケットを使う プロジェクトやチームがどんなに小さくても、例え一人だけでも、修正しようとしている全ての問題に対してチケット(GitHub issues)を作れ。 チケットに問題の簡単な説明とそれに対する考えを記述しろ。 このチケットをその問題に関する全ての情報の一時的なストレージとして使え。 将来、他の誰かがその「不可解なコミット」が何であるかを理解するために参照する可能性のある全ての情報をそこに書け。 コミットからチケットを参照する 言うまでもないが、全てのコミットにはメッセージが付いていないといけない。 メッセージのないコミットはまったくひどい悪習だ。議論の余地はない。 しかしメッセージだけでは不十分だ。 全てのメッセージはチケット番号で始まらないといけない。 GitHub(君ももちろん使っていると思うが)は自動でコミットとチケットをリンクし、変更の追跡可能性を高めてくれる。 何も消さない Gitは「強制」push、つまりサーバに既にあるブランチ全体を上書きするpushを許している。 これは開発履歴を破壊する方法の例のひとつだ。 また、GitHubのチケットを「きれい」にするためにコメントを削除するのをよく見るが、これはまったくの間違いだ。 何であれ決して消すな。 履歴がどんなに汚く(または乱雑に)見えても、そのまま残しておくことだ。 アドホックリリース 全てのソフトウェアはエンドユーザに届けられる前にパッケージングされなければいけない。 Javaのライブラリであれば.jarファイルにパッケージングされリポジトリにリリースされないといけない。 ウェブアプリケーションであればプラットフォームにデプロイされないといけない。 プロダクトの大きさにかかわらず、テスト、パッケージング、デプロイする正規の手順は常にあるべきだ。 理想的な解決策はこの手順を自動化し、コマンドラインから単一のコマンドで実行できるようにすることだ。 $ ./release.sh ...
eyecatch
Tue, Feb 9, 2016

継続的インテグレーションは死んだ

このエントリでは、Yegor Bugayenkoによる記事、Continuous Integration is Deadを紹介する。 (Yegorから和訳と転載の許可は得た。) 以下はその全文の和訳だが、意訳超訳が混じっているので、もとのニュアンスを知りたければ元記事を読んでもいいし、読まなくてもいい。 (adsbygoogle = window.adsbygoogle || []).push({}); 数日前、「なぜ継続的インテグレーションは機能しないのか」という私の記事がDevOps.comに公開された。 それとほぼ同じ日に、Twitterで非常に否定的な批評が送られてきた。 継続的インテグレーションが機能しないとはどういうことだ。この人気なすばらしいアイデアが。 その求めてもない質問への返事をここに書く。 私はこの分野に関して多少の経験があるが、それに基いた論拠は挙げない。 代わりにロジックだけを頼りにする。 ところで、私には50以上のオープンソースや営利プロジェクトで5年間Apache Continuum、Hudson、CruiseControl、Jenkinsを利用した経験がある。 さらに、数年前fazend.com(2013年にrultor.comに改名)というホスト型継続的インテグレーションサービスを開発した。 現在TravisとAppVeyorのアクティブユーザでもある。 継続的インテグレーションはどう機能すべきか 考え方はシンプルで明確だ。 masterブランチ(Subversionなら/trunk)に新しくコミットをする度に、継続的インテグレーションサーバ(またはサービス)はプロダクト全体のビルドを試みる。 「ビルド」というのはコンパイル、ユニットテスト、統合テスト、品質解析などを意味する。 その結果は「成功」か「失敗」だ。 もし成功だったら「ビルドがクリーン」であると言う。 もし失敗だったら、「ビルドが壊れている」と言う。 通常、ビルドが壊れるのは、以前通っていたユニットテストを通らなくするような新しいコードをだれかがコミットしたからだ。 これは問題の技術的な面だ。 この部分はいつも上手くいく。 まあ、依存が直書きされてるとか、ビルド環境が十分分離されていないとか、ビルドの並列性が完全じゃないとか、そういう問題はあるかもしれないが、この記事はそれらについてではない。 アプリケーションが上手く書かれていてユニットテストが安定しているなら、継続的インテグレーションは簡単だ。 技術的には。 組織的な面を見てみよう。 継続的インテグレーションというのは、ビルドを実行するサーバだけを指すのではなく、上手く機能すべき管理的/組織的プロセスだ。 プロセスが上手く機能するとは、Jez Humbleが「継続的デリバリー: ビルド、テスト、デプロイの自動化による確実なソフトウェアリリース」の55ページで言っていることそのものを意味する。 もしビルドが失敗したら、開発チームは何をやっていたとしてもそれを中断して、そのビルドの問題を速やかに直す。これが重要だ。 これが上手くいかず、上手くできないことだ。 誰がこれを必要としているのか 既に述べた通り、継続的インテグレーションとは、開発チーム全体を止めて壊れたビルドを修正させることだ。 繰り返すが、ビルドが壊れたら直ちに、それを修正し、ビルドを安定した状態に戻すコミットを入れることに全員が集中すべきだ。 ここでひとつ疑問が生じる。誰が、活動中のチーム内の誰がこれを必要としているのだろうか? 一刻も早く新しい機能をリリースしたいプロダクトオーナ? または、締め切りに責任を持つプロジェクトマネージャかもしれない。 もしくは、他の誰かが作りこんだバグをプレッシャーを受けながら修正すること嫌うプログラマかもしれない。 誰がこの継続的インテグレーションを好み、誰が必要としているのか? 誰でもない。 実際に何が起こるのか 教えよう。 私は何度も見たことがある。 シナリオはいつも同じだ。 継続的インテグレーションのビルドステータスは単に無視されるようになる。 ビルドがクリーンか壊れているかにかかわらず。 そして以前のやり方が継続される。 Jez Humbleが推奨するように開発を止めて問題に対応したりしない。 代わりに、継続的インテグレーションサーバから来る情報を無視する。 しばらくして、次の日かもしれないし月曜日かもしれないが、空いた時間を探してビルドの修正に取り組む。 これは単に、ダッシュボードの赤いボタンが嫌で緑に変えたいからだ。 規律についてはどうか そう、これには別の見方もある。 チームに規律を徹底させることもできる。 ビルドは常にクリーンで、壊した人は何らかの罰を受けるという厳格なルールを設けることができる。 これを試すとなると、恐怖駆動型開発を実施することになる。 プログラマは、ビルドを失敗させたら少なくとも謝罪しなければならなくなるため、リポジトリへのコミットを恐れるようになる。 この場合の厳格な規律(私は大好きだが)は、単に状況を悪化させる。 開発プロセス全体が遅くなり、プログラマはビルドを壊さないように自身のコードをできるだけ長い間手元に保持する。 いざコミットするとなった時、変更は巨大になっていて、マージは非常に難しいか、時に不可能になる。 結果、プログラマが書いた多くのコードがコミットされること無く捨てられる。 あの恐怖因子のせいだ。 OK。解決策は?
eyecatch
Mon, Jan 11, 2016

ソフトウェアアーキテクトは何をするのか?

このエントリでは、Yegor Bugayenkoによる記事、What Does a Software Architect Do?を紹介する。 (Yegorから和訳と転載の許可は得た。) 以下はその全文の和訳だが、意訳超訳が混じっているので、もとのニュアンスを知りたければ元記事を読んでもいいし、読まなくてもいい。 (adsbygoogle = window.adsbygoogle || []).push({}); 君のプロジェクトにはソフトウェアアーキテクトが居るだろうか? 必要だと思う? まあ、ほとんどのアジャイルチームはそのような役割を明確には定義せず、民主的な感じで働く。 全ての重要な技術的な意思決定はチーム全体で議論され、最多数の投票を得た解決策が採用される。 しばらくして、このようなチームが「ソフトウェアアーキテクト」バッジを誰かのTシャツに付ける事に決めたときは、もっとも評判のいいプログラマがそのバッジを手にする。 このバッジが彼の責務を変えることはまれだけども。 結局、チームは同じように働き続け、全員を巻き込んだ技術的議論を楽しむ。 つまり、ソフトウェアアーキテクトは責務が明確に定義された役割というよりもステータスになる。 それは最年長で最も権限のある人へのチームメンバからの尊敬の印になる。そうだろ? 全く間違っている! アーキテクトは品質の責任を負う 普通はアーキテクトは最も知識、スキル、経験、権限がある人がなるということは明らかだ。 もちろん普通はアーキテクトは他の人よりもものを知っていて、必要に応じて外交的指導的手腕を発揮してその知識を伝達する。 アーキテクトは普通はチームの中で最も賢いやつだ。 しかしこのことは、彼をアーキテクトたらしめているものではない。 そして、チームに必要なものでもない。 私のソフトウェアアーキテクトの定義こうだ。 アーキテクトは品質の責任を負う人だ。 「責任 (blame)」を職責 (accountability) とか 責務 (responsibility) と言い換えてもいいが、私は「責任 (blame)」という言葉を使うのがいいと思う。 なぜなら、開発中の製品の全ての品質問題がアーキテクトの個人的な失敗であることをより強調するからだ。 もちろん、その責任の対価として、品質がよかった場合には満足した顧客からの称賛は全てアーキテクトのものだ。 これがチームに必要なものだ。 開発するソフトウェアの品質に対して誰かが個人的に責任を負うのだ。 プロジェクトマネージャの仕事は、アーキテクトによる全ての技術的決定に対して誰にも不信を抱かせないようにすること アーキテクトが他のメンバにどのように責任を委譲するかはアーキテクト自身の仕事だ。 知識やスキル、品質管理ツール、ユニットテストフレームワーク、権限、コーチング、体罰、何を使おうとも、それが彼の仕事だ。 プロジェクトマネージャは品質管理をソフトウェアアーキテクトに委譲した。 それをさらにどう委譲するかはソフトウェアアーキテクト次第だ。 ソフトウェアアーキテクトの役割は全てのプロジェクトにおいて重大だ。 たとえたった二人のプログラマが同じデスクで働いている場合でもだ。 二人のうち一人はアーキテクトでなければならない。 理想的なアーキテクトは上記の長所の全てを持つ。 彼は全員の意見を聞いて考慮に入れる。 彼はよいコーチであり先生だ。忍耐もある。 彼は効果的な伝達者であり交渉人だ。 外交官だ。 技術的な領域のエキスパートだ。 しかし、たとえこうした長所全てを持たなくても、彼の決定は常に最終決定だ。 そして、プロジェクトマネージャの仕事は、アーキテクトによる全ての技術的決定に対して誰にも不信を抱かせないようにすることだ。 これが委譲というものだ。 責任には常に権力が伴う。 プロジェクトマネージャは定期的にアーキテクトの成果を評価すべきだ。 チームで開発中の製品の品質はアーキテクトの個人的な(!)責任だということを思い出してほしい。 どんな問題であっても彼の問題だ。 彼を責めたり罰したりすることを恐れてはいけない。 ただし、罰を有効なものにするためには、アーキテクトの行動に対して全力で応えるべきだということをを忘れてはいけない。 繰り返すが、彼の決定は最終決定だ。 もしプロジェクトマネージャが製品の品質に満足せず、またアーキテクトがその状況を改善しないなら、アーキテクトを交代させる。 彼をプログラマに降格させ、他のプログラマをアーキテクトに昇格させる。
eyecatch
Sun, Jan 3, 2016

オブジェクト指向プログラミングにおいてユーティリティクラスに代わるもの

このエントリでは、Yegor Bugayenkoによる記事、OOP Alternative to Utility Classesを紹介する。 (Yegorから和訳と転載の許可は得た。) 以下はその全文の和訳だが、意訳超訳が混じっているので、もとのニュアンスを知りたければ元記事を読んでもいいし、読まなくてもいい。 (adsbygoogle = window.adsbygoogle || []).push({}); ユーティリティクラス(またはヘルパークラス)は、スタティックメソッドだけを持っていて、状態を内包しない「構造体」だ。 Apache CommonsのStringUtils、IOUtils、FileUtilsや、GuavaのIterables、Iterators、またJDK7のFilesはユーティリティクラスのいい例だ。 ユーティリティクラスはよく使われる共通機能を提供するので、この設計手法はJava(やC#、Rubyなど)の世界でとても人気だ。 要するに我々は、DRY原則に従い、重複を避けたい。 だから、共通コードをユーティリティクラスに入れて必要に応じて再利用する。 // これはひどい設計なので再利用しないように。 public class NumberUtils { public static int max(int a, int b) { return a > b ? a : b; } } 実際、これはとても便利なテクニックだ!? ユーティリティクラスは悪だ しかし、オブジェクト指向の世界では、ユーティリティクラスはかなり悪い(酷いという人さえいるかもしれない)手法だ。 これについては多くの議論がある。 いくつか挙げると、Nick Malikの「ヘルパークラスは悪か?」、Simon Hartの「なぜヘルパー、シングルトン、ユーティリティクラスはだいたい間違っているのか」、Marshal Wardの「ユーティリティクラスを避ける」、Dhaval Dalalの「ユーティルクラスを殺せ!」、Rob Bagbyの「ヘルパークラスは問題の兆候」。 また、StackExchangeにはユーティリティクラスについての質問がいくつかある。 例えば、「ユーティリティクラスが悪なら、どこに共通コードを書けばいい?」とか、「ユーティリティクラスは悪」とか。 これらの主張は要するに、ユーティリティクラスは適切なオブジェクトではないということだ。 だから、オブジェクト指向の世界に適合しない。 ユーティリティクラスは、当時の人々が機能分割パラダイムに慣れていたために、手続き型言語から受け継がれた。 君がこの主張に同意し、ユーティリティクラスを使うのをやめたがっていると想定し、そいつをどのように適切なオブジェクトに置き換えるかを例を挙げながら教えよう。 手続き型の例 例えば、テキストファイルを読んで、行で分割し、各行をトリムして、その結果を別のファイルに保存したいとする。 これはApache CommonsのFileUtilsを使えばできる。 void transform(File in, File out) { Collection<String> src = FileUtils.readLines(in, "UTF-8"); Collection<String> dest = new ArrayList<>(src.size()); for (String line : src) { dest.add(line.trim()); } FileUtils.writeLines(out, dest, "UTF-8"); } 上のコードはきれいに見える。 しかし、これは手続き型プログラミングであって、オブジェクト指向じゃない。 コードの各行で、データ(byteとbit)を操作し、コンピューターにどこからデータを取ってどこに書き込むかを明示的に指示している。 処理の手順を定義している。 オブジェクト指向な方法 オブジェクト指向パラダイムでは、オブジェクトをインスタンス化して合成すべきだ。 これはオブジェクトにオブジェクト自身のやり方でデータを管理させるためだ。 補足的なスタティックメソッドを呼ぶ代わりに、求めている挙動を提供できるオブジェクトを生成するべきだ。 public class Max implements Number { private final int a; private final int b; public Max(int x, int y) { this.a = x; this.b = y; } @Override public int intValue() { return this.a > this.b ?