Meblog

このブログ記事は個人の見解であり、所属する組織の公式見解ではありません

働き方改革

f:id:gntm_mdk:20171027005034j:plain

昨今、日本では働き方改革が叫ばれている。

生産性という単語はソフトウェア業界では相当聞き慣れた単語で、既に相当研究がなされている。にもかかわらず、日本のSI業界では人月商売が横行して、プログラマの生産環境はほとんど改善されていない。Web業界は確かに生産性を気にしているところも多そうだが、シリコンバレーのまねごとをしていると思われている節もあるし、なかなかグローバルに出ていける企業も少ないのが実態だ。ガラパゴスな日本でのソフトウェア産業はまだまだ世界的な地位を築けていないのが現状だろう。

そんな中こんな記事がHackerNewsに出てきた。

The first 150 days of van life - Ruby on Wheels

ドイツからキャンピングカーで旅をしながらプロジェクトに参加しているプログラマの様子だ。

こういうのは夢でもあるし、本来こういったことが許される職業なんだなあ、と驚きを感じる。そもそも車で旅に出よう、なんてのは大陸の人間の発想だな、と思うんだけれども。でもこんな話もあるわけで。

旅と執筆とプログラミング

結局、技術者たるもの自分の価値は自分である程度自分でブランディングしたもの勝ちなんだろうな、と思う。働き方改革というのもいち労働者としては議論されるべきであり、労働者としての権利を主張していかなくてはいけないんだけども、こういう突き抜けかたもあるわけで。ソフトウェア技術者のサラリーマンとしての生き方と職人としての生き方を端的にあらわしているなと思う。

上の写真は今年モロッコにいったときのもの。僕の会社は夏休み1週間まるごとくれるんだけど、そんなものを有難がってる場合じゃないな、これは。

手続き型からのパラダイムから抜けられない君へ

序章

JavaやCなんかの手続き型から入ると、Rubyやその他スクリプト言語の独特な書き方や新しいパラダイムなんかはなかなかとっつきにくい。

その中でも脱初心者的な要素でありながらも特にとっつきにくいHashクラスのeach, map, select, reduce から始めてみよう。

まずは簡単なサンプルから

まずはrubyの簡単なサンプルから。おそらくいちばんとっつきやすいものは each 文だろう。

僕が手続き型のパラダイムから抜け出せないときには、for文やwhile文を使って以下のような処理を書きがちだった。

array = [1,3,5]
while i < array.length
  print "#{array[i] * 3}\n"
  i += 1
end

これはひどい!ただ、当時の僕はrubyで、他言語で慣れ親しんだfor文を書きたかったのだ。しばらく経つと以下のような書き方に変わっていった。

array = [1,3,5]
for val in array do
   print "#{val * 3}\n"
end

当時としてはまあ満足していた書き方だった。Javaのfor-inループのような感じだし、書いていたときには特に無駄を感じていなかった。

each文の活用

しかし、他のRubyコードを見るにつけ、これをよりrubyらしく書きたい欲望がふつふつと湧き出てきた。というわけで、ステップバイステップで手続き型から抜け出してみよう。

array = [1,3,5]
array.each do |val| 
  print "#{val*3}\n"
end

[1,2,3] という配列に対し、それぞれ同様の演算を行なう。each文の使いどころははじめの一歩としては非常に扱いやすいのではないかと思う。

もう少し複雑に

以下のようなリストがあるとしよう。

mail_list = [
  "yamada-taro@example.com",
  "suzuki-hanako@mail.jp",
  "takahashi-ichiro@example.uk",
  "matsumoto-yukihiro@ruby.com"
]

このリストから以下のようなハッシュを作成したい。

[
  {
    family_name: "yamada",
    given_name: "taro",
    domain: "example.com"
  },
  {
    family_name: "suzuki",
    given_name: "hanako",
    domain: "mail.jp"
  },
  :
]

これを手続き型っぽく書くとなかなか煩雑になる。僕が書くとしたらこんな感じか。

result = []
for ml in mail_list do
  name = ml.split("@")[0].split("-")
  given_name  = name[0]
  family_name = name[1]
  domain = ml.split("@")[1]
  result << {family_name: family_name, given_name: given_name, domain: domain}
