株式会社クリアコード > ククログ

ククログ


2012年のまとめと2013年にやりたいこと

毎年、年の最後にはその年の記事をまとめていたのですが、2012年分のまとめをし忘れたので、ここでまとめておきます。

2013年の記事に2012年のことだけが書かれているというのは違和感があるので、はじめに、2013年にやりたいことをまとめておきます。

2013年にやりたいこと

2012年に社外に向けて新しくやり始めたことがいくつかありました。ただ、クリアコードは「社外に向けて新しくやり始めること」に慣れていなかったので、始めるだけで大変でした。始めることはできたので、2013年は実際にやっていくことに力を注ぎます。具体的には以下のことに力を注いでいきます。

  • パッチ採用
  • コミットへのコメントサービス

詳細は後述します。

また、以下に挙げる既存の業務においては、ソフトウェアの発展やお客様のニーズの変化にあわせて、提供するサービスも多様化してきました。2013年もクリアコードならではの専門的な支援を提供していきます。

  • Mozilla Firefox/Thunderbird
  • 組み込みシステム
  • groonga
  • milter

これらの詳細は新しく始めたことの後に触れます。

パッチ採用

2012年の8月からパッチ採用をはじめました。パッチ採用とは、応募者とクリアコードがパッチをやりとりすることでお互いの適性を判断する採用方法です。私達はたくさんのフリーソフトウェアの開発に参加しています。それらの開発の中で行われている「開発仲間になろうとしている人が信頼できる人かどうかを判断する方法」を取り入れた採用方法です。

クリアなコードでフリーソフトウェアを書いて、自由に扱えるソフトウェアを増やしていきたいという方は、ぜひクリアコードに応募してください。ただいま、開発者を1名募集しています。

コミットへのコメントサービス

2012年の12月からコミットへのコメントサービスをはじめました。このサービスは開発チームが「よいコードを書くことが当たり前」になることを支援するサービスです。これもフリーソフトウェアの開発に参加する中で学んできたことをベースにしています。クリアコード外の開発者とやりとりしたときに、「よいコードを書くことが当たり前」ということがなかなか実現できていないことを知ったため、このサービスを提供することになりました。

自分たちの開発チームが、今は「よいコードを書くことが当たり前」ではないけれど、「よいコードを書くことが当たり前」にしたいという方はぜひお問い合わせください。

Mozilla Firefox/Thunderbird

クリアコード創業以来ずっと続けているのがMozilla Firefox/Thunderbird関連の業務です。これまでも業務の中で開発してきたソフトウェアをフリーソフトウェアとして公開してきましたが、2012年はFirefox、Thunderbirdの法人向け延長サポート版(ESR)のリリースを受けて、Fx Meta Installerをはじめ多くのアドオンを公開しました。今年は、Android端末へのMobile版Firefoxの導入支援や、Firefox OSの開発、サポートも手がけていく予定です。

Mozilla関連の業務の担当人数が少ないので、お断りしているお問い合わせもあるのが現状です。もし、Mozilla関連の業務に興味があるという方はパッチ採用で応募してください*1

組み込みシステム

ここ数年、組み込みシステムの開発に携わっています。今年は、組み込み機器用の既存の製品とは違った斬新なコンテンツ配信システムを開発する予定です。本システムはフリーソフトウェアではありませんが、ベースコンポーネントとして使用しているソフトウェアはフリーソフトウェアです。本システムで得た知見を、これらのフリーソフトウェアに還元していきます。

groonga

2012年もgroongaの開発に積極的に関わってきました。毎月のリリース作業だけではなく、nginxベースのHTTPインターフェイスの開発やfluentdを使ったレプリケーションの仕組みなどを実装しました。groongaベースのMySQL用ストレージエンジンであるmroongaの開発もしています。また、未来検索ブラジルさんらと連携して、groongaのサポートサービスを始めました。

2013年も、引き続き、groonga本体および周辺ソフトウェアの開発を継続し、より高速で柔軟で使いやすい検索エンジンとなるようにしていきます。もし、groonga関連の開発に興味があるという方はパッチ採用で応募してください*2

milter

milter関連の業務では、milter managerおよび各種milterの導入支援や独自のmilterを開発しています。2012年から、大規模なメールシステムでmilterやmilter managerの採用を検討されることが増えてきているようで、お問い合わせもいただいています。今年は体制を強化して、より多くのシステムへのmilter導入を支援していきます。

2012年のまとめ

ククログ上で公開した今年の活動を月毎にまとめます。記名していませんが、ククログの記事は記事によって書いている人が違います*3。須藤が書いていることが多いですが、2012年から須藤以外が書くことが増えました。

1月

1月の技術的な記事はGitで不適切なコミットメッセージを削除した公開リポジトリを作るデバッグしやすいHTMLのテストの書き方でした。Gitの方はGitHubのclear-code organizationにMozilla関連のリポジトリを公開するときに得た知見をまとめたものです。テストの方はこの頃に思いついたよい書き方をまとめたものです。昨年に引き続き、業務で得た知見を公開できてよかったです。

また、技術関連の記事だけではなく、会社運営関連の記事も書きはじめました。1月は均衡待遇・正社員化推進奨励金のうち、正社員転換制度健康診断制度について紹介しました。小さな会社を運営している人たちの役に立つとうれしいです。

モーショノロジー2012 #1: rroongaによる検索サービスの実装という発表もしました。

2月

2月はクリアなコードの作り方: 縦長・横長なメソッドを短くするがありました。普段どうやってよいコードを書いているかを文書化したものの1つです。

また、logalimacsをリリースしました。Emacsで翻訳している人は試してみてください。

コミットメッセージの書き方は2012年一番の人気記事でした。

2012年は4年に1度のうるう肉の日もありましたね。この日のリリースはgroongaとmroongaのメジャーバージョンアップでした: groonga 2.0.0, mroonga 2.00リリース

3月

クリアなコードの作り方: 意図が伝わるコミットのしかたは2月のコミットメッセージの書き方の続きです。

Emacsの設定は継続的に改良しているのですが、この時点のものをEmacs実践入門 - おすすめEmacs設定2012でまとめました。

ifとreturnの使い方もよいコードをどうやって書いているかを文書化したものです。

4月

