give IT a try

プログラミング、リモートワーク、田舎暮らし、音楽、etc.

40歳になりました&アイコン写真変えました

はじめに:ついに40代に突入しました

これまでは「アラフォーの30代」だった僕ですが、先日誕生日を迎え、ついに40代に突入しました。
40歳になったから、というわけでもないんですが、ちょっとした区切りとしてはいいタイミングなので、前から変えようと思っていたアイコン写真も変えてみました。

新しいアイコン写真はこんな感じでいきます。

f:id:JunichiIto:20170717053325j:plain:w300

今回はカメラ目線のドヤ顔ではありません。
もうドヤ顔なんて言わせない!w

ちなみに以前のアイコンはこちらでした。

f:id:JunichiIto:20170717053434j:plain:w300

見慣れるまで時間がかかるかもしれませんが、どうぞよろしくお願いします。

ところで、40歳といってもただの通過点のひとつ・・・と言いたいところですが、やっぱり30代と40代の心理的な差は大きい気がします。
というわけで、ちょっとここで自分の30代を振り返り、それから40歳以降の展望を書いてみようと思います。

30代を振り返る:「無名の社内プログラマ」から「Ruby界隈での有名人」へ?

30代で自分に起きた出来事をざっくり振り返るとこんな感じでしょうか。

比較的大きな出来事をピックアップするとこんな感じですが、他にも地方Ruby会議をはじめとしたIT関連のイベントや勉強会で登壇したり、ブログやQiitaで継続的に記事を書いたりして、だんだんネット上(特にRuby界隈)での知名度が上がってきたのが僕の30代でした。

ソニックガーデンや倉貫さんのおかげで知名度が上がった

その契機になったのはやはり、ソニックガーデンへの転職ですね。
ソニックガーデンという会社や社長の倉貫さんが有名だったので、最初の頃はソニックガーデンの知名度に引きずられる感じで僕のブログや僕自身の知名度も上がっていきました。
あと、当時はまだ珍しかったリモートワークを始めたのも、ネット上で注目を集めるきっかけのひとつになりました。


Rubyのおかげで知名度が上がった

また、ソニックガーデンに入ってからRubyを始めたのも大きな変化だったと思います。
なんとなくの感覚ですが、Rubyコミュニティはすごくオープンな雰囲気なんですよね。
なので、勉強会に参加したり、主催したりするハードルも低く、そこから知り合いがどんどん広がっていった気がします。
技術に関する知見も同様に「積極的に自分の知見を交換しよう」「どんどんコードを公開しよう」という雰囲気なので、「技術記事を書く → ネット上で拡散される」という流れを起こしやすかったように思います。

というわけで、30代は「地方に住む無名のいちプログラマ」から、「なんかしらんけど、Ruby界隈では無駄に知名度の高い伊藤さん」に変わっていった10年間だった、と言えるんじゃないでしょうか。

40代をどう生きるか?もとい、残りの人生をどう生きるか?

過去を振り返ったので、今度は未来を考えてみます。
平均的な日本人は20歳前後で就職して、60歳前後でリタイアすることを考えると、30代までが仕事人生の前半戦、40代からが後半戦になります。

いつのまにか仕事人生の半分が過ぎちゃった!残りの時間はあとたった20年!
・・・なんかそんな気もしてきますね。

というかそもそも、この残り20年を五体満足で健康的な身体で過ごせるのかどうか?ということ自体に僕は疑問を持っています。
ネットのニュースを見てたりすると、40代、50代で大きな病気にかかって大手術を受けたり、不幸にも亡くなってしまったりする人をよく見かけます。
なので、僕自身もここ数年は「この先、自分の身にいつ何が起こるかわからないぞ」と考えながら生きるようになっています。

5年後にもし自分が死ぬと分かっていたら?

60過ぎで仕事をリタイアして、そこからのんびりと老後を過ごす・・・・否!そんな保証はどこにもない!
もし5年後、自分がベッドの上で動けなくなって、人生最後のカウントダウンが始まっているとしたら?
「ああ、死ぬ前に○○○しておけばよかった・・・」ではなく、「まあ、仕方ないか、やりたいことは全部やったし」と思えるようにするには!?

・・・そんな妄想をして、やりたいことは今のうちにやらねば、と考えるようにしています。
というわけで、死ぬまでにやりたいこと、っていうとそうだなあ、ぱっと思いつくのはこのへんでしょうか。

  • 何かしら歴史に残るような、何かしら人々の記憶に残るような何かを成し遂げたい
  • 子どもたちが自立して、自分の力で生きていけるようになるのを見届けたい
  • いいギターを買って、ストレスなく思い通りにギターが弾けるようになりたい
歴史や人々の記憶に何かしらの「つめあと」を残したい

「何かしら歴史に残るような、何かしら人々の記憶に残るような何かを成し遂げたい」は非常に漠然とした目標です。
歴史の教科書に載るような偉業である必要はないのですが、特定の分野における「xxxに関する歴史」をひもとくと僕の名前が残っていたり、歴史に残らずとも「伊藤さんは死んじゃったけど、伊藤さんのことは忘れないよ~」と全く会ったことのない人から思われるような何かをしたい、と漠然と思っています。