end

少し恣意的な気もするが、大方このような書き方になるだろう。
単純に各要素への計算結果を予め宣言しておいた配列(result)に順次格納するコードだ。これをeachを使った文法に書き換えてみる。

result = []
mail_list.each do |ml|
  name = ml.split("@")[0].split("-")
  given_name  = name[0]
  family_name = name[1]
  domain = ml.split("@")[1]
  result << {family_name: family_name, given_name: given_name, domain: domain}
end

中身は全く変わらない。for-inループが単純にeach句に変わっただけだ。ただ、これだけでも手続き型のパラダイムから移ってきた人間には大した変化に見える。

mapの登場

ここで、mapに登場してもらおう。mapもeachと同じく、各要素に対して何かしらの処理をするというメソッドだ。上記の例をmapで書き直してみよう。

mail_list.map do |ml|
  name = ml.split("@")[0].split("-")[0]
  given_name  = name[0]
  family_name = name[1]
  domain = ml.split("@")[1]
  {family_name: family_name, given_name: given_name, domain: domain}
end

これでも中身はあまり変わっていないように見える。しかし、大きな変化はresultがなくなったことだ。決して省略しているわけではない。each文では、スコープの問題を解決するため、resultを予めループの外で宣言してやる必要がある。しかし、mapでは、それ自体が最終の評価値を返すので、スコープ外で変数を宣言してやる必要がないのだ。結果を取り出すには、以下のようにすればいい。

result = mail_list.map do |ml|
  :
end

このmapは結果をそのまま返すという事実に気づくと、これをメソッドチェーンにしてつなぐこともできるという事実にたどり着く。これこそがmapの強みの一つである。

ここで、メソッドチェーンについて復習しておこう。メソッドチェーンはあるメソッドの戻り値を直接用いる記法のことだ。以下に例を示す。

 "good morning, sir".delete('. sir').capitalize 
#=> "Good morning"

ここでは、"good morning, sir".delete('. sir')"good morning"を返すため、これにさらにcapitalizeをつなげ、結果を得ている。

先程の例に戻り、メソッドチェーンを用いて書き直してみよう。

mail_list.map {|ml|
  ml.split("@")
}.map{|ml|
  {name: ml[0].split("-"),domain: ml[1]}
}.map{|ml|
  {family_name: ml[:name][0], given_name: ml[:name][1], domain: ml[:domain]}
}

かなり見た目が変わった。そして、可読性も上がったと思う。

括弧が初出なので、少し解説しよう。

{}の括弧はdo-endと同じ意味だ。人によって使い分けが違うと思うが、僕はメソッドチェーンを使うときには{}括弧を使うようにしている。

例が単純で、少し冗長な書き方ではあるが、mapの威力を端的に表せていると思う。

また、関数型という味方をすれば、mail_list自体の値は変わっていないことにも注目すべきである。この処理を何度処理しても同じ結果が得られるはずだ。

select

その他にも例を見ていこう。例えば以下のリスト(再掲)から .com で終わるメールアドレスを抜き出してみる。

mail_list = [
  "yamada-taro@example.com",
  "suzuki-hanako@mail.jp",
  "takahashi-ichiro@example.uk",
  "matsumoto-yukihiro@ruby.com"
]

selectでは、ブロック({}で構成されるもの)の中が真であるものだけを抽出する。

mail_list.select {|ml|
  ml.include?(".com")
}
# => ["yamada-taro@example.com", "matsumoto-yukihiro@ruby.com"]

そして、ここから先程の例である、 family_namegiven_name ,domain への分割処理につなぐことができる。

mail_list.select {|ml|
  ml.include?(".com")
}.map {|ml|
  ml.split("@")
}.map{|ml|
  {name: ml[0].split("-"),domain: ml[1]}
}.map{|ml|
  {family_name: ml[:name][0], given_name: ml[:name][1], domain: ml[:domain]}
}
# => => [{:family_name=>"yamada", :given_name=>"taro", :domain=>"example.com"}, {:family_name=>"matsumoto", :given_name=>"yukihiro", :domain=>"ruby.com"}]

reduce

