Meblog

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

なぜあなたの発注するソフトは高いのか

ちょっと古いネタなんだけど、こういうのが話題になっていた。

toushichannel.net

ソフトウェア会社にテキストを右揃えにする発注をしたところ、300万円とられたという。これだけでは詳しいことは分からないが、「癒着」という言葉を使っている以上、納得感の到底得られない値段設定だと著者(の妹)は考えていると推察できる。

この例が特段極端な例とは思えない。IT業界では、この手の話はよく耳にする。発注側と受注側の値段の感覚が全くあっていないのだ。

「たかだかテキストのレイアウトを変更するのに300万とは、どういうことだ」「Wordなら数秒でできる操作だ」とあなたは考えるかもしれない。しかし、システムというのは非常に複雑なものであり、「テキストを右寄せ」の一言で済まされる問題ではないかもしれないのだ。

たしかに最終的には見た目が単にテキストが右に寄るだけのことだ。しかし、元のシステムが複雑怪奇であったり、巨大なものであったりすると設計や試験の工数が莫大になってしまう。そもそもあなたの要望は「テキストを右寄せ」だけで終始するものだろうか。どの画面を右寄せにするのか、しないのか、文字数が複数行にまたがる場合の挙動はどうなるのか、英語や中国語などの外国語はどうするのか、画面表示時と印刷時で違うのか、他に細かな要望があったりしないだろうか。

また、要望の少なさにかかわらず、試験はこれまでどおりのものを行わなければならない。今まで開発してきたシステムに影響がないかを確認するためだ。これを「退行試験」と呼ぶ。さらに納品に際しては仕様書、設計書、試験書に加え、内部での会議や外部監査などを踏まえると、3人月の300万という金額は別段に高いというわけでもないのだ。

とはいえ、あなたは開発側の都合を押し付けられても困る、と考えるだろう。なぜなら、我々が普段用いているソフトは安い。個人用のソフトでも、オフィス用途のものなら1万円前後で手に入るし、プロフェッショナルなツールでも10万円程度で手に入る。なぜ発注すると異様に高くつくのだろうか。

ソフトウェアの強みは、コピーを繰り返すことで価値をそのままに製造コストを極端に下げることにある。例えば古きよき時代のパッケージを考えてみよう。とあるパッケージ用のソフトウェアを制作し、10人を一年間稼働させたとする。これは人件費だけでも1億円以上のプロジェクトだ。これを1万本売り上げたとする。そうすると開発コストは一本1万円にまで抑えることができる。単純に割り算にはならずに、これにパッケージのメディア料と配達コストが上乗せさされるのだが、メディア料といっても光学ディスクでかなり安く、配達コストも、ソフト開発のコストから比べると、無視できるような額になる。

インターネットを用いると更に安くなる。パッケージは必要なく、宅配の必要もない。サーバ代や通信料のみで配布できる。これがソフトウェアのビジネス上の強みのひとつである「スケーラビリティ」である。市場の大きさに関わらず、同じコストで対応出来るのだ。しかし、これは逆もしかりである。つまり、ソフトウェアを使用する顧客が少なければ少ないほど、ソフトウェア単価は大きく上がってしまう。

先程の例に戻ると、右寄せにするカスタマイズは発注元のみへのカスタマイズであった、と考えられる。この場合、開発コストは単純に作業分がそのまま顧客が全額負担しなければならない構図となる。あなた専用の料理人を一人雇っているようなものだ。開発コストを分散できず、非常に高価になってしまうのだ。

日本の多くのSIerは、ソフトウェアのカスタマイズで大きく収益を上げているときく。しかし、多くのカスタマイズは特定の顧客専用のものであり、繰り返し使うことは容易ではない。そのため、スケーラビリティは活用できず、大きく価格が上がってしまう。カスタマイズで動かす人員の人件費がこのソフトウェアの料金にそのまま上乗せされるためだ。

本当にあなたのカスタマイズは必要なものなのだろうか。あなた専用の料理人を雇ってまで行わなければならない発注なのだろうか。そのカスタマイズが存在しないということは、業界標準から少しずれた業務であるといえるが、業務を業界標準にキャッチアップしなければならないのではないだろうか。