僕の仕事はプログラマなので、コードを書いて実現するのが理想的なのですが、そこに固執するつもりはなく、執筆業でもいいですし、プログラミング以外の分野(たとえば地元の町おこし事業とか)でも構いません。
ただ、現時点では特に「これ!」という具体的なゴールがあるわけではないので、このままぼーっとしてると実現できないな~、どうするかな~と思ったりします。

父親としての仕事を全うしたい

「子どもたちが自立して、自分の力で生きていけるようになるのを見届けたい」は文字通りの目標です。
今日とか明日に僕が突然死んだら、小学生の息子と娘はさすがに困ると思います。
ですが、ある程度大きくなれば、僕が死んでも一人で生きていけるはずです。
最低でも高校生ぐらいまで、できれば二人が成人するぐらいまでは、父親として子どもたちが自立できるように後押ししていかねば、と思っています。

プログラミングも楽しいけど、ギターも大好き

最後の「いいギターを買って、ストレスなく思い通りにギターが弾けるようになりたい」は、一番具体的かつ、一番俗っぽい目標ですね(笑)。
僕は学生時代はプロミュージシャンになりたいと思っていました。
残念ながらプロミュージシャンになることは諦めましたが、今でも音楽は大好きです。
そしてギターを弾くのも大好きなんですが、いかんせん、大して上手くない。
「プロミュージシャンになりたい」とか言ってた割には、ちゃんとギターの練習をしてこなかったので、人前で弾くのもはばかられるレベルです。。

今でもライブやYouTubeとかでプロの演奏を見ると、「すげー、俺もあんなふうに弾きてー!」と思ってしまいます。
これは結構真剣に憧れるので、死ぬまでになんとか実現したいと思っています。

あと、学生時代はお金がなくていいギターも買えなかったので、当時憧れだったいいギターも買いたい!
ギターの練習は時間がかかるけど、いいギターはお金を出せばすぐ買える!

というわけで、ギターは買っちゃいました。
フェンダーのストラトキャスターとギブソン・レスポール。

f:id:JunichiIto:20170717073247j:plain

ぶっちゃけ、どちらも結構いいお値段がしますが、いいギターを持っていると嬉しい!テンションが上がる!!
さあ、あとは練習あるのみ!・・・なんですが、練習はまあぼちぼちと(苦笑)。
いや、いいギターは弾いてるだけで楽しいので、以前よりはギターを弾いてる時間がずっと増えましたが、ちゃんと練習してるのかと言われるとうーん。。。
練習らしい練習もしっかりやらないとダメですね。がんばります!!

自宅にこもりがちなリモートワーカーには意識的な運動が必要

自分がいつ大きな怪我や病気に遭うのかは予想できませんが、ちょっとでもそのリスクを減らす努力はできるはずです。
僕はこの5~6年、自宅でリモートワークしていますが、さすがに自宅にこもりっぱなしだと明らかに運動不足になっちゃいます。

そこで、ここ最近は自宅にいても意識的にしっかり身体を動かすようにしています。

そのおかげか、最近数年ぶりに体重が63kgを下回るようになってきました。

f:id:JunichiIto:20170717110912p:plain:w300

これで病気になってしまったらもう運命だと思って諦めるしかないですが、やれるだけの悪あがきはやっておこうと思います。

閑話休題:プログラマとして40代をどう生きるか

おっと、「いつ死んでもおかしくないとしたら?」というテーマはちょっと壮大な脱線になってしまいましたね(苦笑)。
40代をどうするの?という元のテーマに戻りましょう。

40代はこんな活動ができたらいいな、と考えていることをいくつか載せていきます。

Rubyを極める?新しい分野に進む?

プログラミングの仕事は基本的に楽しいので、プログラマの仕事はこのままずっと続けたいと考えています。
ですが、RubyやRuby on Railsに関してはそれでお金がいただける程度にはマスターしたので、そろそろ次の言語や技術分野へと・・・と思わなくもないのですが、RubyやRailsはすごく楽しいんですよねえ。
JavaやC#をやってたころは「他の言語もやってみたいなー」と思っていたんですが、Rubyに関しては「ずっとこのままでもいいや」と思えるぐらい楽しいし大好きです。

「むりやり言語や技術分野を変えずに、好きな分野を追求し続ければいいじゃん」という気持ちと、「いや、このままRubyとRailsだけ、というのもさすがに厳しそうだから、新しいジャンルも開拓しないと!」という気持ちがせめぎ合ってる感じがあります。
でもどっちかというと、今は前者の気分かな~。うーむ。

ライフスタイル・エバンジェリスト?

あとは、プログラマという仕事の楽しさや田舎暮らしの良さみたいなものも、いろんな人たち、特に若い人たちに伝えられたらいいなと思っています。
個人的には「プログラマ x 田舎暮らし x リモートワーク」というのは最高の組み合わせだなと思っているので。


物書きもできるプログラマとして

文章を書くのは好きだし、自分の強みのひとつだと思っているので、ブログや本の執筆も引き続きやっていきたいですね。
とりあえず、今年の終わりには「プロを目指す人のためのRuby入門」が発売されます。
40代は紙の本、電子書籍を問わず、2~3冊自分の本を書けたらいいなと思っています。


余談:加えて、カッコいい夫・お父さんとして