reduceはこれら Hash のメソッドの中でも取り扱いが少し難しい。そのため、詳しい説明ははぶく。以下の例では、ドメインが .com で終わるメールアドレスの数を出している。

mail_list.reduce(0) {|sum,ml|
  ml.include?(".com") ? sum + 1 : sum
}
#=2

ml はこれまで同様、 mail_list の要素を返す。 sum は一度目は引数の値、それ以降は前回の結果を返す。そのため、合計値等を算出するのによくつかわれる。

結び

以上、長々と説明した。

ステップ・バイ・ステップな説明を意識したため、冗長ではあるが、理解のギャップがないように構成したつもりだ。

僕はJavaからrubyを勉強したときになかなかこの文法になれなかった。しかも、これらを避けて手続き型で書いても当然同じ結果が得られる。

しかし、せっかくRubyという素晴らしい道具を手に入れたのだから、新しいパラダイムをとりいれ、脳みそを活性化するべきである。

僕のプロジェクトではあまり使わないが、それでも細かいツールやワンライナーでは頻出するため、覚えておいて損はないと思う。

認証付きプロキシでgradlewビルドする

前座

僕の職場の低スペPCでは、だんだんAndroidStudioが満足に動かなくなってきた。普通に使う分にはギリギリ使える感じなんだけど、ブラウザと行き来したり、同時に画像編集をしたりなんかは望むべくもない。そこで、以前よりEmacsでコードを買いてターミナルよりgradlewコマンドでビルドしていた。そんなにターミナルにお世話にならない人たちには馴染みがないかもしれないけど、以下のコマンドでターミナルからビルドすることができる。

$ ./gradlew assembleDebug

毎回場所を忘れるので、find . -name "*.apk"で探してくる。ちょっとビルドしたいときには非常に便利だ。

問題

ところが、最近になって突然、以下のエラーが出るようになった。

Exception in thread "main" java.lang.RuntimeException: java.io.IOException: Unable to tunnel through proxy. Proxy returns "HTTP/1.1 407 Proxy Authentication Required"

会社の環境は認証付きプロキシを使って外部と接続してるんだけど、その認証がうまくいっていないっぽい。以前は使えたはずなのに。。エラーの内容からすると、プロキシを通そうとしているんだけど、認証は通らないようだ。奇妙なことに、AndroidStudioではうまくビルドできている。そんなわけで、多少の不便は感じつつも数ヶ月放置していた。

解決

Java8のどこからかのバージョンからBasic認証が禁止されていたみたい*1。そしてこれを無効化し、Basic認証を使えるようにするにはコマンドライン引数を渡してやればいいみたい。

gradlewを開き、以下を追加する。

DEFAULT_JVM_OPTS="-Djdk.http.auth.tunneling.disabledSchemes=\"\""

これで通った!!

rubyを道具として使う

f:id:gntm_mdk:20170809205057j:plain

何か仕事を自動化したいときに、最近Rubyを多用するようになってきた。これは、今のプロジェクトでRuby on Railsを使っていることにも起因している。もちろんWindowsのバッチやLinuxシェルスクリプトでもいいんだけど、あえてRubyを使うことにメリットがあって、

  • 文法が明快。Windowsのバッチで複雑なことをさせるのは無理がある。。
  • Rubyのお勉強になる。

そんなわけで最近はもっぱらRubyなわけだ。

Thor

gemのThorを使い始めた。使い方や入門編は以下の記事が詳しい。
qiita.com


ThorはCUIアプリケーションのフレームワークだ。
毎回Rubyスクリプトを書くたびに、引数の処理を各々のスクリプトで個別に書いていたんだけど、これが以外と厄介だったのだ。Thorを使うと引数のパースをある程度やってくれるし、使い方のDescriptionを書くように設計されているので、簡易なドキュメンテーションもこれで十分だ。たとえ公開するつもりがなくても、何かメモしておかないと、自分ですら使い方を忘れてしまう)
ミニマムなサンプルは以下のとおりだ。

require 'thor'
class Command < Thor
  desc 'hello NAME', 'say hello to a given name'
  def hello (name)
    puts "hello, #{name}"
  end
end
Command.start

gemにする

これを以下の記事のとおりにgemにまとめ、社内のgitサーバに公開し、配布することもある。
qiita.com