このように、そもそもそういった特殊な要件は出さないのがベストな選択肢といえる。この判断は非エンジニアにとって非常に難しいものになるかもしれない。それでも、カスタマイズに対する肌感覚の価格というものが多いに参考になるはずだ。そして、重大でないものは、社内の業務改善により対応するべきだろう。

何者であるかということ

もともと同じ職場でもう転職してしまった元同期と近況報告がてら2人でのみにいった。

当時同じ部署にいてAndroid開発を担当していた。しかし、元同期といっても、研修後すぐに辞めてしまったので、あまり職場のカラーには染まってない様子だった。今はRailsのエンジニアとして働いているらしい。僕も偶然にも今Railsエンジニアをしてるよ、とかm思い出話に花を咲かせているうちに、そういえば、あいついまなにしるの、と訊いてきた。あいつってのは、3人でつるんでいたもう一人の同期だ。

僕はまだそのもう一人の同期とはランチを一緒に食べるし、今はもう違う部署に異動になったとはいえ、仕事のことも事細かに聞いているはずだった。でも、何をしているか、という質問に対して、簡単には答えられなかった。もと開発ではあるが、今は開発をしていない。そもそもうちの会社の開発ってのは、多くのSIerが意味するそれで、要するにエンジニアなどではなく、単なる外注管理だったりする。でも、それでもない。営業でもなければ、SEでもない。この男に彼が今何をしている人間なのか、どう説明していいかわからない。

果たして、この話を後日同期に伝え、何を仕事としているのか聞いてみたが、やはりわからなかった。営業、SEと開発の橋渡し的な窓口をしているらしい。よくわからない。

    • -

SIerではあるものの、仮にも開発会社に身を置いていながら、自分の立場をひとことで言い表せないのは、もどかしいものがある。もどかしいというより、自分の存在意義に疑問を呈さざるを得ない。

僕も前の部署にいたときはそうだった。開発にいるものの、やっていることは外注の管理だった。では、仕様決めなど上流工程を担当していたかというと、そういうわけでもない。仕様は仕様で決めるチームが別にあるのだ。かといって、プログラミングはもちろん、設計をするわけでもない。外注管理というからには、開発リーダのような任を負っていたかというと、それは先輩がやっていたのだ。当時も自分が何をやっている人間なのかと自問してみたが、「開発事務」だな、と結論づけた。様々な開発にまつわる雑用をこなす人間、といった意味だ。あまり開発のたのしさを得ることができていなかったし、当時もそれで悩んでいた。

今は、運良くいわゆるエンジニアとしての仕事をでき、存在意義を得ている。何をしているかと訊かれたらエンジニアをしていると堂々と、簡潔に答えられるだろう。

では、当時の僕は存在意義はあったのだろうか。誰かの仕事のための仕事をしていたといわざるを得なかったのではないだろうか。あるいは自分の部の仕事を守る仕事だったかもしれない。しかしそれを議論するには若すぎたのだった。

    • -

転職していった元同期は外資系でしかも規模感も僕らの会社よりも小さなソフトウェア会社で働いているらしい。立派にエンジニアとしてだ。そんな彼に、もはや我々の業務やひいては会社の中での存在意義を伝えることは難しい。

一方で何をしているか毎日顔を合わせている僕でさえ説明が困難な同期が、社会からもネグレクトされるのではないかと危惧するばかりなのだ。

静電容量無接点なキーボード、「NiZ Plum」を買ってみた

ので、動画をとった。
今まで使っていたFilcoの茶軸と比較

前半がFilcoで後半がNiZ。

静電容量無接点ってのは、東プレのRealforceとかPFUHappy Hacking Keyboardなんかの高級キーボードに使われているスイッチの種類。
これがなかなか高かったんだけど、最近中国メーカから色々出てきており、それもなかなか安くて良いという噂をきき、買ってみた次第。

https://www.amazon.co.jp/NiZ-%E9%9D%99%E9%9B%BB%E5%AE%B9%E9%87%8F%E7%84%A1%E6%8E%A5%E7%82%B9%E6%96%B9%E5%BC%8F-75%E3%82%AD%E3%83%BC%E3%83%BB%E3%83%AC%E3%83%88%E3%83%AD-%EF%BC%88%E3%82%B0%E3%83%AC%E3%83%BCMIX%E3%83%9B%E3%83%AF%E3%82%A4%EF%BC%89-%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9E%E3%83%96%E3%83%AB/dp/B074JV7TDT/ref=pd_lpo_vtph_229_tr_t_2?_encoding=UTF8&psc=1&refRID=TSMZCR5DMM13X9HBRVJB