40代、50代となると、さすがに身体の老化も見過ごせなくなってくることでしょう。
とはいえ、シワや白髪が増えてくるのは避けられないとしても、体型なんかは努力次第である程度キープできるはずです。
ぽっこりお腹とか、二重アゴとか、そういう典型的な中年太りは避けたいなあ。
そんなわけで、毎日の運動は生活習慣病の予防と体型の維持を兼ねて続けていこうと思います。

ちなみに僕の理想の40代男性は西島秀俊さんです。
(とても40代には見えない、カッコいい!)

まとめ

というわけで、このエントリではアイコン写真変更のお知らせと、30代の振り返り&40代の展望を書いてみました。
30代もあっという間に過ぎていきましたが、40代も同じように、いや、30代以上のスピードで過ぎていくんだろうなあ~。
というわけで、この先何が起きるかわかりませんが、今後とも引き続きよろしくお願いします!

あわせて読みたい

30代より前の振り返りについてはこちらのエントリでいろいろ書いています。

前回アイコン写真を変えたとき(2015年)のエントリです。

「1.5個のタマネギは4分の3」と答えた甥っ子の意外な理由

はじめに:タマネギ1.5個は4分の3個!?

先日、家に遊びに来ていた甥っ子(Aくん)とご飯を食べていたときのことです。
Aくんと話していたら食べている料理の材料の話になりました。
その中で妻が、

「この料理に使ったタマネギは 1.5個

と発言しました。

そしたら、それを聞いたAくんが、

「じゃあ、4分の3 やな」

と言いました。

僕と妻は顔を見合わせてびっくり。

「いやいや、1.5個は4分の3とは違うで。だって、4分の3って1個より少ないやん」

って説明したんですが、Aくんは「えっ???僕、なんかおかしい?」みたいな顔になり、全く分数のルールを理解していないように見えました。
このAくん、学校の成績はいつもいい方だと聞いていたのですが。。

Aくんが「4分の3」と答えた理由

それからしばらく、僕たち夫婦はAくんに分数の数え方を手を変え品を変え説明していました。

そのやりとりの中で、なぜAくんが1.5を4分の3と答えたのか、その理由がようやくわかりました。

実はAくんは頭の中で、2個のタマネギをそれぞれ半分にし、そこから3つ取り出すことで、1.5 => 3/4 を作り出していたのです。

f:id:JunichiIto:20170713125939j:plain

こうやって説明を聞くと、「あー、たしかに4分の3か・・・」と思えなくもありません。
ですが、「タマネギ1.5個 = 3/4個」はやっぱり明らかにおかしいです!

少なくとも「タマネギ2個を1とした場合」という前提条件を付けないと、「タマネギ1.5個 = 3/4」説は成り立ちません。

ティッシュ1/2枚=ティッシュ1/3枚=ティッシュ1枚!?

話はタマネギだけにとどまりません。

本当に分数を理解しているのかどうか確認するために、ティッシュペーパーを渡して「2分の1枚ちょうだい」とお願いしてみました。
僕としてはティッシュを半分にちぎって、片方を渡してくれることを期待したのですが、Aくんは特に迷うこともなく、まるまる1枚のティッシュペーパーを渡してきます。

心の中で「おおっと!?」と思いながら、今度は「じゃあ3分の1枚ちょうだい」とお願いしたら、さっきと同様、まるまる1枚のティッシュペーパーを渡してきます。
なんでっ!?なんでどっちもまるまる1枚渡してくるの!?

f:id:JunichiIto:20170713131532j:plain:w300

これもどういう理屈でそうなったのか話を聞いてみると、Aくんは、

「ティッシュが3枚あったら、そのうちの1枚で3分の1」

みたいな説明をしてきました。

f:id:JunichiIto:20170713131701j:plain

いやいやいや、そのプラス2枚はいったいどこから出てきたんやっ!?っていう話です 😱

そんなこんなで、最初はいろいろ勘違いしていたAくんですが、あれこれ説明した結果、最終的には正しい答えを返せるようにはなりました。

続き:えっ、みんなそう考えてるの??

「まあ、そんな変わった考え方をするのはAくんぐらいだろうなー」と思いきや、実はそうではありませんでした。

このやりとりをそばで聞いていた娘が、学校に行ってこの話をしたところ、結構な数の同級生の子たちが「3分の1を考えるときは頭の中でティッシュを3枚用意する」と答えたらしいです。

また、妻のママ友の一人も、やはり同じように分数を考えるときは「3つ用意した中の1つ」みたいに考えているらしいです。

もちろん、「3枚あるティッシュのうちの1枚は何分の何?」と聞かれたら「3分の1」でOKです。
でも、「3分の1枚のティッシュ」と言われたら、普通「1枚のティッシュを3等分したうちの1つ」って数えますよね?ね?ね??
(違うのかな?もしかして僕の考え方の方が少数派?)

まとめ:分数と小数の考え方は要再チェック!?

なんでこういう考え方になっちゃうのかなーというのは非常に不思議ですが、子どもに限らず「分数の基準を1以外の都合のいい数字に変えて考える人たち」は意外と多いのかもしれません。

まあ、分数の計算だけにフォーカスするなら、それでも計算はできそうな気がします。
しかし、日常生活で分数や小数を扱うときは基準となる数字を毎回自分の都合のいいように変えてしまうと問題が出てきますよねえ。

というわけで、小学校高学年ぐらいのお子さんがいるお父さんお母さんは、ためしに「1.5個のタマネギって、分数に直したらいくつ?」と聞いてみてください。
もしかすると、びっくりするような答えが返ってくるかもしれませんよ・・・!?

