[プログラミング]よいコードを書くために

コードをたくさん読んでいると、よくできていて参考にしたくなるコードや、身の毛もよだつクソコードなど、色んなコードに出会う。

自分一人で書いていた頃は、コードの良し悪しは全て自分にはね返ってきていたのだが、チームを組むとそうはいかない。

人の書いたコードで苦しむこともあれば、自分の書いたコードで人を苦しめることもある。

そんなことにならないよう、少しでも良いコードを書くために意識するべきことをまとめてみた。

読むのに苦労しないコードを書く

書かれたコードは、それが使われ続ける限り、何度も読まれる。

読む人は自分かもしれないし、他のチームメンバーかもしれない。別の会社の顔も知らない人かもしれない。

そんな人たちでもスラスラ読み解け、理解できるコードは、きっと良いコードだ。

冗長さを制御する

大体の悪いコードは長い。どんなに素晴らしい設計がされていても、長いと読む気力が失せる。コードは短いに越したことはない。

ただ、短くすることだけを目的にしてしまうと逆に読めないコードになる。

Pythonならリスト/辞書内包、lambda、map/reduceが乱用されたコードを考えてみるとわかりやすい。

このような場合は多少行数が増えるとしても、forや普通の関数定義を使った平凡なコードの方が良いコードになることは多い。

どちらの方が読みやすいかを常に吟味しながら、冗長に書くか短く書くかを選択していく必要がある。

問題を分割する

長い、読めないコードができてしまう原因の一つとして、問題を分割できていないというのがある。

やりたいことに対して、それに必要な処理をずらーっと全部ひとところに書いてしまうようなのだ。


例えば「ユーザーを登録したい」というシンプルな目的に対しても、それを実現するために幾つもの細かい問題を解決しているはずだ。

リクエストメソッドを判別する、POST値からユーザー名とメールアドレスを取り出す、値を検証する、ユーザーオブジェクトを作ってsaveするetc

これら細かな問題を、関数やクラスの形に分割できないかを検討してやらなくてはいけない。

問題を分離しておけば、細かく処理を追うか、おおまかに流れを掴むかを選択でき、読み手の負担はぐっと下がる。

通化やテスタビリティ向上の面でも、問題を正しく捉えて分割するというのはとても重要だ。

分離したら再設計する

長い処理を分離すると、分離した先は大抵なにかデータを入力し、その結果として出力値を返すようなものになる。

間違った分離をしてしまうと、例えば値の検証の関数が、リクエストオブジェクトを取ってフォームオブジェクトを返す みたいなわけのわからない動きをするものになってしまうことがある。

こういうものは大抵、ベタっと書かれていたコードの一部分をカットペーストで移動して、移動前に使っていたコンテキストを引数にしただけのもので、あまり価値がない。単純に処理のジャンプ回数が増えただけだ。

値の検証であれば、未検証の値の辞書を渡して、検証済みの値の辞書を返すような、入力と出力の関係がシンプルなものの方が使いやすいだろう。

入出力のみに限らず、コードを分離するのであれば、その部分での関心ごとにちゃんと注目し、かくあれかしという設計に焼き直してやることが必要だ。

適切な名前

関数や変数の名前というのは、コードを読み解くにあたって大きな手がかりになる。

それはつまり、名前がおかしければそれだけ読み解く障害になるということでもある。

ユーザーオブジェクトが入っている変数名が"a"とかだったら、もうなんのことやらさっぱりという感じだろうし、グリッドの行と列に順次アクセスするためのループ変数がiとjだったら、どっちが行でどっちが列なのかぱっとは分からない。

また、関心ごとの分割においても、分割した関心ごととその動作を正しく表す名前を関数につけてやる必要がある。

create_userという関数でユーザーを作り…つつ登録完了メールも飛ばす、なんてことをしているとしても、使い手からは全く想像できない。

プログラムとしては名前なんてただの識別子でしか無いが、読み手にとってはそうじゃない。

嘘の説明を書くなんてことは、しないようにしよう。

型にはめる

ある程度型に沿ったコードというのは、それだけで多少読みやすくなる。

場所によってタブインデントだったりスペース4つだったり、ブロックの中括弧がifの行についてたり、次の行についてたりするのは地味にストレスになる。