こうすることで、ソースコード管理と配布を一元化できるし、バージョン管理も楽々になる。

gitサーバからgemインストールするには、bundlerを使うのがお手軽だ。一方でgem installのようにやりたければ、specific_install というgemでデフォルトのgemレポジトリ以外からインストールできる。一度導入すれば、bundlerを使うよりもお手軽だ。
github.com

とはいえ、ある程度のITリテラシが求められるよね、これじゃ.. そんなわけで、僕もまだ社内で啓蒙中だ。

gnuplot

こっちは割りと細かい話。

業務上よくあるのが、エクセルとの連携だ.. かならずどこかでエクセルによる作業がどうしても発生してしまう。確かにエクセルは便利なんだけど、大量にレコードがある場合、すぐにハングしてしまう(感覚的にいえば、1000行を超えるともう危ない。GUIが止まっているだけでしばらくするともとにもどることもあるが..)。また、ハングしなくとも、大量のシートやグラフを相手するときに、コピペ作業や、右クリックしてなんちゃらみたいな反復作業が非常に多く発生してしまう。プログラマにとってはいつまでたっても関わりたくない相手だと思う。

というわけで、なるべく作業フローからエクセルは排除したい。今まで表計算部分はcsvでなんとかすることができる。厄介なのはグラフ化だ。そんなときに便利だったのが、gnuplotのgemだった。
github.com


gnuplotは、ほとんど使ったことがない.. 大学でも、何やら複雑な関数のグラフ化はしたものの、実用上はあまりそういった使い方はせず、むしろcsvのプロットに使うわけで。Googleを検索しつつ、ぽちぽちとスクリプトを組んだ。

やってみると、タイムスタンプの処理なんかは割りと優秀で、案外使い勝手が良い。ただ、あまり色数が使えないのが玉にキズ。最近のエクセルのカラーパレットは小洒落ているからねえ。

ほかにもいろいろ

基本的に書捨てのアプリでもThorを使って書くようになった。社内Git(というより部内Gitサーバ)とはいえ、ちょっと全てを上げるのは気が引けるものばかりだ.. まあ、プライベートリポジトリにして他人からは見えなくするのアリだけどね(そもそもITリテラシが低くてGitがマトモに使うのは僕だけというのは内緒だ)。

org-mode で画面キャプチャをペタッとはりつける。

org-modeで画像貼り付けたい

org-mode使っていて、Evernoteなんかと比べたときに圧倒的に不便だな、と感じるのは、画像を取り扱っているときだ。さっくりメモをとったり、文章を構造化したりするときなんかには非常に役に立つのだが、何かの画像を切り貼りしたいときには、いちいち画像をimg配下なんかに保存して、そのパスを記述する、なんて面倒なことをしなければならない。

どうせ同じことで悩んでいる人がいるんだろうな、と思ってサーチしてみると、あっという間に見つかった。PCの画面キャプチャをorg-modeの画面にペタッと貼ってくれるelispだ。名前はそのままorg-attach-screenshot。

github.com

レポジトリ名はorg-screenshotだが、MELPAではorg-attach-screenshotで引き当てることができる。

M-x org-attach-screenshot で、適当にEnterを押下してると、Emacsがバックグラウンドになり、カーソルが十字になる。んで、範囲選択してキャプチャをとることができる。画像はデフォルトだと、編集中のorgファイルの下にdataフォルダが作成され、こんなかに入る仕組み。

これはこれで十分なんだけど、以下の点に不満が残った。

  • HTML出力するときに、画像がサムネイル化されない
  • Mac (OS-X)だと、imagemagickよりscreencaptureのほうがよさそう

改造

結論から言うと、ちょっと改造した。

github.com

差分は以下。

add thumbnail feature and adopt os-x. · medaka/org-screenshot@98ecee8 · GitHub

これで、HTML出力してみると、以下のような感じになる。

f:id:gntm_mdk:20170623004146p:plain

これだと普通だな。。オリジナルはサムネイル化されないんで、大きなキャプチャとると、全部はみでちゃうんよね。。

詳細

まあ、サムネイルに関してはいいでしょう。単に画像縮小してリンクはっただけ。