WEB+DB PRESS Vol.99の「良いコード」を本気でコードレビューしてみた

はじめに

Twitterを見てたら、気になる雑誌の特集を見つけました。
WEB+DB PRESS Vol.99の「Rubyで学ぶ!良いコードって何だろう?」という特集記事です。

WEB+DB PRESS Vol.99

WEB+DB PRESS Vol.99

  • 作者: ?橋健一,谷口禎英,井本大登,山崎勝平,大和田純,内村元樹,坂東昌哉,平田敏之,牧大輔,板敷康洋,大?浩崇,穴井宏幸,原口宗悟,久田真寛,ふしはらかん,のざきひろふみ,うらがみ,ひげぽん,池田拓司,はまちや2,竹原,片田雄樹,渋江一晃,WEB+DB PRESS編集部編
  • 出版社/メーカー: 技術評論社
  • 発売日: 2017/06/24
  • メディア: 大型本
  • この商品を含むブログを見る
Ruby大好き!きれいなコード大好き!!な僕にとっては、この特集は読まずにはいられません!
早速買って読んでみました。

お~、なるほど、たしかにいいことが書いてある!
うんうん、そうそう・・・あれ?このコード、どうなん?
うーん、この例はちょっと微妙かも。。
と思ったところがちーらほら。

重箱の隅を突っつくような話をするのは野暮だなあとは思いつつ、それでも「良いコード」をうたう特集記事なのであれば、隅から隅まで「良いコード」を貫き通してほしい!
というわけで、このエントリではこの特集記事を読んでいて、ちょっと気になったサンプルコードをピックアップしていこうと思います。

本編へ進む前に

執筆者のみなさまへ

せっかくがんばって書いた記事に、あれこれいちゃもん付けてすいません!
先に謝っておきます。🙇🙇🙇

指摘事項の中には「そんな細かいところまでチェックしなくても」と思われるものもあるかもしれませんが、せっかくのいい特集なので、サンプルコードは徹底的にブラッシュアップした方がいいんじゃないかと思い、このエントリを書きました。

まだWEB+DB PRESS Vol.99を読んでいないみなさんへ

僕のエントリだけ読むと、「この記事、問題だらけなん?」って思われるかもしれませんが、そうではありません!
記事自体は非常に良い内容になっていますし、僕もほとんどの内容に同意しています。
また、説明の文章が非常に読みやすいのも、この記事のいいところだと思います。
ですので、良いコードを書けるようになりたいと思っているプログラマのみなさんはWEB+DB PRESS Vol.99を購入してもらっても損はありません。
安心して購入してください!

とはいえ、真剣に僕基準でサンプルコードをレビューしていくと、それなりの件数の改善ポイントが見つかりました。
僕はソニックガーデンの社内でも「一番コードにうるさくて、面倒くさい男」と思われてるそうなので(苦笑)、僕が仕事でコードレビューするなら、たぶんこれぐらいツッコミを入れます。

WEB+DB PRESS編集部のみなさんへ

このエントリが引用の範囲を超えていると判断された場合はエントリを削除しますので、ご連絡ください。

サンプルコードについて

WEB+DB PRESS内で使われているサンプルコードはGitHubでも公開されています。


お待たせしました。
それでは以下が本編です。

P12: 何もしないelseは必要か?

記事の一番最初に出てきたこのコード例ですが、いきなり「すごく微妙なライン」を持ってきたな~という印象です。

# 意図が伝わりにくいコード
case hour
when 9
  say_hello()
when 18
  say_goodbye()
end

# 意図が伝わりやすいコード
case hour
when 9
  say_hello()
when 18
  say_goodbye()
else
  # 何もしなくてよい
end

記事によると、「上のコード では、hour が 9 と 18 以外の場合に何もしないのは意図したことなのか不安が残るのに対し、下のコードでは何もしなくてよいという書き手の意図が読み手にもしっかりと伝わります。」とのことですが、この「何もしないelse」が必須かと言われると、うーん。。。

僕だったら「何もしないelse」は作らないですね。
このコード例であれば「elseがない=9時と18時以外は何もしない」と解釈します。

もちろん「elseがないとコードを読んだ人が明らかに不安に感じる」という特殊なケースでは「何もしないelse」を書くことも検討しますが、このコード例のレベルであれば僕はelseは書きません。

(2017.6.30 13:30追記)
あえて何もしない条件分岐を作る場合、こういうコードならまだアリかな~と思います。

def greeting(hour)
  case hour
  when 9
    'Hello'
  when 18
    'Good-bye'
  when 0..23
    # 何もしない
  else
    raise ArgumentError, "Invalid hour: #{hour}"
  end
end

greeting(0)  #=> nil
greeting(9)  #=> "Hello"
greeting(18) #=> "Good-bye"
greeting(23) #=> nil
greeting(24) #=> ArgumentError: Invalid hour: 24

ここでは「予期しない時間(hour)」が渡ってきたときに、問題にすぐ気づけるようエラーを発生させています。(else節)
ただし、 "when 18" の直後に else を持ってくると0時や23時もエラーになってしまうので、 "when 0..23" の条件(hourが0~23なら真になる条件)を入れています。
ですが、特に何かをするわけではないので、 when節の中身は空っぽです。