Emacs上でカラフルにdiffを表示するは今も便利に使っています。

中小企業倒産防止共済制度は会社運営シリーズです。

テストをすっきり書く方法はテストをグループ化しようという話です。test-unitを使っている人はぜひ実践してください。

5月

国際化サポート付きのYARD 0.8.0がリリースされました。長かったです。引き続き国際化サポートを強化する変更をpull requestしていて、徐々にですが進んでいます。

The dRuby Bookを紹介しました。

クリアなコードの作り方: 余計なことを書かないはコードを読むときにどのようなことを考えるかがわかります。

6月

ククログの記事のライセンスをCC BY-SA 3.0とGFDLのデュアルライセンスに設定したので、ライセンスの範囲内であれば、クリアコードに個別に許可を取る必要なく自由に利用できるようになりました。

リーダブルコードの解説を公開しました。この解説はCC BY-NC-SAなので、他のククログの記事とは違うライセンスです。

名前のつけ方はプログラミングではとても大事な「名前をつけること」について扱った記事です。どんな名前がよいかを説明しています。

7月

思い出せるチケットの書き方: 「動機」、「ゴール」、「実現案」は作業をチケットにメモしている人はぜひ試して欲しいことです。

クリアなコードの作り方: 同じことは同じように書くはどうやってよいコードを書いているかを文書化したものです。

Firefox/Thunderbird用アドオン開発者向けテスティングフレームワーク UxU 1.0.0をリリースしました。UxUの開発を始めたのが2007年なので、5年かけて初めてのメジャーリリースでした。

8月

金融機関からの運転資金借入は会社運営シリーズの記事です。会社運営に大事なお金の話です。

「パッチ採用」はじめました

2012年8月リリースまとめにあるように、8月の肉の日は15個リリースしました。

9月

2012年9月1日付けで取締役に就任した足永の取締役就任挨拶です。クリアコードが大事にしていることがわかる挨拶なので、クリアコードに興味のある人はぜひ読んでみてください。

短期前払費用による節税は会社運営シリーズの記事です。記事にも書いてありますが、クリアコードがここに書いてある方法をしているわけではありません。調べたことをまとめたものです。

札幌Ruby会議2012: 「バグの直し方」、「クリアなコードの作り方」 #sprk2012という発表しました。いろいろ思うところがあったRuby会議でした。

クリアコードは札幌Ruby会議2012のスポンサーをしたのでスポンサーチケットをもらったのですが、社内では必要なかったので希望者にプレゼントしました。実際にスポンサーチケットで参加したみなさんが札幌Ruby会議2012のレポートをしてくれたので、それをまとめました: 「札幌Ruby会議2012のチケットを譲るキャンペーン」当選者のレポート紹介

10月

Rubyの拡張ライブラリにYARD用のドキュメントを書く方法はYARDの使い方シリーズです。これからも増えていく予定です。

シェルスクリプトで「ビルドスクリプト」を作る時に便利なテクニックは実際に自分たちが使っている方法を紹介したものです。シェルスクリプトとMakefileの使い分けは補足です。

11月

クリアなコードの作り方: オーバースペックな機能を使わないはどうやってよいコードを書いているかを文書化したものです。

Fx Meta Installerをリリースし、Fx Meta Installerを使った、カスタマイズ済みのFirefoxのインストーラーの作り方を紹介しました。カスタマイズしたMozilla Firefox/Thunderbirdを社内に配布したい、という場合は試してみてください。

退職金共済制度を活用した退職金制度は会社運営シリーズの記事です。クリアコードでも利用している制度です。

久しぶりにるびまに寄稿したので、その紹介をしました: るびま0040号の「Rubyコードの感想戦」とDevLOVE Conference 2012の「リーダブルコードを読んだ後」のお知らせ

12月

いいアドバイスをもらうための相談の仕方は技術的な話ではなく、人とのやりとりに関する話です。プログラミングは1人でもくもくと進める作業と思っている人もいるかもしれませんが、実際は他の人に相談しながら進めることの方が多いものです。そのため、この記事の内容はプログラマーにとって大事なことなのです。

CutterをWindowsでgrowlnotifyと一緒に使う方法は2012年唯一のCutterの紹介記事です。2012年にCutterに劇的な変更はなかったですが、コツコツと開発は継続しています。

DevLOVE 2012: 「リーダブルコードを読んだ後」 #devlove2012というセッションをしたおかげで「コミットへのコメントサービス」の内容をまとめることができました。その2週間後のクリスマスに「コミットへのコメントサービス」の販売を開始しました。

まとめ

2013年にやりたいことと2012年の記事をまとめました。2012年は去年よりも内容が充実していましたね。また、話題も増えました。充実した年だったといえます。

2013年もよろしくおねがいします。

*1 Mozilla関連の業務はサポートの方が多いので、業務全部がMozilla関連というわけではなく、Mozilla関連の業務がないときはRubyなどで開発していたりします。

*2 groongaのコアの開発に興味のあるという方はgroongaの開発元である未来検索ブラジルさんに応募したほうがよいかもしれません。

*3 内容を見るとだれが書いているかがわかることもあります。たとえば、記事中のコード例やコミットログ、発表者名などがヒントになります。

2013-01-08

設計判断事例の紹介:E4Xの事実上の廃止を受けての、UxUの開発方針の検討

設計判断とは何か?

ソフトウェアの開発においては、仕様を決める時に、並立できない複数の選択肢の中からどれか1つを選ばなければならないことがあります。特に設計に関わる場面での判断のことを、一般的に「設計判断」と言います。「この新機能を加えるのか、加えないのか」「この新形式をサポートするのか、しないのか」などのような、「やるか、やらないか」の判断はその典型です。

設計判断においては、常に「やる」という判断を下せるとは限りません。様々な事情から「やる」という選択肢を選べないこともありますし、あるいは何らかの明確な理由があって積極的に「やらない」という判断を下すこともあります。

つい最近も、Firefox用アドオン開発における自動テストを支援するテスティングフレームワーク「UxU」において、「Firefox 20以降ではE4Xを使ったテストケースをサポートしないことにする」という設計判断を下しました。これを例にとって、このエントリでは具体的な設計判断のプロセスを解説します。

発端