今までのFilcoはメカニカルキーボードと呼ばれるやつで、デスクトップについてるやつよりも随分いいんだけど、なんとなくちょっと硬い気がしていた。んで、静電容量無接点のものを買ってみた。

感想としては、Filcoはカチカチと打つのに対して、NiZはスコスコという感じ。キャラクターが違いすぎて、全然比較できない。良い悪い、というのはなくて、もはや個人の好みの問題な気がする。

NiZの明らかにいいところとしては、

  • 安い: 国産の同種のキーボードの7割から半分くらいの値段帯だと思う。
  • 高機能: 専用のソフトで完全にリマップし、キーボードのファームに書き込むことができる。
  • 小さい: HHKB並の大きさ。ファンクションキーがある分やや大きい

といったところ。今のところ、悪い点としては、以下が挙げられる。

  • 安っぽい: デスクにおくと、ややガタツキがある。薄い紙を敷く必要がある。
  • キートップがダサい: プログラマブルなキー配置のくせに、やたらファンクションキー押下時の動作が書き込んである。好みでない。
  • わかりづらい: マニュアルは添付されておらず、箱についてるQRコードからサイトにとび、更にそこのGoogle Driveからマニュアル類をおとす必要がある。やや不親切

今のところ、こんなところか。
また何かわかったら、追記しよう。

20%ルール

「20%ルールをうちでもできないかなあ」

と隣の先輩が言った。


20%ルールとは、いわずもがなGoogleなんかで仕事とは別のプロジェクトをあてる有名なアレだ。色々バリエーションはあるようだが、ある一定時間通常の業務とは全く関係のないプロジェクトを、休憩時間や終業後ではなく、勤務時間中に充てられるという制度をさす。現在は状況は違っているようだが、その昔Googleや他のスタートアップが導入し、イノベーションを促進したとか、してないとか。

従業員側からすると相当魅力的な制度のように思える。もし現時点であまりプロジェクトと関係ない事をすると上司には不真面目に見られてしまう。Webアプリを内製している我々にとっても、いろいろ堂々と実験ができるわけで、たしかにかなり興味をそそられる。

実は導入を試したことはある。

厳密には今の組織ではないが、前身の組織で取り入れたチームがあった。マネジメント側にとっても、20%ルールを取り入れることは結構容易い。直属の上司が単にそれを容認すればいいだけのはなしだ。しかし、うまくいかなった。理由は、そのルールを取りいれても仕事をしてしまうためだ

不思議なことに20%ルールがあっても、そもそもその制度を使う社員がいなかったのだ。その理由をなんとなくサボっているように見えてしまうためなのかとマネジメント側は考えた。そこで、時間を固定し、ある曜日のある時間帯は別プロジェクトを行なうこと、と制度を改善したが、別の部署からの問い合わせ対応などで潰れてしまっていたり、優先度の高いタスクを消化してしまい、また社員側も単に就業時間が伸びてしまうだけだ、などの意見が出、結局長くは続かなかった。

そもそも我々の順番が逆であったのだ。この制度の有無にかかわらず、優秀なプログラマは勝手に何かを作ってしまうものなのだ。

会社としても、もし社員が勤務時間外に有用なプロジェクトを持ってしまえば、その優秀な人材のリソースはそのプロジェクトに消え、転職・独立のリスクが高まるし、知的財産もその会社は主張しにくい。そうなってしまう前に勤務時間中にサブプロジェクトに没頭させるほうがよほどその会社にベネフィットがある。リスクを逆手にとり、制度化してしまうのが、この20%ルールなのだ。

実態がない中、もなくルールだけ取り入れても仕方がない。

旧態然とした我々には勝手に自分のアプリを書くような不真面目な社員はおらず、そのため、20%の枠組みを入れても不真面目にはなれなかったのだ。仮にこの制度を取り入れたとしても大きくなにかがかわることはないだろう。


そんなことを考えながら、

「そんなのがあったら、いいですねー」

と答えたのだった。

働き方改革

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=\"\""

これで通った!!