こういうコードであれば、何もしない条件分岐を用いることで「hour が 9 と 18 以外の場合に何もしない」という意図が、よりハッキリするんじゃないかと思います。

(2017.7.3 12:00追記)
あと、「何もしないelse」を書く代わりにテストコードを書いておくのも有効だと思います。
そうすれば「開発者はこの条件だけを処理の対象にしたいと考えている」という意図がよりわかると思うので。

def greeting(hour)
  case hour
  when 9
    'Hello'
  when 18
    'Good-bye'
  end
end

require 'minitest/autorun'

class SampleTest < Minitest::Test
  def test_greeting
    assert_nil greeting(0) # <= 何もしないよ!
    assert_equal 'Hello', greeting(9)
    assert_equal 'Good-bye', greeting(18)
    assert_nil greeting(23) # <= 何もしないよ!
  end
end

P14: 設計の意図が全く読めないサンプルコード

続いてはこちらのコード例です。

class Foo
  attr_accessor :bar

  def print(content)
    @bar + content
  end
end

foo = Foo.new
foo.bar = "WEB+DB PRESS Vol."

method1(foo)
method2(foo)
method3(foo)

foo.print("99") # => NoMethodError: undefined method `+' for nil:NilClass

このコードは変数のスコープが広すぎる悪い例として挙げられているものです。

記事によると、「どうやらどこかで bar に nil が入ってしまっ たようです。(中略) そもそもこの bar は何度も代入したいものなのでしょうか。初期化時だけでは駄目なのでしょうか。」とのことなんですが、「そもそもこの bar は何度も代入したいものなのでしょうか。初期化時だけでは駄目なのでしょうか。」という問いに対する答えは、「要件による」としか言いようがありません。
しかし、このサンプルコードでは、Fooクラスと変数barの役割が全くわかりません。

また、method_1、method_2、method_3も何をするメソッドか全くわかりません。
いい名前が付いているメソッドであれば「barがnilに変わるケースもありそう」と思えるかもしれません。

もちろん、言わんとしていることはわかります。
「不用意にオブジェクトの状態を変更できるようにすべきではない」「無用な副作用をメソッドに持たせてはいけない」というのは、良いコードの大事な条件です。
ですが、ここでは何をやろうとしているのかわからないコード例になっているため、いまいち説得力に欠けているのが残念なポイントです。

ちなみに、まったく同じテーマではないですが、少し似た話をその昔Qiitaに書いたことがあります。

参考: (あなたの周りでも見かけるかもしれない)インスタンス変数の間違った使い方 - Qiita

P17: initメソッド?initializeメソッド?

次はマジックナンバーではなく、定数を使おうというこのサンプルコードです。

class Tweet
  MAX_TWEET_LENGTH = 140

  def init(content)
    @content = content
  end

  def post!
    if @content.length <= MAX_TWEET_LENGTH
      # 投稿
    else
      # エラー処理
    end
  end
end

