2015年8月11日にピクシブさん会場提供でピクシブの開発者(7名)と永和システムマネジメントの開発者(1名)に参加していただき、「リーダブルコードワークショップ」を開催しました。
このワークショップは「チームメンバーのリーダブルコードを見つけて共有」することを体験する内容になっています。体験した内容が有益だと思ったらチームに取り入れていって欲しい、という趣旨です。
この内容は「メンバーのリーダブルコードを見つけて共有することが習慣になっているチームはリーダブルコードが当たり前になっていく」という仮説をベースとしています。図にすると次の通りです。
メンバーのリーダブルコードを見つけるためにはメンバーのコードを読まないといけません。書く側は「メンバーに読まれている」とわかっていればリーダブルコードを書こうという気持ちが強くなり、リーダブルコードが増えていきます。リーダブルコードが増えると見つかるリーダブルコードも増えていきます。
自分が見つけたリーダブルコードは、次に自分が書くコードに取り入れます。チームの誰かがリーダブルコードを書くことにより、リーダブルコードが徐々に他のメンバーにも共有されていきます。新しいリーダブルコードの書き方を見つけた・編み出したらそれを使ってコードを書く。リーダブルコードを書くことによりチームでリーダブルコードを共有できる。そんなフィードバックループです。
このフィードバックループが回り始めると「リーダブルコードが当たり前のチーム」になっていくのではないかということです。
5時間程度でこのフィードバックループを1周体験するのがこのワークショップです。今後、このワークショップをいろんなチームに提供していくべきかどうか、有用性を確認するためにピクシブさんと永和システムマネジメントさんに協力してもらいました。後述する通り、貴重なフィードバックをもらいました。ありがとうございます。
参加者視点でどうだったかについては、ピクシブさんがまとめてくれています。興味のある方はチームにとってのリーダブルコード - pixiv insideもあわせてご覧ください。
どうしてこの内容のワークショップを提供しようと検討しているかについて、簡単に背景を説明します。
リーダブルコードの解説を書いてからは「リーダブルコード(=読む人が読みやすいコード)」というテーマでコードについて他の人に伝える機会が増えました。
クリアコードは社名の通り「クリアなコード(=書いた人の意図が明確なコード)」を大事にしています。「クリアなコード」と「リーダブルコード」はどちらも大事にしていることは同じです。読んで理解できるコードです。
「クリアなコード」を大事にするというのは、元々は「自分たちが書くコードはクリアなコードにしよう!」という気持ちでした。しかし、「リーダブルコード」というテーマでコードについて他の人に伝える機会が増えるうちに「他の人たちがクリアなコード・リーダブルコードを書くことも手伝いたい!」という気持ちになってきました。
開発は実際に手を動かす作業がたくさんあります。説明を聞いて頭で理解するだけよりも、実際に体験して体でも感じられた方が、より多くのことを伝えることができそうです。そのため、ワークショップの提供を検討しています。
ワークショップ開催前にピクシブさんからヒアリングをしました。こちらが想定している内容が参加予定の開発者にとって有用そうかを検討するためです。
ヒアリングの結果、想定していた内容を変えて、もっと突っ込んだ内容にした方がよいことがわかりました。想定よりも対象者のレベルが高かったのです。(ヒアリングしてよかったです。)
最終的にワークショップの対象者は次のような開発者にしました。
ざっくりとまとめると、自分はリーダブルコードを書ける自立した開発者で、チーム全体でも同様のことを実現したい人です。
ワークショップは次のような内容にしました。
体験が3つにわかれているのは、1人→2人→3人以上というように段階的に体験するためです。
「個人で開発を体験」するフェーズでは「後で他のメンバーがリーダブルコードを探すために読む」という前提で開発します。それを意識して「リーダブルコード」を書きます。これは、フィードバックループでいう「読まれるしリーダブルコードを書こう!」という部分の体験になります。
「ペアで開発を体験」するフェーズでは「個人で開発を体験」するフェーズで開発したコードの中からリーダブルコードを見つけて共有します。これは、フィードバックループでいう「リーダブルコードはないかな?」と「お、リーダブルコードだなぁ。自分が書く時に取り入れよう!」の部分の体験になります。
「チームで開発を体験」するフェーズでは参加者全員(今回は8人)のコードの中からリーダブルなコードを見つけて共有します。これは、これまで個人(1人)・ペア(2人)でやったことをチーム(3人以上)にスケールさせる体験です。
これらの内容を通じてフィードバックループを1周体験し、「メンバーのリーダブルコードを見つけて共有」が自分たちのチームにとって割にあうか(コストよりメリットが大きいか)を判断してもらおうという狙いです。
なお、内容の詳細はGitHub上で公開しており、CC BY-SA 4.0のライセンスで自由に利用できます。
チームにとってのリーダブルコード - pixiv insideの「感想」のところにもある通り、参加者のみなさんから貴重なフィードバックをもらいました。ありがとうございます。
開催側からの視点として整理します。
普段はレビューを通して、良くない部分や直すべき部分を指摘する・される事が多いのですが、今回の*「良い部分を共有する」という体験は新しく*、それを意識づけるきっかけができて良かったです。
この感想をもらえたことは、このワークショップの内容が開催側の狙いを実現するための内容として適切だったということと捉えました。
最近は、コードを読むというと「コードレビュー」としてコードを読むという場面を思い浮かべることが多そうです。Webの記事や雑誌の記事でコードレビューについて書かれていることが多いからです。コードレビューは「コードに問題がないことを確認する」ことが1番の目的です。コードを共有する側面もありますが、それは副次的な目的です。
一方、このワークショップでの読み方の1番の目的は「リーダブルコードを見つけて共有する」ことです。問題が見つかることもありますが、それは副次的な効果です。
目的が違えば読み方も変わります。
「問題がないことを探す」ときは漏れがないか、特殊なケースでも大丈夫か、などといった観点で読むことでしょう。言い方はアレですが、少しイジワルな視点で読むということです。
「リーダブルコードを探す」ときは「自分がすぐに理解できたか」を意識して読まないといけません。意識しないと見つけられないからです。(意識しなくてもすぐに理解できるようなコードがリーダブルコードです。)「いいこと探し」視点で読むということです。
この違いを体験できたという感想をもらえたので、ワークショップの内容として方向性は間違っていなそうです。
レビューする側もされる側も人間なので、指摘を通してある種のすまない気持ちや否定された気持ちを持ってしまう事があるのですが、良い部分を共有するという行為を習慣化する事で、コードのためだけではなくチームメンバーの感情のためにも良い影響がありそう。
今回のワークショップは技術的な理由だけから「チームメンバーのリーダブルコードを見つけて共有」を体験する内容にしたので、感情的な側面については考えていませんでした。たしかに感情的な側面でもよい効果がありそうなので、新しい視点を得られた貴重なフィードバックでした。
なにかしら仕組みを作ることで感情的な側面の効果をより活かすことができそうです。GitHubベースで考えるなら、GitHubではコミットやコードに対してコメントをつけることができるので、よい部分を見つけたらコメントしておき、別途用意した「よい部分を見つけた!」コメントを収集して一覧表示するツールを使ってチームで共有するという仕組みを思いつきました。
他にも、すでにコードレビューでうまく回っているチームならこのワークショップは必要ないかもという知見も得られました。
クリアコードは「チームメンバーのリーダブルコードを見つけて共有」を体験する「リーダブルコードワークショップ」の提供を検討しています。
ピクシブさんと永和システムマネジメントさん協力のもとこのワークショップの有用性を検証してみました。それぞれの開発者が自立してリーダブルコードを書いているが、チーム全体の取り組みにはまだ届いていないというチームには有用そうです。
「チームメンバーのリーダブルコードを見つけて共有」するという取り組みは技術面だけでなく、チームの感情的な側面で有効かもしれないという仮説が得られました。
ピクシブさん・永和システムマネジメントさんご協力ありがとうございました。
自分たちのチームでも「チームメンバーのリーダブルコードを見つけて共有」を体験して、チームでリーダブルコードのフィードバックループを回すか検討する材料にしたい、という方はお問い合わせください。1回10万円(最大8名)で「リーダブルコードワークショップ」を提供します。
RedPen という技術文書の校正に使うことを目的としたソフトウェアがあります。
コマンドラインから RedPen を起動して文章をチェックしたり、RedPen サーバを稼動させ、REST API 経由で使うこともできます。
RedPen そのものの詳しい説明は、開発者による連載記事があるのでそちらを参照するとよいでしょう。
今回は、RedPen 1.3 からサポートされた、JavaScript による Validator を書く方法を紹介します。
RedPen の リリースページ からこの記事を書いている時点の最新版である RedPen 1.3 をダウンロードします。
ダウンロードできたら任意のディレクトリに展開します。RedPen を利用するには Java 8 の実行環境をあらかじめセットアップしておきます。
以下のように redpen
コマンドを正常に実行できたら準備は完了です。
% ./redpen-distribution-1.3/bin/redpen -v
1.3.0
RedPen で文章をチェックするには、設定ファイル(XML)と対象となるテキストを用意します。
設定ファイルには、どのルール(Validator)を使って対象となる文章をチェックするかを指定します。 設定ファイルを指定しなかった場合には、デフォルトのルールが適用されます。
配布物にサンプルのテキストが含まれているので、それを使ってどんなふうにチェックできるかがわかります。
% ./redpen-distribution-1.3/bin/redpen redpen-distribution-1.3/sample-doc/ja/sampledoc-ja.md
例えば、上記のテキストだと次のようなエラー(一部抜粋)を検出できます。
sampledoc-ja.md:2: ValidationError[SentenceLength], 文長("101")が最大値 "100" を超えています。 at line: 最近利用されているソフトウェアの中には複数の計算機上で動作(分散)するものが多く存在し、このような分散ソフトウェアは複数の計算機で動作することで大量のデータを扱えたり、高負荷な状況に対処できたりします。
sampledoc-ja.md:3: ValidationError[InvalidSymbol], 不正なシンボル "," がみつかりました。 at line: 本稿では,複数の計算機(クラスタ)でで動作する各サーバーを**インスタンス**と呼びまます。
sampledoc-ja.md:3: ValidationError[KatakanaEndHyphen], カタカナ単語 "サーバー" に不正なハイフンが見つかりました。 at line: 本稿では,複数の計算機(クラスタ)でで動作する各サーバーを**インスタンス**と呼びまます。aEndHyphen],
RedPen の使い方がわかったところで、今度は実際に Validator を書いてみましょう。
Validator はどこに実装したものを配置すればいいのでしょうか。RedPen で Validator の置き場所を指定するには、2つ方法があります。
REDPEN_HOME
環境変数で配置先のパスを指定するJavaScriptValidator
の script-path
で配置先のパスを指定するREDPEN_HOME
を指定するときは、実際には $REDPEN_HOME/js
以下から RedPen が Validator を探します。
Validator で実装すべきものについては、RedPen のソースコードのコメントにサンプルがあります。
var message = "your sentence has validation error : {0}";
function preValidateSentence(sentence) {
}
function preValidateSection(section) {
}
function validateDocument(document) {
// your validation logic for document here
}
function validateSentence(sentence) {
// if(your validation logic for sentence here) {
// addValidationError(sentence, 'specific message');
// }
}
function validateSection(section) {
// your validation logic for section here
}
いろいろありますが、文のチェックで最低限必要なのは、message
と validateSentence
の実装です。
var message = "your sentence has validation error : {0}";
function validateSentence(sentence) {
// if(your validation logic for sentence here) {
// addValidationError(sentence, 'specific message');
// }
}
バリデーション処理で問題のある箇所を見付けたら、addValidationError(sentence, 'specific message');
を呼びます。
RedPen では、Kuromoji を単語の分割に使用しています。
validateSentence
内で sentence.tokens.forEach(print);
などとして分割されたトークン *1 を出力すると、どのように分割されたかがわかります。
例えば、次のテキストがどのように分割されるかみてみましょう。
% cat ranuki.md
# ら抜き言葉
お刺身を食べれない。
次のようなトークンとして分割されていることがわかります。
TokenElement{surface='お', offset=0, tags=[接頭詞, 名詞接続, *, *, *, *, お, オ, オ]}
TokenElement{surface='刺身', offset=1, tags=[名詞, 一般, *, *, *, *, 刺身, サシミ, サシミ]}
TokenElement{surface='を', offset=3, tags=[助詞, 格助詞, 一般, *, *, *, を, ヲ, ヲ]}
TokenElement{surface='食べ', offset=4, tags=[動詞, 自立, *, *, 一段, 未然形, 食べる, タベ, タベ]}
TokenElement{surface='れ', offset=6, tags=[動詞, 接尾, *, *, 一段, 未然形, れる, レ, レ]}
TokenElement{surface='ない', offset=7, tags=[助動詞, *, *, *, 特殊・ナイ, 基本形, ない, ナイ, ナイ]}
TokenElement{surface='。', offset=9, tags=[記号, 句点, *, *, *, *, 。, 。, 。]}
surface
が実際に分割された文字で、Kuromojiによる形態素解析結果を tags
に保持していることがわかります。
テキストがどのように分割されるのか、そして形態素解析の結果がどのように保持されているかがわかったところで、いわゆる「ら抜き言葉」をチェックしてみましょう。 厳密なチェックまでは踏み込まず、一段活用の未然形に対する接尾というとても簡略なチェックだけをします。厳密ではないので誤検出もありますが、Validator のサンプルなのでそれでよいものとします。
具体的な実装例は次のとおりです。
var message = "your sentence has validation error : {0}";
function isTargetVerb(token) {
if (token.tags[0] == '動詞' &&
token.tags[1] == '自立' &&
token.tags[4] == '一段' &&
token.tags[5] == '未然形') {
return true;
} else {
return false;
}
}
function isRaRemovedWord(token, token2) {
if (isTargetVerb(token) &&
token2.tags[0] == '動詞' &&
token2.tags[1] == '接尾' &&
token2.tags[6] == 'れる') {
return true;
} else {
return false;
}
}
function validateSentence(sentence) {
for (var i = 0; i < sentence.tokens.length; i++) {
if (i + 1 < sentence.tokens.length &&
isRaRemovedWord(sentence.tokens[i],
sentence.tokens[i + 1])) {
addValidationError(sentence, 'ら抜き言葉を使用しています。');
//sentence.tokens.forEach(print);
}
}
}
こうして実装した Validator を試してみましょう。JavaScriptValidator を使うための設定ファイルを用意します。
<redpen-conf lang="ja">
<validators>
<validator name="JavaScript" />
</validators>
</redpen-conf>
あとは、設定ファイルとサンプルテキストを指定して RedPen を実行します。
% ./redpen-distribution-1.3/bin/redpen -c js.xml ranuki.md
[2015-08-29 15:16:02.165][INFO ] cc.redpen.Main - Configuration file: /home/kenhys/work/java/redpen/js.xml
[2015-08-29 15:16:02.169][INFO ] cc.redpen.ConfigurationLoader - Loading config from specified config file: "/home/kenhys/work/java/redpen/js.xml"
[2015-08-29 15:16:02.176][INFO ] cc.redpen.ConfigurationLoader - Succeeded to load configuration file
[2015-08-29 15:16:02.176][INFO ] cc.redpen.ConfigurationLoader - Language is set to "ja"
[2015-08-29 15:16:02.176][WARN ] cc.redpen.ConfigurationLoader - No type configuration...
[2015-08-29 15:16:02.177][INFO ] cc.redpen.ConfigurationLoader - No "symbols" block found in the configuration
[2015-08-29 15:16:02.222][INFO ] cc.redpen.config.SymbolTable - "ja" is specified.
[2015-08-29 15:16:02.222][INFO ] cc.redpen.config.SymbolTable - "normal" type is specified
[2015-08-29 15:16:02.720][INFO ] cc.redpen.parser.SentenceExtractor - "[。, ?, !]" are added as a end of sentence characters
[2015-08-29 15:16:02.720][INFO ] cc.redpen.parser.SentenceExtractor - "[’, ”]" are added as a right quotation characters
[2015-08-29 15:16:02.730][INFO ] cc.redpen.validator.JavaScriptValidator - JavaScript validators directory: /home/kenhys/work/java/redpen/js
ranuki.md:3: ValidationError[JavaScript], [ra-removed-word.js] your sentence has validation error : ら抜き言葉を使用しています。 at line: お刺身を食べれない。
期待通りに、食べ「れ」ないという「ら抜き言葉」を検出することができました。
RedPen の Validator を JavaScript で書く方法を紹介しました。 RedPen 1.3 からは JavaScript でバリデーションを簡単に書くことができます。 これまでなら Validator を Java で実装するしかなかったので、大分敷居が下がったのではないでしょうか。
その後、開発者の方から記事に関するフィードバックをもらいました。 「次のバージョン(1.4?)ではvar message=、みたいな定数を定義せずゴリっとエラーメッセージをハードコードできるようになりました。お試しくださいませ!」とのことです。
var message
の定義が不要になり、エラーメッセージには addError
を呼ぶスタイルになるようです。
*1 RedPen では TokenElement として表現される。
クリアコードはフリーソフトウェアの開発で得た知見を活かして受託開発(やサポート業務や開発支援など)をしています。
フリーソフトウェアの開発は「やりたいことベース」あるいは「必要なことベース」で開発することが多いです。その開発スタイルでは「期日」という概念が希薄です。定期的にリリースする開発スタイルを取り入れているプロジェクトもありますが、「リリース日までに○○を実現するぞ!」というよりは「リリースに間に合わなかったら○○という機能は次のリリースに持ち越しだな」という感じです。
しかし、仕事として開発する場合は「期日」という概念が希薄なことは致命的です。約束したものを約束した日時までに提供できない可能性が高くなるからです。提供できない場合は対価を得られませんし、次の仕事にもつながりません。(約束を守らない会社と契約したいか考えてみてください。)
「約束を守れない」という事態を未然に防ぐ方法として、「進捗を共有する」という方法があります。「朝会」として毎朝進捗を共有しているチームもあるでしょう。
進捗を共有することで、当事者が「約束を守れない」という事態になりそうだと気づいていない場合でも、まわりの人が気づいて対策を講じることができます。
補足:「進捗を共有する」ことは、「約束を守れない」という事態を未然に防ぐ、という防御を目的にしたときだけ利用できることではありません。共有してお互いの協力を促進するという目的のために使うこともできるでしょう。他にもいろいろな目的のために使うことができますが、ここでは、「約束を守れない」という事態を未然に防ぐ、という目的に使うという話です。
前置きが長くなりましたが、このような情報を進捗として共有すれば「約束を守れない」という事態を未然に防ぐことに役立つ、という情報を説明します。
「約束を守れない」という事態に気づきやすくするために次の4つの情報を共有します。
それぞれの関係(+α)を図にすると次のようになります。
「スタート」からはじめて、「期日」までに「目的」の「成果」を実現することを目指します。
「現状」は「スタート」からどのくらいの「期間」が「使用済み」になったかと、今の時点での「成果」です。
「今後」は今の時点からどのように「目的」に向かっていくかの方向性と「使用予定」の「期間」です。
「現状」と「今後」を材料に、「残り」の「期間」で「目的」に到達できそうかを検討することで「約束を守れない」という事態に気づきます。
他の人が到達できなそうと気づいたら約束を守れそうか当事者に聞いてみます。他の人は、異変に気づくために次のことに注目します。
これらに疑問を感じるときは当事者に声をかけます。
具体例を示します。Groongaという全文検索エンジンの管理用Web UIを開発するという仕事をしているとします。
当事者に声をかけた方がよさそうな例です。
8月31日の進捗:
- Groongaの管理Web UIの開発
- 昨日から検索UIを実装し始めた。今日も引き続き検索UIを実装する。
大丈夫そうな例です。
8月31日の進捗:
- Groongaの管理Web UIの開発
- 目的:Groongaのコマンドの詳細を知らなくてもユーザーがデータベースを操作できるようになること
- 期日:2015年9月4日18時
- 現状:
- 使用済み期間:3日(全部で5日)
- 成果:検索UI以外のUIは完成した。検索UIは昨日から着手している。
- 今後:
- 今日1日で基本的な検索UIを完成させる。
- 残りの1日で初心者ユーザーでも使えるように改良して目的達成を目指す。
それでは個別にそれぞれの項目について説明します。
「目的」はこの開発が始まったときに決まっているはずです。目的は明示的に変えない限りそのままです。つまり、目的を忘れないことが大事です。
フリーソフトウェアの開発では「自分の興味が目的」であることが多いため、「関係者で共有した目的」を忘れないことについて、フリーソフトウェアの開発での知見を活かす機会はほとんどありません。そのため、「目的を忘れない」ということは意識して実施しなければいけません。
なお、目的が変わることは別に悪いことではありません。一度覚えた目的を覚えなおしたくないために、目的が変わることを拒否してはいけません。目的が変わることはフリーソフトウェアの開発でもよくあることです。時代は変わるので、時代に合わせてプロジェクトの方向性を変えることは普通です。目的が変わり関係者で新しい目的を共有したら、新しい目的を受け入れて忘れないようにします。
「目的」に関して、他の人が注目するポイントは次の通りです。
「期日」もこの開発が始まったときに決まっているはずです。期日も明示的に変えない限りそのまままです。つまり、期日も忘れないことが大事です。
フリーソフトウェアの開発では「期日」という概念が希薄なことが多いため、フリーソフトウェアの開発の知見を活かす機会はほとんどないでしょう。
期日を忘れている場合、適切な実装の判断に失敗する場合があります。その結果、期日までに目的を達成できないかも(「約束を守れない」という事態になるかも)しれません。
時間がどのくらいあるかによって適切な実装は変わってきます。時間があまりないならたまにしか実行しない処理の性能向上にそれほど時間を使わないかもしれません。期日は適切な実装を選択する重要な条件になります。
「期日」に関して、他の人が注目するポイントは次の通りです。
「現状」は「スタート」からどのくらいの「期間」が「使用済み」になったかと、今の時点での「成果」です。これは日々変わります。成果がでていなくても「使用済み」になった「期間」は増えるので変わります。
「現状」はできるだけ正確に把握できていることが大事です。
フリーソフトウェアの開発では、現状のうち、今の時点での「成果」については知見を活かせる場合があります。「使用済み」の「期間」については活かせる知見はあまりないでしょう。なぜなら、「期日」という概念が希薄なことが多いからです。
フリーソフトウェアの開発では、困ったら途中でも相談する人といくら困ってもある程度形になるまでは相談しない人がいます。進捗共有に知見を活かせるのは、前者の「困ったら途中でも相談する人」です。
前者の人は随時コードを共有する傾向が強く、後者の人はできあがるまで見せてくれない傾向が強いです。一緒に開発していると知見を活かせる人かどうかわかるでしょう。
「現状」に関して、他の人が注目するポイントは次の通りです。
「今後」は今の時点からどのように「目的」に向かっていくかの方向性と「使用予定」の「期間」です。一番むずかしい項目です。他の3つの項目はそれぞれ独立していましたが、「今後」は他の3つの項目を把握した上でないとまとめられないからです。
たとえたくさん成果をだしていたとしても、「目的」に向かっていない方向性なら要注意です。当事者は成果をだして順調に進んでいる気になっているかもしれません。「期日」が近づき「目的」はあとどのくらいで達成できるかを考えたとき、かなり作業が残っている(「目的」を達成する「成果」がほとんどない)、という自体になりかねません。
「目的」を忘れているとこの状態になりやすいです。
方向性が正しくても、「期間」の使い方を間違うと「期日」までに完成しません。道半ばにして「期日」になってしまいます。
「期日」を忘れているとこの状態になりやすいです。
「今後」に関して、他の人が注目するポイントは次の通りです。
フリーソフトウェアの開発で得た知見だけを活かしていると「期日」までに約束した「成果」を達成できない可能性が高いです。そのため、それを未然に防ぐための対策が必要になります。
対策として、進捗を共有し、当事者が気づいていなくても周りの人が気づいて対応を始める方法を紹介しました。この対策を実現するために、進捗として次の4つの情報を共有することを提案しました。
フリーソフトウェアの開発ができても仕事ができるわけではありません。クリアコードは、フリーソフトウェアの開発もできるし、仕事もできる開発者を集め、対価を得ながら継続してフリーソフトウェアを開発できる体制を維持していきます。