PEP8でも社内コーディング規約でもいいが、なにか指標があるのであれば、できるだけそれに従って書くのが良いだろう。

規約自体がそもそも良くない場合はどうするかとかは、また別の話。

コメントを必ず書く

ここまで、どのようにコードを書けば読みやすくなるかという話をしてきた。

しかし、どんなに綺麗に設計しようが、素晴らしい名前付けをしようが、意味を伝えるということにおいては自然言語で書かれたコメントには敵わない。

どこまでいってもコードだけで表せるのは「この関数をよびだす」だとか「あの変数に代入する」だとか、その程度の意味までだ。コードは決して語らない。

各処理の要点や、関数の概要は、必ずコメントとして残し、処理内容と一致するように常にメンテしていく必要がある。

コメントを書かないという選択肢はない。

テストをかく

ユニットテストを書いてみると、自分のコードがよいコードかどうかが結構な精度で分かる。

問題を適切に分割できていれば、要は設計がうまくいっていれば、必然にテスタビリティも上がっていくので、ユニットテストが簡単に書けるようになっているはず。

逆にユニットテストレベルで苦労するようであれば、なにかしらよいコードの条件を破っていると思って間違いない。


また、書いたテストを今後も運用し続けていくかはともかく、「書こうとしてみる」ことの方が重要だ。

なので、最悪まともに書けなくてテストコードを捨ててしまうことになっても構わない。

次に書くコードでは少しでもテストを書きやすいように意識してみると、きっと何かしらの改善があるはずだ。

疑問をもつ

自分が書いたコードが本当に悪いコードじゃないかという疑問を常に持ち続ける。

悪いコードを書いていないか、たまに振り返ってみる。人に評価を求めてみる。

書いてしまっているなら、何が悪いのか、どこを変えればよくなりそうなのかを考えてみる。相談してみる。やってみる。

これがないと一生改善は始まらない。

おわりに

誰もが悪意を持ってクソコードを書こうとしている訳ではないが、経験が足りなかったり、時間が足りなかったりして、悪いコードを生み出してしまうことがある。

そんな人に指導するとき、もしくは、自分が悪いコードを生み出さないようにするために、この記事が助けになれば嬉しい。

一週間ノマドやってみた

ばあさんが入院したというので、先週から実家に帰っていた。

さすがにこんな状況で日帰りだの1泊2日だの慌ただしいスケジュールで動くのもやだし、かといってまる一週間休みが取れるほど代休を残してるわけでもヒマなわけでもなかったので、休暇一切なしにしてみた。

平常通り働いて仕事終わりに病院に行く、要はノマドというやつである。

去年の震災時、先月のインフルエンザパンデミック時など、何度か非出社での作業を続けてきたので、それらも含めた感想などをまとめてみることにした。

もちもの

仕事のために持って帰ったのは以下のものだけ

  1. macbook air
  2. wimaxルーター

実家にもwifi環境があるので、ネット環境は快適といって差し支えなかった。

仕事

通常通り、9:00に作業開始報告して、18時に作業終了報告。

というか、自分一人のタスクに関してはほぼ通常通りに仕事ができた。ネットとノートPCがあれば特に不自由しない。

これもwebストレージで案件資料を共有したり、webメールを使ったり、開発サーバーにsshで接続して開発するスタイルをメインにしたりといった日頃の取り組みの結果だと思う。

紙資料メインだったり、社内ネットワーク内でないとアクセスできないサービスが業務に必要だったりすると、こうはいかない。

仕事環境

問題はこれで、ちゃんと腰を落ち着けて仕事ができるスペースが家になかった。

仕方なく駅前を放浪して電源があって長居ができるカフェを探してそこで仕事してた。

いい椅子と広い机の重要性を再認識。

チームの連携

これも難しい。

Skypeやらメールやらの文字情報に起こさなかったあらゆる情報はチームメンバーに一切伝わらなくなるので、本当にマネージャーが俺の進捗を把握できているかどうか、俺が他メンバーの状況を正しく把握できているかという点が非常に不安になる。

今のチームでは意識的に日報の類は課さないというルールにしているが、こういう状況になった場合は自主的にでも日報出して、自分の状況を伝えなきゃいけないなと思った。

眠い

仕事環境のとこでも言ったが、腰を据えて仕事ができないので、必然に布団でやったり寝っ転がったりして仕事をするはめになる。