screencaputureに関して。オリジナルはimagemagickのimportっていうコマンドを用いてスクリーンをキャプチャするみたいなんだけど、同等の機能がOS-Xにはデフォルトでscreencaptureってのが入っている。importってのがサクっと動かなかったのと、screencaptureのほうがいちいちimagemagickを使わなくてすむということから、こちらに換装。

TODO

たぶんやらないであろうTODO

  • OSによって切り替え
  • サムネイル化するかのフラグを切り出す
  • サムネイル化したときの画像サイズを指定する変数を切り出す

終わったらプルリクエストを投げるのだ。

参考

実は更にオリジナルはるびきち氏のscreenshot.el。org-mode使わない人はこっちのほうがいいかも。

EmacsWiki: Screen Shot

アジャイルとハイプ

全体の工程にウォーターフォールモデルを採用しているプロジェクトも未だ多いと思う。

こういったプロジェクトでは、設計・製造工程(プログラミング工程のこと)のあと、何段階かの試験工程がある。試験工程ではブラックボックステストを多分に含み、要望元のユーザテストのようなものも含む。そうすると、当然基本的な設計に関わるような不満も噴出する。こういった機能がほしいだの、ボタンはこういう配置がいいだの、そういう要望だ。

こういった意見はくだらないものもあるが、なかには耳が痛いものも多い。当然対応すべきだった事を指摘されることもあり、エンジニアとしては是非対応したいところだ。しかし、試験工程に移った段階で開発要員は絞られており、残ったリソースで修正するのは容易ではない。そもそもウォーターフォールの概念から言うと、禁じ手でもある。なぜなら、ウォーターフォールにおける各工程の終了とは、その段階での完璧な品質の担保を意味しているため、要望による修正は、全ての工程をやりなおしを意味するからだ。幾度となく言われていることであるが、これがウォーターフォールの最大の欠点である。

すると当然、アジャイルを導入するか、という話が持ち上がってくる。

simplearchitect.hatenablog.com

アジャイルであれば、途中の要件変更に強くなるはずだ、という目論見である。アジャイル自体もう新しい技術でなくなっているし、コンサバティブな職場でも幾度となく検討されていることだろう。しかしこれも何度となく挫折を味わうことになる。最近携わったアジャイル開発も例外ではなかった。この失敗は、人員の問題や企業風土の問題も確かに少なからずある。しかし、今回言及したいのは、それ以外の部分、「ハイプ」だ。

        • -

ウォータフォールでは全ての機能を完全な仕様書におとしこみ、それを開発が順次実装していく。仕様書は開発工程開始時点では完璧であるはずなので、これをどれだけ忠実に実装していくかが問われるわけだ。しかしひとたびこれを「アジャイル」にしてみると、企画からしてみると、開発開始時点で仕様書は完成してなくてもよい、という解釈になる。企画からの要望はずるずると開発終盤まで変更される。開発終了時には、ぎりぎりまで要望変更の対応に追われるため、なんとか動くものはできる。しかし、品質も中途半端、工数も大きく膨らんでしまう。

企画が開発完了した製品を実際にテストする。すると、思った以上に齟齬が多いことにおどろく。齟齬を紐解こうと思っても開発途中には様々なバージョンの要求仕様書があり、どれが結論かも全く判然としないのだ(非エンジニアはいまだにバージョン管理には相当疎いことに留意しなければならない)。本来、いかなる開発においても、企画と開発が一体になっていない場合、ドキュメントは残すべきである。しかし、「アジャイル」という言葉を導入した途端、多大なコミュニケーションにより仕様を担保できると考えてしまい、完成したドキュメントはなくなってしまう。

簡単にいうならば

「なぜドキュメンテーションしなかったの?」
「アジャイル開発だったからです」

ということになる。アジャイルに過度に期待をよせてしまった「ハイプ」だ。