UxUには、テストケースの記述を容易にするためのユーティリティが多数実装されており、その中には、文字列として与えられたCSVを解釈したり加工したりするといった機能も含まれています*1。これらの機能を使う際には、長い文字列をテストケース中に埋め込む形で記述する方法として、E4XのCDATAマーク区間をヒアドキュメント記法の代わりとして使用することを想定していました。

JavaScriptには現在の所、複数行にわたるテキストを素直に文字列リテラルとしてソースコード中に記述する方法がありません。この問題を解決するためにE4Xを使うことができます。E4Xは本来はJavaScript中にXMLの要素をリテラルとして記述するためのものですが、CDATAマーク区間の記法を利用すると複数行にわたるテキストを比較的素直にリテラルとして記述できます。そのため、以下のようにヒアドキュメント代わりに利用するという用法が、一部の開発者の間で知られていました。

1
2
3
4
5
var longText = <><![CDATA[
      aaa bbb ccc
      ddd eee fff
      ggg hhh iii
    ]]></>.toString();

この用法は、配布物を1つのJavaScriptのファイルにまとめる必要があるユーザースクリプトなどにおいても重宝されているようです。

ところが、2013年にリリースされる予定のFirefox 20開発版において、E4Xを既定の状態で無効化するという決定が下りました(参照:788290 - Turn javascript.options.xml.chrome off by default)。このままFirefox 20がリリースされると仮定すると、UxU用に書かれたテストケースのうちE4Xを利用したテストケースはFirefox 20上では動作しません。

そこで、これを受けて、E4Xを使用して書かれたテストケースについてUxU側で何らかの対策を取ることができないかを検討することにしました。

テスティングフレームワークとして重視したい点

UxU用に書かれたテストケースを「UxUというアプリケーション専用のデータファイル」と捉えた場合、UxUが取るべき姿勢は「可能な限り互換性を保つ」「過去に作成されたデータファイルも、新しいバージョンで問題なく利用できるようにする」ということになります。この点について、疑問を差し挟む余地はないでしょう。

その方針に沿って考えると、今回のFirefoxの仕様変更に対して取り得るUxU側でのアプローチは、以下のようなものがあります。

  1. Firefox(Thunderbird)のE4X機能を自動的に有効化するようにする。
  2. E4Xを自前で実装する。
  3. テストケースの読み込み時に、E4Xをヒアドキュメント代わりに使用している箇所を検出して、文字列リテラルに自動的に変換する。

それぞれのアプローチについて、メリットとデメリットを考えてみます。

Firefoxの隠し設定を変更するというアプローチの検討

前述のbugおよびコミットされたパッチを見るとわかりますが、今回の変更は、E4Xの有効無効を切り替える設定を、既定の状態でオフにしておくというだけのものです。この設定は隠し設定ではありますが、Firefox上で動作するアドオンから見た場合、通常の設定項目のひとつと何ら変わりありません。よって、UxU自身が自動的にこの隠し設定を変更して、E4Xを再度有効化するということは普通にできてしまいます。

このアプローチのメリットは、単にFirefox自体の設定を変更するだけなのでUxU側の負担が非常に小さく済む、という点です。

それに対して、この方法のデメリットは最低でも2つあります。1つは、テスト実行時の環境が「テスト対象のコードが実際のユーザーの環境」からかけ離れた環境になってしまうことです。そもそもUxUがFirefox上で動作するアドオンとして開発されているのは、実際の環境になるべく近い環境でテストを走らせたいという理由からです。テスト実行時に環境そのものを変更してしまうと、「実際の環境でどんな問題が起こりうるか」ということを確かめられなくなってしまいます。

もう1つのデメリットは、この方法が明日にでも利用できなくなってしまうリスクがあるという点です。今回はE4Xの有効無効を切り替える設定の初期値が変わっただけでしたが、E4Xのパーサー自体が削除されてしまった場合、この選択肢はとれなくなります。

E4Xを自前で実装するというアプローチの検討

次に、E4XそのものをUxU側で実装する場合を検討します。

現在、E4Xに対応したJavaScriptの処理系は、C++製のSpiderMonkeyとJava製のRhinoがあり、どちらもオープンソースのプロダクトとして公開されています。よって、極端なことを言えば、これらのJavaScript処理系を丸ごと抱え込んでしまうということも技術的には可能です。

ただ、抱え込むコードの量が増えるという事でもあるため、メンテナンスコストは明らかに増大します。Rhinoのソースコードは全体で20MB、SpiderMonkeyのソースコードは40MBにも及ぶ巨大なプロダクトですし、それらを組み込んで動作させるためのコードも相当な規模になることが予想されます。UxUはクリアコード内製の製品ですが、開発に大きなコストをかける余裕は残念ながらありません*2。開発コストの増大は、プロジェクトそのものの存続を危うくしてしまいます。

また、この場合、必然的に、テスト対象のコードも「UxUが動作しているFirefoxのJavaScript実行エンジン」ではなく「テスト環境用にUxUが内蔵しているJavaScript実行エンジン」で走ることになります。これでは、テスト環境と実環境が大きくかけ離れてしまうという問題が残ってしまいます。

E4X(のCDATAマーク区間)の箇所を文字列リテラルに自動的に変換するというアプローチの検討

E4X全体に対応することは無理でも、E4Xをヒアドキュメント代わりに使っている箇所についてだけ対応することはできないか、という考え方もあります。100%完全にあらゆるケースに対応することは難しくとも、最小のコストで全体の8割程度の場合に対応できるようなやり方があるのであれば、それは有力な選択肢になり得ます。

このアプローチで鍵となるのは、E4Xがヒアドキュメントとして使われている箇所をどのようにして見つけるかです。その方法が十分に簡単なのであれば、この方法は検討に値すると言えます。

この方向でまず思いつくのは、スクリプト全体を文字列として扱った上で、E4Xの部分を正規表現で探して一括置換するという方法です。しかし、この方法には以下のようなスクリプトで「誤爆」が発生してしまうという問題があります。