更に家族は仕事にでかけるので家に自分一人しかいない。

そんな状況で黙ってコードなんか書いてるともうとてつもない眠気が襲ってくる。外に逃げたのは家にいると眠気に負けてしまうからというのもある。

まとめ

1. 色々事前の工夫は必要だが、仕事はできる。
2. 快適に作業できる環境の確保が重要。
3. 報告の頻度を通常より増やすと良いかも。

所感

今回は「しばらく帰省します」って言ったのが月曜の昼頃で、その日の23時のバスで帰るという相当な突発スケジュールだったが、さくっと帰れて仕事にも深刻な影響は出なかった*1ので、非常にうまくいったと思っている。

プログラマーな人達みんな、こういう働き方ができればいいなと思う。

ちなみに

ばあさんはわりと元気でした。拍子抜けするぐらいに。

*1:と信じている

macで256色terminal使いたいけどiTermが重すぎて死にたい

こたえ

iTerm2つかえよ

時は201X年...

にも関わらずmacのTerminal.appはansiカラーしか出せなかった。*1


feizは8色の世界に飽き飽きしていた。ansiカラーはやたら眩しいか暗すぎて見えないかの2択しかなくて終わっている。

特にvimシンタックスハイライトでそれをやられては仕事にもならない。desertは確かに鉄板なのだが、それでも多少目に眩しい。

256色カラースキームを使おうにもそもそもTerminalが対応していないのでどうしようもない。本当に終わっている。

救世主iTerm

そんな世紀末の荒野でiTermというターミナルエミュレータを見つけた。256色対応らしい。

しかしいざ起動してみるとあまりの弱さに愕然とする。

256色こそ触れ込み通り対応しているが、よくわからない設定、最大化ボタンを押すだけでがくがくする挙動、再描画漏れ…

そしてなにより27インチiMacで最大化してssh>tmux>vimと起動するともはや使い物にならないレベルで重い。

世紀末覇者iTerm2

救世主かと思われたiTermはジャギだった。

誰もが希望を捨てかけたその時、真の世紀末覇者iTerm2を見つけた。

iTermの派生らしいが、ジャギiTermと違ってめちゃくちゃ軽い。設定も解りやすい。

かくして

よくできた弟の活躍により世界は救われたのであった。

*1:職場のmacSnow Leopardなのだ

fabfileの構造化

fabfile.pyって、task数個までならまあ1ファイルでいいんですが、それ以上増えてくるとスクリプト自体がカオスになって収集がつかなくなります。

更に似たような、けど微妙に違うtaskをいっぱい定義したりすると名前まで酷くなってきます。こんなかんじに。

@roles('lb')
def lbserver_deploy():
    """ロードバランサーにデプロイする"""
    ...

@roles('app')
def appserver_deploy():
    """アプリケーションサーバーにデプロイする"""
    ...

@roles('batch')
def batchserver_deploy():
    """バッチサーバーにデプロイする"""
    ...

こんなファイルメンテする気が起こらないので、なんとか綺麗にしたいと思って調べたところ、fabric1.1から付いたクラスベースのtaskというのを使うとよいようです。

http://docs.fabfile.org/en/1.2.2/usage/tasks.html#new-style-tasks

つかいかた

あんまり詳しく調べてないのでベーシックな使い方を。

1. まずfabfile.pyを、モジュールからパッケージに直します
fabfile/
     ┣__init__.py

__init__.pyに元のfabfile.pyの内容をまんま移せばとりあえずこれまでどおり動きます。

2. 思うがままにモジュール化する

心ゆくまで分割してください。

fabfile/
     ┣__init__.py
     ┣lb.py
     ┣app.py
     ┣batch.py

とりあえずサーバー種別ごとに3つのモジュールに分解した と思ってください。

この段階で関数名も被りを気にしない短いモノに変えてしまいましょう。この例だと"appserver_deploy" -> "deploy"ぐらい。

3. 各task関数に@taskデコレータをかける
from fabric.decorators import task

@task
@roles('app')
def deploy():
    """アプリケーションサーバーにデプロイする"""
    ...

これはまあおまじないで。

4. __init__.pyでimportする

最後に分割した各モジュールを__init__.pyでimportします