このハイプは、昨今の開発で、多く見られる現象のひとつのように感じる。例えばAIにしても中身や特徴、処理方法は別にして、とにかく「データ処理をうまくやってくれるもの」、VMは「サーバ上の処理をうまくやっていくれるもの」、先程のアジャイル開発はとにかく「開発工程をうまくやってくれるもの」、といった様にだ。ここでの教訓は肝心なのはハイプであるのは開発だけでなく、企画を始めとする間接部門にとってもハイプだということだ。開発には銀の弾などないということは、プロフェッショナルにとっては常識になっている。どれだけ便利な手法でもプラクティスを誤れば、全くうまくいかないアンチパターンになるリスクを孕んでいる。特に昨今の手法はこれさえやっておけばうまくいく、といった簡単なものではなくなっている。自分たちの状況に合わせたプラクティスの選択・構築が何より重大な命題になっていくだろう。この問題は、非エンジニアに説明するのは相当難しい。例えばアジャイルにとっても開発時のステークホルダーのスコープはどれだけか、開発規模はどの程度か、どの仕様が最低限必要なのか(これが一番むずかしい。少ない機能を実装しようとすると普通の人はサボっているとか生産性が低いなどと思ってしまうだろう)、などなど、様々なパラメータがあるが、これらが開発に影響にどのように影響するか。おそらく完全に理解してもらうのは不可能だろう。

        • -

そんなわけで、僕の周りのアジャイルだとかAIとかのバズワードの信頼度は下がるばかりだ。実際にはそれら自体ではなく、その言葉を誤用している僕らやステークホルダーが悪いのだけども。こういったことで時代の潮流に遅れないようにしたいと願うばかりだ。

ソフトウェアとビジネスの狭間

Hackernewsみてたらこんな記事が。

news.distrokid.com

ソフトウェア書きとしては気持ちのいい話だ。特にこのご時世、JASRACの話とも相まって、相乗効果的にその印象が強まってくる。

    • -

まあ、音楽面の話はさておき、ソフトウェアを書くことの価値とはなんだろうかと思うことがある。実際会社でソフトウェアは書いているけれども、それを責任もって事業化し、売上げてくれるのは別の部署の人たちだ。我々開発者がそのソフトウェアについての価値について直接考える機会はほとんど失われてしまっている。逆に我々に求められているのは品質やコスト、納期であり、いかにバグのないソフトを安く早く作るかということに主眼をおいている。それはそれで開発に集中できるのでいいのだけれど、ときには俯瞰して考えたい。

そもそもソフトウェアの価値とはなんだろうか。

古来、というより、欧米でよく言われる「ドットコムバブル」の時代、あるいはさらにその前時代、ソフトウェアの価値は複製の容易性にあった。一度ソフトウェアを書いてしまえば、ほとんどコストの無視できるメディア代だけで価値を増幅することができたのだ。それまで存在いたあらゆる製品は、生産に大規模な設備を要し、流通経路を確保し、顧客に届けなければならなかった。これがソフトウェアではほとんど無視することができたのだ。

さらに現代、更にその仕組は強化され、ほとんど物理的な物流に依存することなく、ソフトウェアの価値を顧客に提供することが出来るようになった。顧客にはプラットフォームとなるハードウェアすら準備する必要がなくなったのだ。そのため、顧客はほとんど設備投資する必要なく、ソフトウェアを手に入れることができるようになった。

しかし、おそらくソフトウェアを開発する総コストは根本的に変わってないというところに注目しなければならない。

あらゆる技術の革新でより早く高機能なものを顧客に提供できるようにはなってきている。しかしそこに投入するエンジニアのリソースは変わっていないように思える。顧客の要望も技術的革新に追従し、ハイレベルなものになってきているためだ。結果として、ソフトウェアをビジネス化するには、より少ない生産量で、より多くの顧客をかかえることが重要度を増すようになってきたのだ。

ここで僕は自分を振り返りたい。自分は開発としてこのソフトウェアの優位性を理解し、仕事をしているだろうか。誰か特定の顧客のためだけに愚直に価値を提供してやいないだろうか。

  • -

上記記事では、ほとんどの定形業務を自動化することにより極端に少ない人数での業務のオペレーションに成功している。言葉でいえば単純だが、自動化はソフトウェアの得意とする一分野であり、さらにそれをうまくスケールするところまで成功している。また、他のサービスと異なるのは、これが既存産業をおびやかしにかかっているところだ。強力な既得権を置き換えにかかるその様は、きっと開発者本人も予想しなかったことだろう。