1
2
var cdataLikeMatcher = /<![CDATA[]/;
if (array[object['property']]>10) { ... }

この問題を回避するためには、「<![CDATA[」という文字列がどのような文脈で登場しているのかを調べて、地の文として登場している場面だけを検出する必要があります。ですが、これは思ったほど簡単な事ではなく、JavaScriptにマクロ記法を導入するライブラリであるsweet.jsで採用されている字句解析レベルでの効率のよい手法を使ったとしても、それなりの規模のコードが必要となります。

「100%完全にあらゆるケースに対応することは難しくとも……」という観点でいえば、そこまでの事をせずとも、単純な文字列置換でできる場合についてはそれで動けばよしとして、動かない場合はE4Xの使用を諦めてスクリプトを書き直してもらう、というやり方もあるように思えるかもしれません。ですが、その場合に生じる「うまく動かないケース」には、E4Xをヒアドキュメント代わりに使っているケースだけではなく、先の例のような「それ自体は何の変哲もないスクリプトだが、たまたまE4Xに似ている文字列が登場する」ケースも含まれ得ます。本来使えないはずのE4XがE4Xとして使えないのは許容できるとしても、本来であれば何の問題も起こらないはずのスクリプトまでもが「E4X対策の影響」で動作しなくなってしまうのでは、本末転倒もいいところです。

前提を見直す

ここまでいくつかのアプローチを検討してきましたが、残念ながらどのアプローチにも問題があり、選ぶに選べない結果となってしまいました。

このように行き詰まってしまった時は、行き詰まりの原因になっている前提を疑ってみることも必要です。いくつかの選択肢を前にして、必ずしもその中からどれかを選ばなくてはならないということはありません。ある観点で見た時に解決策が見えなくても、視野を広げて別の観点から問題を捉え直すことで、解決策が見えてきたり、あるいは、問題が問題ではなくなってくることがあります。ここでは、「UxUでこの問題に対策を行わないといけない」という前提自体を疑ってみます。

そもそも今回の件の発端を考えると、問題提起の仕方は「E4Xをヒアドキュメント代わりに使っている部分がFirefox 20以降で動かなくなる。どうしよう。」というものでした。「E4Xの仕様がサポートされない」「E4Xで書かれた部分が動かない」という点は元々は問題視していなかったのです。

そして、ここでもう1つ大事なのが、ヒアドキュメントの記法があるか無いか・使えるか使えないかというのは、UxUに求められる機能というよりも、JavaScript一般で求められる機能であるという事です。

設計上の判断材料の1つとして、プロジェクトの責任範囲は常に頭に入れておく必要があります。いくら利便性が高まるといっても、本来の責任範囲を逸脱した部分に手を出し始めてしまうと、そのうち収拾がつかなくなってしまいます。今回はUxUのテストケースでのニーズが発端ではありましたが、「FirefoxやThunderbirdのアドオンの自動テストを支援する」というUxUプロジェクト本来の目的を考えると、これはいささか外れたトピックと言わざるを得ません。「Firefoxで動作するスクリプトはテストケース中でも全部利用できること」「UxUが提供するユーティリティやアサーションが正常に機能すること」といった事柄は本来の目的から演繹できる責任範囲ですが、「Firefoxが対応しなくなったJavaScriptの文法上の拡張仕様に対応すること」を演繹することは難しく、UxUの責任の範囲外にあると言えます。

結論

以上を踏まえて、最終的に、UxUプロジェクトはE4Xの事実上の廃止に対しては「特に何もしない」という判断を下すことにしました*3

この結論に至るまでに、以下の要素が判断基準として登場しました。

  • 手間(コスト)はどれだけ必要か。
  • プロジェクト本来の目的を疎外しないか。
  • どれだけの場合に対応できるか。(100%すべての場合に対応できるのか、一部のエッジケースには対応できないのか。)
  • 追加・変更が必要なコードの量はどの程度か*4
  • プロジェクトの持続性にどれだけ影響するか。
  • プロジェクトの責任範囲に含まれるか。

ある点を突き詰めると別の点で問題が増大するという風に、判断基準同士の間にトレードオフの関係がある場合、すべての基準を満足させる選択肢というものは無いことになります。その時に重要になるのが、判断基準同士の中での優先度です。そのプロジェクトが何を大事にしているのか・何を大事にしていくのかという軸がはっきりしていればいるほど、判断のぶれは小さくなりますし、素早い判断が可能になります。

また、目先の問題や、最初のよく調べていない時点で仮に設定した目標に囚われてしまうと、そのまま泥沼に突っ込んで戻って来られなくなってしまうことがあります。今回の事例で言えば、「E4Xの仕様を完全に満たさないといけない」という意識に囚われたばかりに、字句解析処理の実装に着手したものの、いつまで経っても完成に辿り着けず、そのうちプロジェクト自体が頓挫してしまう、という「最悪のケース」に陥ってしまう可能性も十分にありました。

自分が今進もうとしている道の先にあるのが底なし沼なのかどうなのかを早めに見極めて、これは駄目だとなったら深手を負う前に引き返すことが大切です。そのためにも、何を大事にするのかという判断基準が必要になります。「そこは大事にしなくてもよい」ということが分かっていれば、引き返す判断を早めに下しやすいですし、埋没費用の発生もなるべく小さく抑えられます。

最良の選択を最初の時点ですぐに行い、それに向かって無駄なく一直線で進むのが、理想的といえば理想的です。しかし、最初の時点ですべてを知り尽くせていない事はままあります。今回も、調べ始めてみるまで「ソースコードの中で、どこがE4XのCDATAマーク区間なのかを判別する」ということがそこまでコストの高い事であるとは分かっていませんでした。最初にすべての方針を決めてまっすぐ邁進することに比べると、このような進め方は一見、無駄が多く不格好かもしれません。しかし、一度に投入できるリソースに厳しい制限がある場合には、その時々の目標設定を随時見直してスピーディーに方針を転換していく方が、リスクやコストを平滑化できるのではないでしょうか。

もちろんその時には、ぶれない大目的が存在していることが前提にあります。大目的無しに方針転換を繰り返すのは、ただの迷走です。目標は、目的に向かって進むための途中の道しるべに過ぎません。今回の例で言えば、「アドオンのコードを実際の環境に近い環境で簡単にテストできるテスティングフレームワークを提供すること」が目的で、「E4XのCDATAマーク区間を使ったヒアドキュメント的な機能を利用できること」は(仮の)目標です。目的のためには目標は動かしてもいいものです。その逆に、目標のために目的が犠牲になって(目的がぶれて)はいけません。

皆さんもプロジェクトを運営される際には、目的をはっきりと持ち、それに則った設計判断を下すよう心がけましょう。

付録:ユーザ側での、あるいは別プロジェクトとしての解決の方向性

なお、UxUプロジェクトとしては「責任の範囲外なので、この件についてはなにもしない」という態度を取るとしても、実際にUxUを利用してテストケースを書くユーザー開発者としては、何らかの対策がないと困ります。そこで、UxUとは別のレイヤーでこの問題を解決する方法についても検討しました。

ヒアドキュメント風の機能を実現する別のアプローチとして、文法を拡張するのではなく、JavaScriptの文法の範囲内で実現するという考え方があります。その実例が、cho45さんがNode.js用に開発されているnode-hereというライブラリです。

このライブラリは「ライブラリが提供するhere()という関数が呼び出された時のスタックトレースを参照して、ソース中の何文字目にあたる箇所で関数が呼ばれたのかを特定し、ソースを文字列として読み込んだ上でその箇所に書かれたコメントの内容を抽出して、それを文字列として返す」という処理を行うものです。スクリプトが実行される時点では純粋に単なる関数呼び出しでしかないため、これのせいで普通のスクリプトが動作しなくなるといった副作用もありません。Node.js(V8)用に開発されていますが、動作だけを見ればFirefox上でも同じようなことができるはずです*5

調査の結果、このアプローチにも限界があることは分かっています*6が、実用上はほぼ問題ないと考えられます*7

今回はUxUプロジェクトとしての対応は見送りましたが、このような機能が十分に小さなライブラリとして実装されるならば、標準添付のライブラリの1つとして含めるという選択もあり得るかもしれません*8

何かの機能を廃止したり、今まではできたことをできなくしたりすると、ユーザーからは必ず反発があります。有効な代替案がないままにただ「できなくなりました」とだけ言い放ってしまうと、ユーザーの不興を買ってしまい、ユーザー離れを引き起こしたり、古いバージョンを使い続けられてしまったりする事もあります*9。「なぜできなくなったのか」という事を説明したとしても、この点は変わりません*10。ですのでこのような場合には、ユーザーの負担を減らすためにも、何らかの形で有効な代替案を用意しておくか、案内をしておくのが望ましいでしょう。

*1 主に、データ駆動テストで利用することを想定しています。

*2 UxUの開発そのものは会社の収入に直結しない上に、その間、収入に繋がる他の業務もストップしてしまうため。

*3 もちろん、UxU自身のコードでE4Xを使っている箇所があれば、そのままではUxU自体が動作しなくなってしまうため、その点の修正は厭わない方針です。

*4 変更の量は少なくても、その後に致命的な悪影響を与える変更、というものもあるので、量だけが重要というわけではない。

*5 実際に、再起動不要なアドオンの開発用の簡易フレームワークRestartlessの一部としてhere.jsを実装しています。

*6 Node.jsではスタックトレースから行内のカラム位置を取得できるため、正確に関数呼び出しが行われた位置を把握できるのですが、Firefoxではスタックトレースから行番号までしか特定できないため、同じ行に複数のhere()が存在していた場合に、どのhere()が呼ばれたのかがわかりません。

*7 元々の目的が「複数行にわたる文字列をすっきり記述したい」という事であると考えれば、1行に複数のhere()が登場するようなケースは相当イレギュラーであると言えるため。最小の実装でほとんどの場合に対応できるのであればよしとする、という考え方に基づけば、この点は目をつぶることができる。

*8 例えば、非同期処理のテストを行う際の利便性向上のため、UxUにはJSDeferredを標準添付している。

*9 実際に、愛用しているアドオンが対応しているという理由からセキュリティ上の脆弱性がある古いバージョンのFirefoxを使い続けている人もいる。このような事態が発生すると、結果的にユーザーを危険に晒してしまうことになるため、非常によろしくない。

*10 背景事情など、ユーザーにとってはどうでもいいことなので。

タグ: Mozilla | UxU
2013-01-16

Ruby用単体テストフレームワークtest-unitでのデータ駆動テストの紹介

test-unitはRuby用のxUnit系の単体テストフレームワークです。2.3.1からデータ駆動テスト機能が追加されていたのですが、2.5.3まではリファレンスに記述がなく、知る人ぞ知る機能でした。

2013-01-23にリリースされた2.5.4ではデータ駆動テスト機能についてのドキュメントが追加されています。

データ駆動テスト自体の説明はUxUを用いたデータ駆動テストの記述を参照してください。

Cucumberのscenario outlinesに似ていると言えばピンと来る人もいるのではないでしょうか。 Cucumberのscenario outlinesも前述のククログ記事の通り、テストのデータとロジックを分離しているのでデータ駆動テストの一種と言えます。

今回は、データ駆動テストを導入した例を見ながらtest-unitでのデータ駆動テスト機能の使い方を紹介します。なお、以降の説明では「テスト対象のデータ」のことを「テストデータ」とします。

データ駆動テスト導入例

データ駆動テストの導入例としてBitClust*1での使い方を紹介します。

データ駆動テスト導入前のBitClustのnameutils.rbには次のようなテストメソッドがありました。test_nameutils.rb@r5333から一部を抜粋します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class TestNameUtils < Test::Unit::TestCase

  include BitClust::NameUtils

  def test_libname?
    assert_equal true, libname?("_builtin")
    assert_equal true, libname?("fileutils")
    assert_equal true, libname?("socket")
    assert_equal true, libname?("open-uri")
    assert_equal true, libname?("net/http")
    assert_equal true, libname?("racc/cparse")
    assert_equal true, libname?("test/unit/testcase")
    assert_equal false, libname?("")
    assert_equal false, libname?("fileutils ")
    assert_equal false, libname?(" fileutils")
    assert_equal false, libname?("file utils")
    assert_equal false, libname?("fileutils\n")
    assert_equal false, libname?("fileutils\t")
    assert_equal false, libname?("fileutils.rb")
    assert_equal false, libname?("English.rb")
    assert_equal false, libname?("socket.so")
    assert_equal false, libname?("net/http.rb")
    assert_equal false, libname?("racc/cparse.so")
  end
# ...省略
end

上記の抜粋箇所だけ取り出してテストを実行すると、結果はこのようになります*2

$ ruby test/run_test.rb -n /test_libname.$/ -v
Loaded suite test
Started
TestNameUtils: 
  test_libname?:                                        .: (0.000636)

Finished in 0.000982531 seconds.

1 tests, 18 assertions, 0 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications
100% passed

1017.78 tests/s, 18320.03 assertions/s

テストが1つだけ実行されています。この1つのテストの中で様々なデータに対するアサーションを実行しています。ただ、上のコードでは、どのようなデータに対してテストしているのかを知るためにはソースコードを確認する必要があります。

これをデータ駆動テスト機能を使用して書き換えると以下のようになります。コード全体は現在のtest_nameutils.rb@r5551を参照してください。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class TestNameUtils < Test::Unit::TestCase

  include BitClust::NameUtils

  data("_builtin"                       => [true, "_builtin"],
       "fileutils"                      => [true, "fileutils"],
       "socket"                         => [true, "socket"],
       "open-uri"                       => [true, "open-uri"],
       "net/http"                       => [true, "net/http"],
       "racc/cparse"                    => [true, "racc/cparse"],
       "test/unit/testcase"             => [true, "test/unit/testcase"],
       "empty string"                   => [false, ""],
       "following space"                => [false, "fileutils "],
       "leading space"                  => [false, " fileutils"],
       "split by space"                 => [false, "file utils"],
       "following new line"             => [false, "fileutils\n"],
       "folowing tab"                   => [false, "fileutils\t"],
       "with extension .rb"             => [false, "fileutils.rb"],
       "CamelCase with extension .rb"   => [false, "English.rb"],
       "with extension .so"             => [false, "socket.so"],
       "sub library with extension .rb" => [false, "net/http.rb"],
       "sub library with extension .so" => [false, "racc/cparse.so"])
  def test_libname?(data)
    expected, target = data
    assert_equal(expected, libname?(target))
  end
# ...省略
end

上記の抜粋箇所だけ取り出してテストを実行すると、結果はこのようになります。

$ ruby test/run_test.rb -n /test_libname.$/ -v
Loaded suite test
Started
TestNameUtils: 
  test_libname?[_builtin]:                              .: (0.000591)
  test_libname?[fileutils]:                             .: (0.000389)
  test_libname?[socket]:                                .: (0.000365)
  test_libname?[open-uri]:                              .: (0.000354)
  test_libname?[net/http]:                              .: (0.000355)
  test_libname?[racc/cparse]:                           .: (0.000354)
  test_libname?[test/unit/testcase]:                    .: (0.000349)
  test_libname?[empty string]:                          .: (0.000397)
  test_libname?[following space]:                       .: (0.000346)
  test_libname?[leading space]:                         .: (0.000343)
  test_libname?[split by space]:                        .: (0.000346)
  test_libname?[following new line]:                    .: (0.000353)
  test_libname?[folowing tab]:                          .: (0.000344)
  test_libname?[with extension .rb]:                    .: (0.000344)
  test_libname?[CamelCase with extension .rb]:          .: (0.000347)
  test_libname?[with extension .so]:                    .: (0.000343)
  test_libname?[sub library with extension .rb]:        .: (0.000346)
  test_libname?[sub library with extension .so]:        .: (0.000345)

Finished in 0.007920974 seconds.

18 tests, 18 assertions, 0 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications
100% passed

2272.45 tests/s, 2272.45 assertions/s

アサーションの数は修正前と同じですが、テストの数が修正前よりも増えています。具体的には「1 tests」から「18 tests」に増えています。また、実行結果にテストしたテストデータの名前が表示されるようになったので、どのようなテストデータに対してテストを実行したのかを実行時にも確認できます。

dataとload_dataの使い方

テストデータを登録するためにはdataメソッドまたはload_dataメソッドを使います。それぞれのメソッドの使い方を説明します。

dataメソッドとload_dataメソッドはTest::Unit::TestCaseに定義されている特異メソッドです。publicprivateのようにメソッド定義の直前に書いて使用します。例えば、以下のように書きます。

1
2
3
4
5
6
class TestDataDrivenTest < Test::Unit::TestCase
  data("...")
  def test_xxx(test_data)
    # ...
  end
end

dataメソッドの使い方には次の三種類があります。

  • data(label, data)
  • data(data_set)
  • data(&block)

load_dataメソッドの使い方は次の一種類だけです。

  • load_data(file_name)

それぞれの使い方を順に説明します。

data(label, data)

labelにはテストデータの名前を指定します。dataにはテストデータとして任意のオブジェクトを指定します。ここに指定したオブジェクトがテストメソッドにそのまま渡されます。

1
2
3
4
5
6
7
8
9
10
require "test-unit"

class TestData < Test::Unit::TestCase
  data("empty string", [true, ""])
  data("plain string", [false, "hello"])
  def test_empty?(data)
    expected, target = data
    assert_equal(expected, target.empty?)
  end
end

この例ではテストデータを配列で指定していますが、複雑なデータを渡すときは、Hashやテストで使いやすいようにラップしたオブジェクトを使うとテストコードが読みやすくなります。

data(data_set)

data_setにはテストデータの名前をキー、テストデータを値とする要素を持つHashを指定します。この使い方の場合は、Hashの各要素の値がテストメソッドにそのまま渡されます。

1
2
3
4
5
6
7
8
9
10
require "test-unit"

class TestData < Test::Unit::TestCase
  data("empty string" => [true, ""],
       "plain string" => [false, "hello"])
  def test_empty?(data)
    expected, target = data
    assert_equal(expected, target.empty?)
  end
end
data(&block)

ブロックでテストデータを生成することもできます。

ブロックはテストデータの名前をキー、テストデータを値とする要素を持つHashを返すようにします。ランダムな値を生成するテストや、網羅的な値を生成して使うテストが書きやすくなります。外部からテストデータを読み込んで使うようなテストも書きやすくなるでしょう。

以下のようにテストデータの生成部分とテストのロジック部分を独立して書くことができるので、テストが書きやすくなります。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
require "test-unit"

class TestData < Test::Unit::TestCase
  data do
    data_set = {}
    data_set["empty string"] = [true, ""]
    data_set["plain string"] = [false, "hello"]
    data_set
  end
  def test_empty?(data)
    expected, target = data
    assert_equal(expected, target.empty?)
  end
end

最初に紹介したnameurils.rbのテストでも網羅的なテストを実行するためにこの機能を使用しています。興味のある人はtest_typemark?test_typechar?を見てください。

load_data(file_name)

load_dataメソッドは外部のファイルからデータを読み込みます。

load_dataはファイルの拡張子によって、ファイル形式を自動的に判断してデータを読み込みます。現在の最新版であるtest-unit-2.5.4では、CSVとTSVに対応しています。

例えば、次の表のようなtest-data.csvという名前のCSVファイルを用意します。

labelexpectedtarget
empty stringtrue""
plain stringfalsehello

ヘッダーの最初の要素(一番左上の要素)は必ず「label」にしてください。

CSVファイルだと以下のようになります。

label,expected,target
empty string,true,""
plain string,false,hello

このCSVファイルを使って書いたテストコードはこのようになります。このファイルをtest-sample.rbとします。

1
2
3
4
5
6
7
8
require "test-unit"

class TestData < Test::Unit::TestCase
  load_data("test-data.csv")
  def test_empty?(data)
    assert_equal(data["expected"], data["target"].empty?)
  end
end

実行結果はこのようになります。

$ ruby test-sample.rb -v
Loaded suite TestData
Started
test_empty?[empty string]:                              .: (0.000572)
test_empty?[plain string]:                              .: (0.000424)

Finished in 0.001337316 seconds.

2 tests, 2 assertions, 0 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications
100% passed

1495.53 tests/s, 1495.53 assertions/s

CSVファイルを使ったこのテストコードは、以下のように書いたテストコードと同じテストになります。

1
2
3
4
5
6
7
8
9
10
# test-sample.rb
require "test-unit"

class TestData < Test::Unit::TestCase
  data("empty string" => {"expected" => true,  "target" => ""},
       "plain string" => {"expected" => false, "target" => "hello"})
  def test_empty?(data)
    assert_equal(data["expected"], data["target"].empty?)
  end
end

また、次のようなヘッダーのないCSVファイルにも対応しています。一番左上の要素が「label」にならないように注意してください。「label」となっていると最初の行をヘッダーとみなします。

empty stringtrue""
plain stringfalsehello

CSVファイルだと以下のようになります。

empty string,true,""
plain string,false,hello

この場合は、次のようなテストコードになります。

1
2
3
4
5
6
7
8
9
10
# test-sample.rb
require "test-unit"

class TestData < Test::Unit::TestCase
  load_data("test-data.csv")
  def test_empty?(data)
    expected, target = data
    assert_equal(expected, target.empty?)
  end
end

実行結果はこのようになります。

$ ruby test-sample.rb -v
Loaded suite TestData
Started
test_empty?[empty string]:                              .: (0.000584)
test_empty?[plain string]:                              .: (0.000427)

Finished in 0.001361219 seconds.

2 tests, 2 assertions, 0 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications
100% passed

1469.27 tests/s, 1469.27 assertions/s

このようなCSVファイルを読み込んだ場合はdata(data_set)の例と同じように解釈されます。サンプルコードを再掲します。

1
2
3
4
5
6
7
8
9
10
require "test-unit"

class TestData < Test::Unit::TestCase
  data("empty string" => [true, ""],
       "plain string" => [false, "hello"])
  def test_empty?(data)
    expected, target = data
    assert_equal(expected, target.empty?)
  end
end

CSVファイルやTSVファイルでテストデータを作成できると、テストデータの作成に表計算ソフトやデータ生成用スクリプトを利用できます。そのため、たくさんのパターンのテストケースを作成しやすくなります。ただし、テストデータは多ければ多いほどよいというものではないことに注意してください。テストデータが多くなるとその分テスト実行時間が長くなり、テスト実行コストが高くなります。テストデータを作りやすくなったからといって、必要以上にテストデータを作らないようにしましょう。

まとめ

test-unitでのデータ駆動テスト機能について紹介しました。いろいろなパターンがあるテストをメンテナンスしやすい状態に保つために、データ駆動テスト機能を使ってみてはいかがでしょうか。

*1 るりまプロジェクトで使用しているドキュメント生成ツール。

*2 test/run_test.rbはtest-unitでテストを実行するためのスクリプトです。

タグ: Ruby | テスト
2013-01-23

社宅制度による社会保険料と所得税の削減

はじめに

来年4月の消費税率引き上げを前に、政府では軽減税率の導入や低所得層向けに現金給付する案などが検討されています。しかし、消費税負担額の増加は不可避な状況です。さらに健康保険、厚生年金といった社会保険料もその料率が年々引き上げられており、庶民の負担は増える一方です。

そのような状況において、会社としては社員の負担を軽減したいところです。ただ、給与の引き上げは追加のコストが発生するため容易ではありません。しかし、社宅制度を導入すると、追加のコスト無しに社員の可処分所得を増やすことができます。

以前紹介した退職金共済制度も可処分所得を増やす仕組みではありますが、社員が可処分所得の増加を実感できるのは退職金を受け取った時であり、在職中はメリットを感じられません。しかし、今回紹介する社宅制度は社宅契約時から社員の費用負担を軽減し、さらに毎月の可処分所得が増加することからメリットを感じてもらいやすい制度です。

今回はクリアコードが導入している社宅制度を紹介するとともに、具体例からその金銭的メリットを紹介していきます。

クリアコードの社宅制度

クリアコードの社宅制度は、いわゆる借り上げ社宅を対象としたものです。借り上げ社宅とは、貸主と会社が賃貸契約した不動産を、会社が社員に住宅として提供するものです。クリアコードでは入社や結婚による転居が生じるときに社宅制度*1が利用可能です。

社宅制度の内容は、ここでは費用についてのみ取り上げます。

この制度では、社宅契約時に発生する敷金、礼金、仲介手数料、火災保険料を会社が全額を負担します。毎月の家賃は全額を社員が負担しますが、負担方法がすこし変わっています。家賃の50%から55%を基本給から減額し、50%を社宅家賃として給与から天引きします。家賃を基本給の減額と社宅家賃の天引きに分割して社員が負担することがこの社宅制度の肝で、これが社員の可処分所得の増加につながります。では、なぜ2つに分けて家賃を負担させるのかというと、それは社宅に関する所得税の取り扱いによるものです。

社宅に対する所得税の取り扱い

会社が役員や社員に対して社宅を提供するとき、一定額以上の家賃を受け取っていれば、社員は給与として課税されません。逆に、一定額未満の家賃しか受け取っていない場合は、一定額と家賃の差額は社員に対する給与とみなされ所得税が課税されます。この一定額の算出方法は、固定資産税評価額から算出する方法などいくつか選択できます。クリアコードでは、家賃の50%を一定額としています*2

国税庁が提供している社宅に関する情報は以下の通りです。

事例でみる社宅契約の効果

では、実際に個人で契約した場合と社宅契約とした場合に、社員の家賃支払い後の手取り給与額がどれだけ違ってくるか、以下の例で確認します。

月給40万円の社員が次の物件に引越すものとします。

家賃100,000円
仲介手数料105,000円
礼金100,000円
火災保険料(2年)25,000円
個人で契約した場合の手取り給与額

個人で契約した場合、手取り給与額は月額331,342円となります。

月給400,000円
社会保険料▲56,808円
[内訳]健康保険料20,438円
厚生年金保険料34,370円
雇用保険料2,000円
源泉所得税▲11,850円
差引支給額331,342円

不動産の費用として、契約時に仲介手数料、礼金、火災保険料の合計230,000円を支払います。毎月の家賃として、100,000円を支払います。

一般的な賃貸契約の期間である2年間で家賃支払い後の手取り給与額を計算すると、以下の通り5,332,208円となります。

(331,342円 * 24) - 230,000円 - (100,000円 * 24) = 5,322,208円
社宅契約にした場合の手取り給与額

社宅契約にした場合は、基本給を50,000円減額して350,000円とします。また社宅家賃として50,000円を給与天引きします。なお、不動産契約時の費用はすべて会社が負担します。このとき、給与の手取り額は241,705円となります。

月給350,000円
社会保険料▲49,875円
[内訳]健康保険料17,946円
厚生年金保険料30,179円
雇用保険料1,750円
源泉所得税▲8,420円
社宅家賃▲50,000円
差引支給額241,705円

2年間の家賃支払い後の手取り給与額は、以下の通り5,800,920円となります。

241,705円 * 24 = 5,800,920円
家賃支払い後の手取り給与額の比較

家賃支払い後の手取り給与額は、個人で契約した場合は5,322,208円ですが、社宅契約した場合は5,800,920円となり、478,712円も多くなります。つまり、社宅契約にすると、1ヶ月あたり2万円近く手取り給与額が増えます。これは不動産契約時の初期費用を会社が負担し、また給与を低くすることによって社会保険料や所得税*3が抑えられるためです。

続いて、会社の負担額について確認します。不動産契約に関して、支払った費用と人件費の合計額で比較します。

個人で契約した場合の会社負担額

個人で契約した場合は、不動産に関する費用は発生しません。給与と法定福利費の合計を人件費として計算することにします。

給与400,000円
法定福利費60,024円
[内訳]健康保険料20,438円
厚生年金保険料34,370円
雇用保険料3,400円
労災保険料1,200円
児童手当拠出金616円
合計460,024円

  2年間の合計額は以下の通り11,040,576円となります。

460,024円 * 24 = 11,040,576円
社宅契約にした場合の会社負担額

社宅契約にした場合、社宅関連の費用として契約時に230,000円を負担します。毎月の家賃として100,000円を負担します。2年間の合計は以下の通り2,630,000円となります。

230,000円 + (100,000円 * 24) = 2,630,000円

人件費は、給与と法定福利費の合計から社宅家賃の受取額を差し引きます。

給与350,000円
法定福利費52,689円
[内訳]健康保険料17,946円
厚生年金保険料30,179円
雇用保険料2,975円
労災保険1,050円
児童手当拠出金539円
社宅家賃▲50,000円
合計352,689円

2年間の合計額は、以下の通り11,094,539円となります。

2,630,000円 + (352,689円 * 24) = 11,094,539円

計算の結果、社宅契約にすると会社側の負担額が53,963円増えることがわかりました。これは1ヶ月に換算すると2,248円の負担増です。もし、基本給から減額する金額を2,300円増やして52,300円とすれば会社の負担はなくなります。このとき、社員はもともと2万円近く手取り給与が増える計算なので、基本給が2,300円減ってもなお大きなメリットがあります。こうすれば、会社も個人もメリットが得られます。

その他のメリットデメリット

金銭的なメリットについては、紹介したとおりですが、デメリットがないわけではありません。

例えば社員にとっては、「会社をやめた時に個人契約に切り替える手続きが発生する」というデメリットがあります。また基本給が減額されますので、基本給から算出される残業代は少なくなります。社会保険料の負担額が少なくなっていますので、将来受け取る年金額や失業手当が減ります。

一方、会社にとっては、賃貸物件について何らかのトラブルが発生した場合の保証リスクが発生します。また不動産契約を管理するための事務コストが発生します。

まとめ

社宅制度を使った社会保険料と税金の削減策を紹介しました。もうすぐ新入社員が入ってくる時期です。一人暮らしをはじめるにあたって家を借りるとなると金銭的な負担も小さくありません。これからの会社を担う新入社員のために、社宅制度の導入を検討してみてはいかがでしょうか。

*1 社宅管理規程を定めて制度を導入するのが一般的です。

*2 固定資産税の課税標準額から算出すれば、社員の支払うべき家賃を減らすことができ、より一層のコストメリットが得られます。しかし、会社側の事務コストも増加します。

*3 給与が抑えられることによって住民税も減ります。ただし、住民税は前年の所得に対して課税されることから、効果があがるのが翌年となるため計算には含めていません。

タグ: 会社
2013-01-30

«前月 最新記事 翌月»
タグ:
年・日ごとに見る
2008|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
2018|01|02|03|04|05|06|07|08|09|10|11|12|
2019|01|02|03|04|05|06|07|08|09|10|11|12|
2020|01|02|03|04|05|06|07|08|09|10|11|12|