定数を使うのはOKなのですが、initメソッドはいったい何なのでしょう?
initializeメソッド(JavaやC#でいうところのコンストラクタ)とは別のメソッドなのでしょうか?
このコードを見る限り、initializeメソッドでよいと思います。
そうしないと、newしたあとに、わざわざinitメソッドを呼び出さなければならないからです。

あえてのinitメソッドなのか、initializeメソッドと書くつもりだったのか、意図がつかめずモヤモヤします。

P18: 定数はドットでは参照できない(エラーが起きる)

さて、次はこちらのコードです。

class ExceptionCode
  INCORRECT_CREDITCARD = 100
  COMMUNICATION_FAILURE = 101
end

begin
  paid!
rescue ServiceException => ex
  if ex.code == ExceptionCode.INCORRECT_CREDITCARD
    show_error "クレジットカードの情報が誤っています。"
    redirect user_creditcard_path
  elsif ex.code == ExceptionCode.COMMUNICATION_FAILURE
    retry!
  end
end

えーと、 "ExceptionCode.INCORRECT_CREDITCARD" ではなく、 "ExceptionCode::INCORRECT_CREDITCARD" (ドットではなくダブルコロンで区切る)が正ですね。
ドットを使うなら INCORRECT_CREDITCARD がメソッドになっていなければなりません。
しかし、 INCORRECT_CREDITCARD は定数なのでエラーが起きます。

irb(main):005:0> ExceptionCode.INCORRECT_CREDITCARD
NoMethodError: undefined method `INCORRECT_CREDITCARD' for ExceptionCode:Class
	from (irb):5
	from /Users/jit/.rbenv/versions/2.4.1/bin/irb:11:in `<main>'

ダブルコロンなら問題ありません。

irb(main):006:0> ExceptionCode::INCORRECT_CREDITCARD
=> 100

うっかりミスだとは思いますが、このへんはきちっとコードレビューしてほしかったな~と思うところです。

P18: クラスよりもモジュール? / 例外クラスに定数を定義すべき?(状況による)

先ほどと同じこちらのコードです。

class ExceptionCode
  INCORRECT_CREDITCARD = 100
  COMMUNICATION_FAILURE = 101
end

もし、これが定数しか持たないクラスなのであれば、クラスではなくモジュールとして定義する方が良いでしょう。
なぜならExceptionCodeはインスタンス化する必要がないからです。

module ExceptionCode
  INCORRECT_CREDITCARD = 100
  COMMUNICATION_FAILURE = 101
end

ただし、これはあくまで短いサンプルコードですが、実務レベルのコードではインスタンス化する必要が出てくるかもしれません。
クラスにすべきか、モジュールにすべきかは、そのときの状況によります。

また、そもそもの話になりますが、 "ex.code == ExceptionCode::INCORRECT_CREDITCARD" のようにしてエラーコードを比較するのであれば、ServiceExceptionクラスでこれらの定数を定義している方が設計として自然だと思います。(これもまた状況によりますが)

begin
  paid!
rescue ServiceException => ex
  if ex.code == ServiceException::INCORRECT_CREDITCARD
    show_error "クレジットカードの情報が誤っています。"
    redirect user_creditcard_path
  elsif ex.code == ServiceException::COMMUNICATION_FAILURE
    retry!
  end
end

P18: Rubyの例外クラス名は慣習的に~Errorで終わることが多い

またまた先ほどのコードを登場させます。

class ExceptionCode
  INCORRECT_CREDITCARD = 100
  COMMUNICATION_FAILURE = 101
end

begin
  paid!
rescue ServiceException => ex
  if ex.code == ExceptionCode.INCORRECT_CREDITCARD
    show_error "クレジットカードの情報が誤っています。"
    redirect user_creditcard_path
  elsif ex.code == ExceptionCode.COMMUNICATION_FAILURE
    retry!
  end
end

rescue 節にServiceExceptionという例外クラスが登場していますが、Rubyの実行時例外は通常、StandardErrorクラスを継承します。
そしてStandardErrorクラスを継承した場合は、例外クラスの名前も~Errorで終わることが多いです。

参考: library _builtin (Ruby 2.4.0)

なので、ServiceException よりも ServiceError の方がRubyっぽい例外クラス名だと言えます。
ただし、これは慣習的な話なので、必ずそうすべきというルールではありません。

P18: retry! メソッドとは?

もう一度、先ほどのコードに登場してもらいます(4回目)。

class ExceptionCode
  INCORRECT_CREDITCARD = 100
  COMMUNICATION_FAILURE = 101
end

begin
  paid!
rescue ServiceException => ex
  if ex.code == ExceptionCode.INCORRECT_CREDITCARD
    show_error "クレジットカードの情報が誤っています。"
    redirect user_creditcard_path
  elsif ex.code == ExceptionCode.COMMUNICATION_FAILURE
    retry!
  end
end

下から3行目に登場するretry!はどこで定義されているメソッドなんでしょうか?
Rubyにはretryを使って、例外処理をやり直したりすることができますが、retry! というビックリマーク付きの制御構造やメソッドは標準では存在しません。

もしかすると、retry! はどこかで定義されている独自のメソッドなのかもしれませんが、例外処理の中でそのようなメソッドを使うとRuby標準のretryと混同しやすいので、あまり良いメソッド名だとは言えません。
あくまで予想ですが、これは本来retryと書くつもりだったんじゃないかと思います。

P18: redirectメソッド??

またまた先ほどのコードを出します。(これで最後です)

begin
  paid!
rescue ServiceException => ex
  if ex.code == ExceptionCode.INCORRECT_CREDITCARD
    show_error "クレジットカードの情報が誤っています。"
    redirect user_creditcard_path
  elsif ex.code == ExceptionCode.COMMUNICATION_FAILURE
    retry!
  end
end

if文の中に出てくる "redirect user_creditcard_path" ですが、これがもしRailsのコードだとしたら、redirectではなく、redirect_toが正です。
ただし、記事の中にはどこにも「Railsのコードである」との言明はないため、架空のフレームワークに定義された架空のメソッドである可能性もあります。

とはいえ、user_creditcard_pathというメソッドもRailsっぽいです。
もし、これがRailsっぽくてRailsでないコードなのであれば、読者を困惑させる原因になります。
なので、「これはRailsのコードです」もしくは「これはあくまでRailsではない架空のWebアプリケーションです」との言明が本文中にほしかったなと思います。

P20: order.new の o が小文字

P20に出てくる以下のコードは、状況から見て "order.new" ではなく、 "Order.new" が正ですね。
(特殊な状況では小文字もありえるが、普通はまずない)

def product_paid_and_send_mail(user, product)
  # 決済処理
  payment = Payment.new(product.price)
  if payment.pay
    # 注文を作成
    order = order.new(user.id, product.id)
    order.save

    # ユーザーに注文が完了したことを送信
    mail = Mail.new do
      from    "from@example.co.jp"
      to      user.mail
      subject "商品を購入しました"
      body    "#{product.name}を購入しました"
    end
    mail.deliver
  end
end

うっかりミスだと思いますが、コードレビューをしっかり!!

P20: 使われないローカル変数を登場させない

以下のコードの上から3行目、create_orderの戻り値をorderという変数に格納していますが、このorderは使われないローカル変数です。
良いコード例として紹介するのであれば、使われないローカル変数は登場させない方がいいと思います。

def product_purchase(user, product)
  if pay_for(product)
    order = create_order user.id, product.id
    send_ordered_mail product.name, user.mail
  end
end

def create_order(user_id, product_id)
  order = Order.new(user_id, product_id)
  order.save
end

P21: メソッドの名前は「動詞+目的語」の方が自然

もう一度先ほどのコードを登場させます。

def product_purchase(user, product)
  if pay_for(product)
    order = create_order user.id, product.id
    send_ordered_mail product.name, user.mail
  end
end

ここでは "product_purchase" というメソッド名になっていますが、「商品を購入する」ためのメソッドなのであれば、 "purchase_product" のように「動詞+目的語」の語順にした方がメソッド名として自然です。

参考: モデルやメソッドに名前を付けるときは英語の品詞に気をつけよう - Qiita

P26: インデントが1文字深い

下記コードの3行目、インデントが1文字ぶん深いです。
(細かいけどしっかりチェックするよ!!)

class Barista
  def brew(drink)
     drink.brew
  end
end

P31: クラスは文字列にしなくてもwhen節に直接クラスを指定できる

以下はクラス名に依存する悪いコード例として出てきたものなので、ツッコむのも野暮かもしれませんが、気になったので一応書いておきます。

class Customer
  def pay(payment_method)
    case payment_method.class.to_s
    when 'CreditCard'
      payment_method.pay
    when 'Bank', 'Conveni', 'PostOffice'
      payment_method.transfer
    else
      raise ArgumentError.new('unknown payment method')
    end
  end
end

Rubyではwhen節に直接クラスを指定できるので、上のコードは次のように書いた方がシンプルです。

class Customer
  def pay(payment_method)
    case payment_method
    when CreditCard
      payment_method.pay
    when Bank, Conveni, PostOffice
      payment_method.transfer
    else
      raise ArgumentError.new('unknown payment method')
    end
  end
end

ただし、「もしかすると」ですが、この言語仕様はRubyの中でも比較的マイナーな部類に入る(と思う)ので、Rubyに詳しくない人でもわかるようにあえて文字列で比較したのかな?という気がしなくもありません。

P31: Conveniクラスがない

先ほど出てきたコードをもう一度登場させます。

class Customer
  def pay(payment_method)
    case payment_method.class.to_s
    when 'CreditCard'
      payment_method.pay
    when 'Bank', 'Conveni', 'PostOffice'
      payment_method.transfer
    else
      raise ArgumentError.new('unknown payment method')
    end
  end
end

class CreditCard end
class Bank end
class ConvenienceStore end
class PostOffice end

customer = Customer.new
customer.pay(CreditCard.new)

when節に "Conveni" と書いてありますが、その下に出てくるクラスは "ConvenienceStore" クラスです。
クラス名が異なるので、意図した通りに動きません。

このコードはあくまで「悪いコード例」ですが、僕がここで指摘した内容は本文中に「悪い部分」として取り上げられていないので、おそらく純粋なコーディングミスだと予想します。

P31: 例外を発生させるならデバッグしやすい情報を含めるべき

またまた先ほどの「悪いコード例」です。

class Customer
  def pay(payment_method)
    case payment_method.class.to_s
    when 'CreditCard'
      payment_method.pay
    when 'Bank', 'Conveni', 'PostOffice'
      payment_method.transfer
    else
      raise ArgumentError.new('unknown payment method')
    end
  end
end

想定外の選択肢(つまりelse節)に入ってきたら例外を発生させる、という考え方は良いのですが、どんなオブジェクトがやってきたのかわからないと、例外が起きた後のデバッグに苦労します。
なので、例外メッセージには次のようにpayment_methodのクラス名も含めた方が良いでしょう。

raise ArgumentError.new("unknown payment method: #{payment_method.class}")

まあ、これはあくまで「悪いコード例」なので、がんばってきれいにする必要はないのかもしれませんが・・・。

P31: payメソッドやtransferメソッドが定義されていない(サンプルコードとしては許容範囲か?)

こちらも先ほどの悪いコード例です。

class Customer
  def pay(payment_method)
    case payment_method.class.to_s
    when 'CreditCard'
      payment_method.pay
    when 'Bank', 'Conveni', 'PostOffice'
      payment_method.transfer
    else
      raise ArgumentError.new('unknown payment method')
    end
  end
end

class CreditCard end
class Bank end
class ConvenienceStore end
class PostOffice end

customer = Customer.new
customer.pay(CreditCard.new)

when節の処理として "payment_method.pay" や "payment_method.transfer" がありますが、これらのメソッドはどこにも定義されていません。
なので、このサンプルコードをそのまま実行すると以下のエラーが発生します。

irb(main):023:0> customer.pay(CreditCard.new)
NoMethodError: undefined method `pay' for #<CreditCard:0x007fc43b827cb8>
	from (irb):8:in `pay'
	from (irb):23
	from /Users/jit/.rbenv/versions/2.4.1/bin/irb:11:in `<main>'

実際に動かす場合は次のように、メソッドの定義も必要になります。

class CreditCard
  def pay; end
end
class Bank
  def transfer; end
end
class ConvenienceStore
  def transfer; end
end
class PostOffice
  def transfer; end
end

まあ、あくまで「悪いコード例」ですし、記事でフォーカスしているのは「特定のクラスに依存する条件分岐を作るな」という点なので、こんなところにツッコむのは野暮だとは自覚していますが。。。
でも、明らかに動かないコードを見せられるとちょっとモヤモヤしてしまいます。

P33: settlementは名詞 / Settlementableもおかしい

クレジットカードクラスのサンプルコードとして登場した以下のコードにも気になる部分がありました。

class CreditCard
  attr_accessor :number, :expiration_date, :balance

  # このメソッドがよく使われる
  def settlement
  end

  def register
  end

  def charge
  end

  # ほかの公開メソッド
end

よく使われるメソッドとして登場している "settlement" メソッドですが、 "settlement" の品詞は名詞です。
「決済する」メソッドなのであれば、動詞の "settle" を使うか、 "make_settlement" のように「動詞+目的語」の形にする方が望ましいです。
(クレジットカード業界ではどういう英語表現を使うのか僕はよく知りませんが。。。)

また、P33に「このケースでは、Settlementable(決済可能)というロールになるかもしれません」とありますが、接尾辞の-ableは動詞に付けくのでSettlementableではなくSettleableにした方が良いでしょう。(ただし、ネイティブの人が見たらSettleableも違和感があるかもしれません)

参考: モデルやメソッドに名前を付けるときは英語の品詞に気をつけよう - Qiita

P36: セミコロンとif文のカッコは不要

P36に登場している以下のコードですが、僕だったらセミコロンとif文のカッコはなくします。

val = 1;
if ([1, 2].include?(val))
  # 処理
end

val = 1;
if (1 == val || 2 == val)
  # 処理
end

つまり、こんな感じです。

val = 1
if [1, 2].include?(val)
  # 処理
end

val = 1
if 1 == val || 2 == val
  # 処理
end

もしかすると、開発の現場によっては「if文には必ずカッコを付けるべし」というコード規約があるのかもしれませんが、個人的な観測範囲ではそういうルールは見かけたことがありません。

あと、余談ですが、2つ目のif文は僕だったら "val == 1" のように書きますね。
この方が「valが1だったら」というふうにコードが読めるので。

val = 1
if val == 1 || val == 2
  # 処理
end

(2017.6.30 20:00追記)
「val == 1 ではなく 1 == val と書くのは、間違って val = 1 と書いてしまって val に値を代入してしまうバグを避けるためじゃないか」という意見が散見されました。
なるほど、そういえばそういう話を昔どこかで読んだ気がします。

ただ、 val == 1 も 1 == val も、どちらもメリットとデメリットがありますね。

  • val == 1 なら読みやすいが、間違って val = 1 と書いてしまう可能性がゼロではない。
  • 1 == val なら間違って val を書き換えてしまう可能性はないが、若干読みづらい。

こうなると、好みの世界のような気がします。
僕はどっちかひとつを選べと言われたら、 val == 1 を選びます!
僕は読みやすさの方が大事ですし、 val = 1 と間違って書いてしまったことも今のところありません。

「いやいや、そうは言っても・・・」と話し出すと宗教論争になってしまうので、これはみなさんが自分の開発チームでどちらがいいのか(もしくはどちらでもいいのか)を話合ってもらうのが一番よいと思います。

まとめ

というわけでこのエントリでは、WEB+DB PRESS Vol.99の「Rubyで学ぶ!良いコードって何だろう?」に出てきたサンプルコードを、個人的な基準でコードレビューしてみました。

いやあ~、我ながら実に細かいですね。
とはいえ、「良いコード」を議論するのであれば、サンプルコードも徹底的に隙の無いレベルにしておいた方がいいと僕は思いました。
「この部分は参考になるけど、ここはあまり参考にならない」というのも変なので。

もちろん、「良いコード」「悪いコード」の判断基準は個人によって、そして開発の現場によって多少異なるものです。
みなさんの中には、僕が挙げた指摘事項を読みながら「えー、それはそのままでもいいじゃん」と思った人もいるかもしれません。

良いコードとは何か、もっと真剣に考えてみたい!という方はぜひ、WEB+DB PRESS Vol.99を手にとってみてください。
記事を読みながら「うん、それはそうだ!」「なるほど、そういう考え方もあるのか」「え~、このコードは微妙かも」・・・といった議論を、開発チーム内でやってみると面白いかもしれません。
この特集記事と僕の今回のエントリをきっかけにして、みなさんの「良いコードってなんだろう?」を追求してみましょう!

WEB+DB PRESS Vol.99

WEB+DB PRESS Vol.99

  • 作者: ?橋健一,谷口禎英,井本大登,山崎勝平,大和田純,内村元樹,坂東昌哉,平田敏之,牧大輔,板敷康洋,大?浩崇,穴井宏幸,原口宗悟,久田真寛,ふしはらかん,のざきひろふみ,うらがみ,ひげぽん,池田拓司,はまちや2,竹原,片田雄樹,渋江一晃,WEB+DB PRESS編集部編
  • 出版社/メーカー: 技術評論社
  • 発売日: 2017/06/24
  • メディア: 大型本
  • この商品を含むブログを見る

お知らせ:「プロを目指す人のためのRuby入門」もぜひ!

Rubyでもっと良いコードを書きたい!もっとRuby言語に精通したい!という方は、現在執筆中の「プロを目指す人のためのRuby入門」も参考になるはずです!
この本は2017年11月発売予定です。
詳しくは以下のエントリをご覧ください。

あわせて読みたい

「きれいなコード」や「良いコード」というお題を出されると、黙ってはいられないんです、僕。

じゃあなんで「きれいなコード」や「良いコード」にこだわるのかというと、こういうエピソードがあるからです。

いや、なんか偉そうなこと書いてるけど、僕もRubyを始めて間もない頃はみんなからボコボコにされてたんだった・・・。