import lb
import app
import batch

これで終わりです。fab --listコマンドで一覧をだしてみると

$ fab --list

lb.deploy     ロードバランサーにデプロイする
app.deploy    アプリケーションサーバーにデプロイする
batch.deploy  バッチサーバーにデプロイする

みたく表示されます。モジュール名がまんま名前空間になってくれてる感じですね。

もちろん

fab app.deploy

と叩けば動作します。

モジュールじゃなくてパッケージにしたら多段の名前空間が使えるのか とかは調べてないです(´・ω・)

おわり

極力こういう複雑なfabfileは書きたくないもんですが、必要なときにはこうやって構造化するのもよいかと思います。汚いよりよっぽどいいです。

他にもcontribとか面白そうなモジュールがいっぱいありそうなので、また調べて書くかも。

認証と認可

OpenIDだのOAuthだのFacebookコネクトだの認証だの認可だの白だの黒だの!

たまに混同しそうになるので認証と認可についてまとめておく。

認証

英語でAuthentication -> 辞書的な意味*1

目的

アクセスしてきたユーザーが誰なのかを特定すること。

一般的な認証
  • ID/password
  • 端末固有IDとか
  • OAuthとかOpenIDとか*2

認可

英語でAuthorization -> 辞書的な意味*3

目的

認証されたユーザーが、サービスのデータや機能のうちどれを利用できるのかを決めること

一般的な認可

ふつうのwebサイトにおいては、認証された時点で全ての機能が使えるようになっているが、これは認証された時点でそのユーザーに対して全ての機能の利用が認可されているとみなす。
TwitterAPIとかは、APIを利用する各サービスについて認可される内容が決まっている。ユーザーはそのサービスの利用開始前に認可される機能を確認でき、その内容によっては認可をとりやめることも出来る。

まとめ

まとめてみると大したことはないのだが、日本語しかり英語しかり、名前似すぎ。

英語での略称はAuth(Authentication)とAuhz(Authorization)というのがあるらしい。

*1:動詞形のauthenticateの方が分かりやすかったのでそっちを貼ってます

*2:OAuthについては認証と認可が混じっている

*3:動詞形のauthorizeの方が略

PyconJP2011のかんそうぶん

PyCon JP 2011

行ってきました。

プログラムひとこと感想

Webフォームウィジェットツールキットを総括する - aodag

途中から参加。種類が多く、機能も使い勝手も千差万別と中々選定が難しいが、やっぱりdeform使っとくのが一番安定しそうな気がした。

Object-oriented usage of using command line tools in Python - Hsin-Yi Chen

ucltipというライブラリの紹介。

Pythonオブジェクトでシェルコマンドをラップして、

>>>apt_get = ucltip.CmdDispatcher('apt-get')
>>>apt_get.install('vim', 'emacs', y=True)

みたくかける。

シェルスクリプトなりos.system()使うなりすりゃええやん という意見ももっともだが、なれないシェルスクリプトで複雑な条件分岐ロジックをえっちらおっちら書いたりos.systemに渡すコマンド文字列を文字列結合して構築したりするよりよっぽど素敵な解答じゃないかなと思った。

実際今そういう複雑なシェルスクリプトをいじるハメになっていて「ああpythonでかきてぇ」ともやもやしていたというのもある。

Tuning Python Code - INADA Naoki

チューニングのいろは。

%timeとかdstatとか知らなかったキーワードがいっぱいなのでぐぐる

内容は濃かったがデモが多くて非常に分かりやすかった。

Party

当日券で参加。

おやぶんとid:rokujyouhitomaを取り合ったり知らない人と話したりと十二分に楽しんだ。

人数多いところに立食だったので交流しやすくて非常に良かった。

あと、俺の事をブログとか経由で知ってくれてる人がいるのに驚いた。

「新卒準備カレンダーに夢のない記事書いてたfeizさんですか!?」なんて話しかけられた日には苦笑いするしかない。

でも素直に嬉しい。

その他

今回はTarekはじめ海外からの参加者が居たり、English Trackがあったりしたので英会話ができないのはすごくもどかしかった。

別の記事で書こう

おわり

非常に楽しいイベントでした。運営チームのみなさま、おつかれさまでした&ありがとうございました。

次回はなにかしらお手伝いしたいな…