seri::diary

プログラミングのこととかポエムとか

大学院修士課程に入学した

tl; dr

  • 会社員辞めて2018年4月から筑波大学のCS専攻の大学院修士課程に入学した
  • 分散深層学習の学習高速化とか大規模inputデータへの対応などに関する研究をする予定
  • がんばるぞい

大学院に入学した

2018年4月から仕事を辞めて筑波大学のシステム工学研究科コンピュータサイエンス専攻の修士課程で大学院生をやっている。
働きながら入学手続きしたり引っ越ししたりと色々やっていたので3月、4月はずっとバタバタしていたのだが、ようやく落ち着いてきたので近況についてまとめておこうと思う。

背景

一応5年ぐらいソフトウェアを書いて仕事をしてきた訳だけど、自分はCSの学位を持ってる訳でもなく、ちゃんとCSの基礎的な勉強したことがなかった。*1

そのせいか、業務要件をコードに落とし込むことはできても、「なぜこのアルゴリズムを使っているこっちのライブラリの方が早いのか」「こういうのを早くするためにはどういう最適化をすべきなのか」というような、技術に特化した課題に直面した時に、何もできないタイミングがちょくちょくあった。

特に、サーバーサイドで動作するバッチのパフォーマンスチューニングの面で、「とりあえずプロセスを増やせばスケールできるように設計したが、本当はもっと実装を最適化して必要なサーバ台数を減らすための工夫ができるのではないか?」ということが疑われるケースにおいて、自分の持っている知識の範囲では何もできないケースが多く、これはレベルの低いエンジニアリングだと思っていた。他にもいくつか似たようなケースに直面したこともあって、ソフトウェアのパフォーマンスをエンジニアリングの観点から改善できないことが自分の壁だと認識するようになった。

この壁を何とかしなければ自分のエンジニアとしての未来はないと思えた。ありとあらゆるものがサービス化される昨今で、今自分が作っているAPIやバッチすらも、GUIでモデルやパイプラインを定義して誰でも作れるようになるという予感もあったので、ウェブアプリケーションからクライアントにjson返すしか能がない自分はいつか職を失うだろうという危機感があった。

この壁を突破し、エンジニアとしての仕事を維持していくために自分に今何が足りないか?と考えると、明らかにインプットが足りていなかった。ソフトウェアというものを現場でのみ学んできた自分にとって、経験したことがない問題に対してほとんど何もできないという弱点があることに気づいた。その弱点が露骨に表れるケースの1つが前述のような大規模データの取り扱いだった。

そのため、5~6年前から、どこかのタイミングで仕事を辞めてインプットに専念する時期を作りたいと考えるようになった。しかし金銭的な余裕がない、卒業後に雇用され得るだけのスキルがない、という2つの理由で躊躇していた。しかし幸いにして、ここ数年でエンジニアとしてはそこそこのレベルになれたので最低限食うには困らないという気持ちになり、かつ貯金がそこそこ貯まったこともあり、やっていきが高まってきたので実行に移すことにした。

大学院という選択肢について

貯金があると言っても仕事を辞めないにこしたことはないので、当初は働きながら夜間に通える大学でCSを勉強しようかなと思っていた。しかし、日本だと夜間コースでCSが勉強できる大学は殆どなく*2、またカリキュラムを見ると基礎的な内容が多く、今の自分にとっては学びが少ないように思えた。通信教育系のカリキュラムもいくつか調べてみたけど、どれも基礎的な内容というかプログラミングのカリキュラムという感じで、どうにもしっくりこなかった。

なので、思い切って仕事を辞めて普通に大学院に行くことにした。
色々調べた結果、今の日本ではちゃんとしたCSを勉強するには昼間に通う大学院に行くしかないと思えた。その事実の是非はともかく、今自分が新しいことを勉強できる環境に身を置くとしたら、大学院で研究することが最も効率的な選択だと思えた。

入学までの経緯

別のブログに院試までの準備とか最近の様子とかをまとまているので、興味があればこちらを参照してほしい。
今後も学生生活ネタや研究ネタはこちらのブログに書いていく予定である。

serihiro-graduate-school.hatenadiary.jp

大学院で何を研究するのか

配属はHPCS研究室というHPC関連の技術要素を扱う研究室で、もともと興味があったMapReduce on HPCでの性能改善に関するネタを探そうと思っていた。しかし、MapReduceはあまりにレッドオーシャンで論文のネタになりにくそうという話になり、深層学習 on MapReduce(もしくはそれに類する分散環境)はどうかという話となった。

深層学習については以前に一度本を読んで写経した *3ぐらいの知識しかなく、TensorFlowやChainerもMNISTチュートリアルをやったぐらいの経験しかない。しかし、せっかく時間を取って研究するならやったことがない事の方が学びが多かろうということもあるので今は深層学習について論文を読んで知見をinputしている。同じ研究室内には深層学習を扱っている人はいないので何とか一人で頑張ってみる次第である。

最近の様子

大体、朝8時から19時か20時ぐらいまでは大学内にいる感じである。*4

M1の間に必要な単位をできるだけ稼いだ方がよさそうなので、5日間の平日のうち4日は何らかの講義を1日に1,2個履修している。CS専攻のカリキュラムページ はMkDocsで書かれていて読みやすくて良い。
今取っている講義は、自分の研究に関連する分散処理に関するものや、自分が単純に興味がある遺伝的アルゴリズムや数理最適化に関する理論などである。他にも、インテルの中の人が来て最近のインテルのCPUアーキテクチャに関する説明をしてくれる講義なんかも取っている。色々あって楽しい。

講義以外の時間は研究室にいて、今月は研究テーマを決めるための関連研究の調査をしている。まずは基礎的な所からということで、MapReduceや分散深層学習に関連するものを中心に読んでおり、や英語の論文を読んで要約する作業も最初はかなり時間がかかったが、2,3本読み切った辺りから段々と早くなってきた気がする。あと深層学習について復習するために ゼロから作るDeepLearningを最初から写経しながら読み直した。*5

それ以外だと、大学の図書館に行ってみたら大量に技術書があって、何時間でもいられそうな環境であるということを発見したので帰宅する前とかに図書館に寄って漁ってたりする。この土日も図書館に行って技術書を読んでいた。詳解LinuxカーネルとかTypes And Programming Languagesとかドラゴンブックとかの、普通に買うと余裕で7000円とかする本がタダで読めるのは大変ありがたい。大学にいる間にできるだけ読んでおきたい。

お金の話

「無職なのにお金はどうするの?」という話をよく聞かれるのだが、率直に答えると、必要な予算を計算したところ、自分の貯金を全部使っても若干足りなかったので、足りない分は親からの借金でカバーしている。
大学の入学金・授業料*6は国立なので大した額ではないのだが、2年間の住民税*7国民年金・保険*8・生活費が、必要予算の大半を占めている。2年間は旅行はおろか、技術系カンファレンスに日帰りで行くことも厳しくなりそうだが致し方ない。改めて自分が生きていくのに必要な予算を計算することで、ただ生きているだけで物凄くお金がかかるということを実感できる良い機会になった。卒業後は、借りた金を早く返せるように、また貯蓄を増やしなおすべく、前職以上に稼げるようになろうと決意した。

卒業後の話

もちろん就職はするのだが、どの企業を受けるかまでは決めていない。
できれば分散システムを利用したプロダクト開発に関われるソフトウェアエンジニアとしてのポジションに就きたいと思うが、具体的な就職活動的なものは来年以降にやることとし、今年は研究に専念したいと考えている。また、在学中のインターンについても行ってみたい会社がいくつかあるので、その辺りにも挑戦してみようと思う。

*1:独学でやった勉強と言えばパタヘネを読んだぐらいか。

*2:例えば東京電機大学には夜間に学べるコースがあるが

*3:その時の話は別エントリに書いた https://serihiro.hatenablog.com/entry/2016/12/22/000000

*4:前職時代がこんな感じだったのでそれをそのまま引き継いでいる感じ

*5:余談だが、最初の方の版は誤植が多いので、古い版を持っている場合はgithubにあるerrataを見ながら読んだ方がいい

*6:実際の額は http://www.tsukuba.ac.jp/admission/graduate/tuition.html を参照

*7:特にフルタイムで働いていた2017年度の所得で計算される2018年度分がとてもとても痛い

*8:1年目は関東ITSの任意継続の方が安かったのでそちらを使い、2年目からは国民保険の方が安いのでそっちに切り替える

どうやったら成長できますか的な質問にうまく答えるのが苦手だ

これまで散々意識高いポエムをこのブログに書いてきたのに、具体的に「こうすれば成長できるぜ」という答えが、うまく言葉にできないでいる。

大体、自分自身は成長したいと思ったことはない。
ただやりたいことが目の前に転がっていて、それを実現するためにうにゃうにゃ悩みながらやってきたら、結果として「とある時点の自分と比べると成長したな」と言える程度にはなっている、という程度。そもそも成長というものが良く分かっていない。

例えば、勉強すれば成長できるのだろうか。自分の場合、勉強のための勉強が大の苦手だ。
目的が分からず、その上興味も持てない勉強をするのがすさまじく苦手だ。なので勉強についてもうまく説明できない。残念。

基本的に、勉強は必要が生じたらやる、いわゆる「遅延評価勉強スタイル」を採用している。以下のサイトで紹介されているものに近い方法だと思う。大体これでうまく行っている。

d.hatena.ne.jp

以前高校数学を勉強したのも、古典機械学習を勉強しようとしたら数式を見て脳が処理できずにフリーズしてしまったので、スムーズに読むために勉強した程度である。

serihiro.hatenablog.com

もし、勉強というものが成長につながると仮定するならば、勉強の機会を沢山つくればいい、ということになるという仮説が立てられる。
つまり、自分が勉強が必要だと感じる状態を作り続ければいいのではないだろうか。

例えば、月並みだが、やったことのないことをやってみる。そうすれば、知らないことやできないことにたくさん直面するだろうから、勉強の必要性が生じ、勉強することになるだろう。

このブログでは今まではそんな感じの論調でエントリを書いてきたが、こういう話をリアルですると、コメントとして「なにをすればいいかわからない」「やりたいことからやるとしても、やりたいことが見つからない」と言われることが多い。じゃあやらなければ?と言いたいところだが、それだと成長できないというめんどくさいデッドロックになってしまう。

「成長したいがやりたいことはない」という状態は解消するのがとてつもなく難しい。じゃあ成長しなきゃいいじゃん、と回答したくても「成長しないといけない気はしている」という話になってしまうのであまり雑なことも言えない。

こういうことに対する回答としては、以下のようなエントリを自分は過去に書いている。

serihiro.hatenablog.com

serihiro.hatenablog.com

serihiro.hatenablog.com

しかし、どれも結局は「何となくやりたいことがある」という前提の回答になってしまている気がしている。

実際、自分は今、少なくとも30代のうちに達成したい目標があり、それを達成するために1年後、2年後はこうなっていたい、というロードマップがある。*1だから、そのロードマップの途中に設定されたマイルストーンを達成するためには、今後1年はこういうことを勉強しないといけないなぁ、というのも自然と見えてくるので、今時点の状態に関して言えば、自分がすべきことは明確に見えている。

だから、結局は、そうじゃない人のことなんて良く分からない、というのが本音である。
やりたいことがあるから今その環境にいる、というのが自分の中では当然になってしまっている。仮に、今はやりたいことができる環境におらず、やりたいことができる環境が明確に分かっているのなら、環境を変えられるように動く。そういう環境の有無が分からなければ、環境はそのままで自分を変える。それが自分のスタイルで、それ以外のスタイルで生きている人のことは正直わからない。それが自分という人間の限界である。
特に仕事に関してはその傾向が強い。そうでなければ、一番安定していたであろう新卒で入ったSIerに今でもいた気がする。趣味に関しても、かつてはマンドリンオーケストラに所属していたが、それよりもやっぱ独奏をメインでやりたいと思うようになってマンドリンオケ活動を全て辞めて、ギター教室に通うようになったりした。これもある種、やりたいことをやるための環境の変化ともいえる気がする。

こんな調子なので、やりたいことがない、けど成長したい、という人に対してアドバイスできることなんて全くないんだなぁということに最近気づいた。やりたいことがあるがどういうアプローチで攻めればいいか分からないのでブレストの相手になってくれ、というのであれば対応できそうなものだが、どうすればいいですか、と聞かれても「いや私も分からないんですよ」と言うしかない気がしている。

*1:30代というのは大きくピボットするための体力と精神力を維持できるのは30代が限度じゃないかと周りの30代を見ていて思った、という程度の根拠

RubyでMapReduceを実装している

去年の12月ぐらいからRubyMapReduce*1を実装している。
一応、ちゃんと複数のマシンで分散処理ができるところまで実装できたので、今の進捗をまとめておく。

github.com

最初は分散処理で動作するものではなく、1台のマシンでマルチスレッドで動作する疑似分散処理の実装を作ってMapReduceアルゴリズムの理解を深めるのが目的だったが、せっかくなのでちゃんと複数台のマシンで動作するものを作ることにしてみた。

概要

システム全体のアーキテクチャは以下の通り。

f:id:serihiro:20180212175809p:plain

とは言っても、実用が目的ではないので以下の制約がある(Combinerはそのうち実装するかも)

  1. Maptaskは1台のnodeで動作し、Reduectaskは空いているJobWorkerの分だけshuffleして並列に動作する
  2. Combiner未対応なのでMaptaskのoutputをそのままshuffleする
  3. shuffleはkeyのhash値を空きJobWorkerの数で割った余りを使う単純なhashパーティションのみ
  4. MapReduceのinput/output/処理途中の中間データは全てS3に保管し、データのローカリティは一切考慮しない
  5. 途中で処理が失敗してもリカバリする手段はない

ローカルで動作させている様子をキャプチャしてみたが、なんとなくこれで伝わるだろうか。
S3の代わりにminio*2をローカルで実行し、JobTrackerが1host, JobWorkerが3host起動しており、maptaskが1hostで実行され、その後, reduectaskが2hostで実行されている様子である。

www.youtube.com

使い方

クラスタの起動の仕方はREADMEに書いたのでこちらを参考していただきたい。 Dockerイメージも用意したので、めんどくさい人は docker-compose up 一発でクラスタを起動できる。

ジョブは、MapタスクとReduceタスクをそれぞれ別クラスで用意すればよい。 なお、自分で用意するのがめんどくさい人向けにCLIにサンプルが入っており、手順だけまとめると、docker-compose upで起動した場合は以下のようにすればWordCountが動かせる。

$ docker-compose exec job_tracker bundle exec simple_map_reduce generate_lorem_text_data --upload=true
$ docker-compose exec job_tracker bundle exec simple_map_reduce execute_word_count

ちなみにここで実行しているWordCountのJobは以下のようなコードである。*3

class WordCount
  def map(input_data, output_io)
    input_data.split(' ').each do |raw_word|
      word = raw_word.strip
      next if word.empty?
      word.delete!('_=,.[]()#\'"-=~|&%')
      word.downcase!

      output_io.puts({ key: word, value: 1 }.to_json)
    end
  end
end
require 'json'
class WordCount
  def reduce(input_io, output_io)
    output = Hash.new(0)
    count = 0
    input_io.each_line(chomp: true, rs: "\n") do |line|
      input = JSON.parse(line, symbolize_names: true)
      output[input[:key]] += input[:value]
      count += 1
      if count % 100 == 0
        puts "current count: #{count}"
      end
    end

    output.each do |key, value|
      output_io.puts(JSON.generate(Hash[key, value]))
    end
  end
end

これをどうやってJobTrackerに渡して実行しているかというと、ソースコードをStringとしてJobTrackerにPOSTし、temporaryなクラスを生成した上でそのクラス内クラスとしてclass_evalして定義する、という力技により実現されている。*4
Hadoopだとjobをjarとして生成してjarをそのままNameNodeに渡すようになっているが、スクリプト言語である以上こうするしか思いつかなかった。。

使ったライブラリなど

sinatra

実はsinatra*5をちゃんと使ったことがなかったので、勉強を兼ねて使ってみた。
sinatra自体は歴史あるプロダクトなので今さら特に語ることもないのだが、1クラス1アプリの単位で実装できるのが結構都合がよかった。今回作った実装ではデータの永続化を一切しておらず、sinatraアプリとして実装したクラスのクラスインスタンス変数にすべて突っ込む力技*6を採用しているのだが、データストアとしての役割も兼任させる上では1クラス1アプリという単位は管理上都合がよかった。

また、rubyのクラスとしてweb appをそのまま実装して起動できるので、rubyからふつうに起動できるのも便利だと思った。railsだと bundle exec rails s みたいな感じで、普通はシェルスクリプトなどから実行するしか手段がないのだが、sinatrarubyスクリプトとして実行する以外の起動手段を持っている。

例えば今回作ったMapReduce実装には管理用のCLIを添付したのだが、このCLIでJobTrackerを起動する部分は以下のように実装している。*7

SimpleMapReduce::Server::JobWorker.run!(port: config.server_port, bind: '0.0.0.0') do
  SimpleMapReduce::Server::JobWorker.setup_worker
end

run! で起動できるのは割と周知された方法だと思うが、さらにblockを渡すことで起動前に処理を独自のcallbackを追加できる。これはドキュメントを調べても見当たらず、結局sinatraのソースを眺めていて見つけた。*8

また、同様にソースを見ていて発見した例として、sinatraアプリを終了するときに実行されるSinatra::Base.quit! *9をOverrideすることで、sinatraアプリを終了する時にもcallbackを挟むことができるので、WebRickを終了させる前にWorkerを終了させるのに利用している。*10

# @override
def quit!
  @keep_polling_workers = false
  @polling_workers_thread.kill
  job_manager.shutdown_workers!
  super
end

MessagePack

Web APIへデータを渡すときのserializeはいつものようにJSONでいいかなとも思ったが、せっかくなのでMessagePack*11を使ってみた。
独自にTypeを定義すれば自前のクラスもserialize/deserializeできるようだが*12、今回はとりあえずHashとして各種プロパティの値をdumpしたデータをserializeしているだけである。いずれ直接JobやTaskをserialize/deserializeできるようにしてみるのも面白いかもしれない。

Worker Threadの管理

Threadで非同期実行するWorkerを管理をするために別のgemを実装した。

github.com

中身を見てもらえば分かるがSidekiq*13っぽいI/Fで指定したクラスをjobとしてキューイングして、Thread poolに入ってるThread群で並列に実行できる、という程度のものである。JavaのConcurrency UtilにあるExecutorServiceみたいなものの超簡易版ぐらいに思ってもらえれば幸いである。

Sidekiq等のJobQueueと異なる点として、外部のデータストレージをキューストアとして使っていない。漢らしいオンメモリキューストアである。もちろんjobの優先度も管理していない。 jobをEnqueueする部分はこんな感じである。*14

module Rasteira
  module EmbedWorker
    # Manager class that manages the thread pool and executes jobs.
    class Manager
      attr_reader :job_pool

      def initialize
        @job_pool = []
        @thread_pool = []
        @mutex = Mutex.new
      end

      # 省略

      def enqueue_job!(worker_name, options = {})
        @mutex.synchronize do
          @job_pool << ::Rasteira::Core::Job.new(worker_name, options)
        end
      end

データストアが絡まないとここまで実装をシンプルにできるんだなぁと感心した。逆に、普段どれだけ外部システムとの連携に神経をすり減らしているかが良く分かる。

この漢らしい実装のおかげで、JobTrackerやJobWorkerのsinatraアプリとメモリ空間を共有することができるため、jobやworkerなどのオブジェクトの参照をそのまま渡すことができる。
なので、workerからjobやworkerなどのオブジェクトを直接変更することができる。セキュリティもへったくれもないのだが、実用を考えていないのでセキュリティには一旦目をつむりたい。*15

感想

MapReduceそのものの実装は簡単だったのが、その周辺のworker, job, taskを分散環境で管理するのが難しく、分散処理に関する実装に殆どの工数が取られてしまった。
分散処理で動作するプロダクトを作る際に本質的でない実装にものすごく工数がかかるのはGoogleの論文にも書いてあった通りで、まさに身をもって再試をした気分である。

しかし、お陰で何となくHadoopなどの分散処理フレームワークが裏で何をやってくれていて、その恩恵によってどれだけ本質的な処理にだけ集中できるようになったのかが少しは理解できたと思う。 今回作った実装についてはまだいろいろと実験しがいがありそうなので、今後も時間を見てアップデートしていく予定である。 今は分散処理や大規模データ処理の高速化のアルゴリズムについて興味があるので、今後も実装を通じて学んでいきたい所存。

技術で生きていきたいから信じてコードを書く

tl;dr

  • エンジニアとしてどういう風に生き残ればよいかという問いについてずっと考えてきた。
  • でもようやく今後の自分のキャリアをこうしたい!というのが固まってきたよ。
  • それをやるためにコードを書き続けるよ。

エンジニアきのこ話

他の人も良く考えているのか、身近なエンジニアと飲みに行くと大体このきのこ話になるし、その手のネット記事は月に1回ぐらいは見る。

例えば今年だとこういう記事を読んだ。

f-shin.net

srknr.hatenablog.com

エンジニア 生存戦略 とかでおググりになってほしい。大量に出てくる。 媒体も法人メディア、個人ブログ、Qiitaと幅広い。それだけみんな関心があるんだろうな。

自分も長いこと次のキャリアを色々と考えてきた。
現場を離れてピープルマネジメントをする、プロダクト自体のマネジメント方面にシフトするなど、少なくとも今のwebアプリケーションエンジニアのままではいられないと思っていた。 また、プログラミングを職業にしているせいで発生するつらみが大きくなりすぎて、エンジニアのポジションをそもそも辞めようと思ったことも何度かある。1

ここ数年、20代後半になってからずっと将来の結構キャリアに迷ってきたが、色々痛い目を見たり考えたり人の話を聞いたりする中で、ようやくやりたいことがはっきりしてきた。 自分は今後これをやって飯を食っていくんだ、と思えるものがようやく固まってきた。これが2016年後半の出来事。
それからはずっと、そのキャリアを実現するためだけに動いてきた。 そして、来年からはそのキャリアを実現するために実際にいくつかアクションを起こしていく。

で、何やるの

それをどうやって達成するかだが、自分が目的を達成するために足りない技術を勉強し、その上でコードを書き続けるしかないと考えている。 コードを書くことだけが云々、もう歳なんだから云々、という声も聞こえてくるし自分も直接言われたことがある。でも結局自分で手を動かして実感を得ながらでないと、どんな分野でもコンピュータの世界においては「わかった」ことにならないと思っている。だからこれはもうこの業界にいる以上、一生続けたい。

最近はrailsエンジニアとして仕事をする一方で、プライベートでは全然仕事で使わないような技術分野を勉強したりコードを書いたりしている。例えばHadoopのソースを読んだり、量子コンピュータスパコンや分散処理についての本を読んだり、自分でMapReduceを実装したりしている。2

その分野が何なのかは、2018年の自分のアウトプットを見てももらえれば誰から見ても分かるようにしていきたい。それが2018年の目標だ。


  1. プログラミングは好きだが仕事としてはつらい、という感じのやつ。なのでこういうこと も積極的に意識するようになった

  2. これについてはソースを公開するタイミングでこのブログで詳細を書きたいと思う

webサービス業界で働き始めて丸5年が経とうとしている

いわゆる「webサービス業界」というものに入ってから、今年の12月で丸5年が経とうとしている。

一般的に使われる「web業界」(もう死語かな)ではなく「webサービス業界」と書いたのは、「作って別の組織に納品して終わりというスタイルの開発だけではない開発をするようになってから5年が経とうとしている」という意味で区別したかったので、本エントリではこのように書くことにする。因みにそれ以前はSIerと受託開発をしていた。

この5年間はどんな5年間だったかを一度ここらで振り返っておきたい。

最初の3年間

完全に自分の力を伸ばすための期間だった。 当初の所感とかは以下のエントリに書いている。

serihiro.hatenablog.com

serihiro.hatenablog.com

この業界でやりたいことも将来やりたいことも何も考えずに、ひたすら自分のスキルを伸ばして、業界内での存在感を出そうとしていたように思う。とにかくエンジニアとして周りと比べて劣っている自分を何とかする、ということしか眼中になかった。

当時具体的にやったことを書いていく。

まず与えらえた仕事はかなり必死こいてやった。スキルも大したことがなかったので「必死こかざるを得なかった」というのが正解かもしれないが、とにかく早く、正確に、与えられたタスクをひたすら消化することに集中していたと思う。

夜9時に退社するのも普通に感じてたのもこの頃だった。自宅で勉強することよりも職場で学べることの方が圧倒的に多かったので、職場で大量に仕事をこなすことが一番の近道だと信じていた。実際、自分よりも何年もキャリアが長くスキルも高い先輩に囲まれて、とにかく自分が頑張れば頑張るほど自分の成長が見えて楽しい時期だったし、仕事もそれなりに上手くいっていたように思う。

この間、仕事で使う言語もjavarubyの2つの言語を経験したことで、静的型付け言語と動的型付け言語の両方のpros/consを学ぶことができた。どちらもOOP言語だったことで移行がスムーズだったように思う。その前のphpjavaが結構大変だったのに対して。

仕事以外の活動として、勉強会を主催するようになった。渋谷javaという、今でも別の方による運営が続いてる勉強会を立ち上げて、自分で運営して、ついでに自分も発表するというようなことをしていた。

ただ、当時はjavaを極めてjavaで一生食ってこうなんていう気持ちはあまりなかったように思う。自分自身が色んな勉強会に出るようになったことで感じたミュニティの力の大きさに感動して、とにかくコミュニティに貢献したいという気持ちと、自分のプレゼンスを上げて会社外でも名前を知られる存在になりたいという感じの自己承認欲求が主なモチベーションだった。 それを実現するためのテーマとして、当時仕事で使っていたjavaを選んだという感じだった。実際、このころ何かjavaについて取り立てて詳しい訳でもなく、OSSのプロダクトも作っていなかったから、発表した内容はせいぜい自分が見つけた便利なライブラリ紹介をする程度に留まっていた。それが限界だった時代だった。

今でこそembulk、digdagといった著名なjava製のツールをソースレベルで仕様を調べてそれなりに使っているので、もう少し気の利いた話を出来そうなものである。しかし、当時の自分にとって、javaはwebアプリケーションを書くための言語という位置づけでしかなかった。そもそもwebアプリケーションを書くのが自分が出来る精いっぱいのことだったし、それ以外の世界を知らなかった。

次の2年間

10年後ぐらいにこの2年間を振り返った時、大きなターニングポイントになった2年間だったと自分が評価するであろう期間だと思っている。

この2年間で大きく変わったのは自分への評価だ。 それまでは、最初の3年間が終わるその時まで、自分は同業者に対してものすごい劣等感を感じ続けていた。そしてそれをモチベーションに仕事をしてきた節があった。

しかし、この2年間でその劣等感はだいぶ払拭されたように感じる。単なる思い込みや驕りかもしれないが、一応webアプリケーションを書いてる人間としては、そこそこ出来る方の側に属するのではないか、と感じるようになった。

転職して仕事のスタイルが大きく変わったことも大きい。以前、以下のエントリにも書いたが、セルフマネジメントをしなければならない領域が最初の3年間と比べて大幅に増えた。

serihiro.hatenablog.com

具体的な指示もない中で自分で会社にとって何が必要か、今何をすべきかを必死に考えて実践しなければならなくなった。恐らく世の中のスタートアップと呼ばれる企業はどこもそのような要素を多かれ少なかれ含んでいるのだと思うが、自分が入った会社もまさにそうだった。

この環境で鍛えられたのは単純な技術的なスキルというよりは、何をすべきかを自分で考え出すスキルだったと思う。

プロジェクトにアサインされている時は、APIメンテナとして要件をまとめて設計をし実装とテストをするというそれまでと変わらない仕事もした。しかし、それ以外に、今プロジェクトの中で自分が何をしなければならないか、何をすればプロジェクトがうまく進むのか、ということを考えて行動する必要が出てきた。

特に人を動かすという部分ではそうだった。エンジニア出身でない人に何がどういう理由で必要かを説明し、説得し、動いてもらう。そういう技能が要求される画面が増えた。 それまでは、ほぼエンジニアとデザイナ(フロントエンドの実装も兼任する)とマネージャー(エンジニア出身の)とだけコミュニケーションが取れればよく、コミュニケーションに悩んだことはあまりなかった。コンテキストが重なる領域が多いので、ある程度ツーカーで会話ができていた。 しかし、エンジニアリングの知識が無いが、重要なドメイン知識を持つメンバーと仕事をするケースが増えた。そのため、そういったメンバーとのコミュニケーションを行い、上手くその人達と協業するためのコミュニケーションを円滑に行うというタスクが大幅に増えたと言える。 今まで単にそういうことが必要なかったのは他の人がやってくれていたおかげだが、組織が変わってそれを自分でやる必要が出た。最初はうまくいかない面も多かったが、最近はそういうコミュニケーションミスに起因して発生するトラブルを先回りして回避するためのプロジェクト進行上のテクニックがかなり身に付いたように思う。

プロジェクトにアサインされていない時は、今メンテしているプロダクトに何が必要かを考えてタスクを自ら生み出す必要があった。 これは逆にこれまでの貯金が生きた部分で、驚くほど上手くハマった事例をいくつか作ることができた。今までのプロジェクトでやってよかったこと、悪かったことに関する知見をフルに活かすことができた。

今の職場での入社直後に集中的にこれまでの知見を用いて開発環境をもろもろいじったのだが、何をやったかは以下のエントリに書いた。

serihiro.hatenablog.com

プロダクトの設計面では、自分が以前から構想していたrailsにおける非同期jobを管理するためのアーキテクチャを導入して実装したりもした。サブシステムを丸々一つ一人で実装するようなこともあって、そういう時はこれまでやりたくてもやれなかったようなアイデアを堂々と導入することができた。

それ以外の面では属人化していたスキルや知識をパブリックにすることに力を注いだ(誰も気にしていない領域だったので勝手にやった)。

自分が入るまで特定のエンジニアにしか出来なかったことを管理画面に移植してセールスやサポートチームでも出来るようにしたり、社内コミュニケーション用のツールとして使っている esa.io#知見 というタグをつけて、こまめに社内向けの知見(歴史的経緯で分かりづらくなっている機能の仕様だとか分かりづらいモデリングに関する解説だとか)書き残したりしている。

特に、特定のプロダクトや機能に関する解説資料は、 #徹底解説シリーズ というタグをつけてかなりの長文で解説記事を書いたりもした。読まれているものもそうでないものもあるが、主に自分が仕様を思い出したり、他のエンジニアに仕様を説明したりするのに役立っている。

これまで書いた #徹底解説シリーズ で一番社内の反応が多かったのは、APIが参照しているDBのモデルに関する解説記事だった。 これは、主要なモデルに関するドメインレベルでの解説と、それらのモデルを検索するための用途別のSQL例をセットで記載したもので、非エンジニアにもSQLを覚えれば自由にデータを参照できるようになってもらうことが目的だった。まぁ実際にSQLを書いてくれる人が増えた訳ではなく、結局エンジニアやデータサイエンティストがSQLを書いてデータをBigQueryから取り出す状態は変わらなかったが、それでもこれから入ってくるエンジニアやデータサイエンティストのためのオンボーディング資料として役に立つんじゃないだろうか。

仕事以外の部分ではいくつかのgemをリリースした。殆どがrailsに関連するものだが、例えばflatten_routesは自分が趣味で作ってたrails productで必要だったので作ったものだが、実際にこれは似たようなgemを見たことがないという点ではそれなりに意味のあるgemだったのではないかと思っている。あとshoryuken, google-auth-library-ruby, ruby-openid といったgemにcontributeすることもできた。

github.com

あと開発手法に関するLTを社外で2回行った。どちらも大した話はしてないが、両者ともその後の懇親会ではそれなりに好意的なコメントを直接いただいた。

speakerdeck.com

speakerdeck.com

こういった経験で得たいくつかの成功体験を通して、自分自身はまぁまぁ出来る方のエンジニアだなとうっすらと感じるようになった。(もちろんやらかした事例もある)

現職の他のエンジニアも優秀なメンバーが多いと感じるが、彼らとうまく連携して仕事ができているという点と、それまで出来ていなかったことをそれなりに達成できたという点で自分もまぁそれなりにできる方になっていたのではないかと感じる。特にrailsによるweb appの開発という面において技術面では大きな課題を感じたことは殆どなかった。どちらかと言えば課題を感じたのは、上述したプロジェクトの進め方をいかに円滑にするかという部分だった。

新しく興味を持ったもの

一方で、ここ2年間で技術的にはwebアプリケーション以外の領域に目が向くようになった。

今何か作りたいものがありますか?と聞かれたら自分はBigQueryのような大規模データ分析基盤、もしくはそのBackEndで動作する分散job workerか分散File Systemと答える。
きっかけは今の会社に入ってから使うようになったBigQueryで、これは本当に革新的だと思った。

BigQueryは使う人から見たらただのRDBMSじゃん、と思うかもしれない。しかし、MySQLOracleを仕事で使ってきた人なら分かると思うが、比べ物にならないぐらいのreadパフォーマンスを何のチューニングもしなくても出せるという一点で、RDBMSとは全く異なる存在である。 実際自分も使ってみるまではそのパフォーマンスに懐疑的だったが、数千万行から数億行のレコードをフルスキャンしなければ実現できないような集計クエリが20秒程度で返ってきた時は度肝を抜かれた。また、日々どれだけレコードが増えてもパフォーマンスが殆ど劣化しない。雑にBQにdatasetとテーブルを作ってembulkで流し込みさえすれば、後は自由にクエリを書いてデータをシュッと取り出せる。

この体験は使ってみないと分からないと思うが、入社して初めてnginxのログをBQで検索したときは本当に革新的だと思った。こういうものこそが自分がそれまで必要としていたものだった。

これまで、著名なサービスを除けば世の中のサービス開発はデータを軽視されていたと個人的に感じていたのだけど、それは大規模なデータを取り扱うためのDWHのようななにがしかを「サービスが稼働し始めた後から」作るイニシャルコストが無視できないほど大きかったからだと考えている。大企業はともかく、スタートアップとしてはそのような大型投資を簡単にできない状況が多いと思われる。

しかしBigQueryは、PaaSとしては高い方だと思ったけど、テーブルを作ってデータをinsertするだけなら大した額はかからない。しかも、すぐに始められるから、どれぐらいの投資が必要か判断できなくてもスモールスタートで「とりあえずこのテーブルだけ分析できるか検証してから決めたい」という時にすぐに判断ができる。本番のDBに投げたら絶対返ってこないようなクエリがすぐに打てる。これがビジネスにどれだけの影響を与えるか。

というような具合に、ビジネス面でのインパクトも感じたが、一方でそもそもどういう風に実装されているのかという点に自分は興味を持った。
分散データ処理と言えばおなじみMapReduceだが、そもそも自分はMapReduceを使ったこともなければ使いどころについても検討したことがない。まずはこっからだ、ということで今年に入ってからMapReduceのオリジナルの論文や、Hadoopの高速化に関する論文を読んだり、ここ数年のビッグデータの処理基盤に関する傾向を調べたりした。 分かったことは、今の時代ではHadoopがかつて抱えていた問題(それこそ論文で議論されていたような)を解決するようなossが増加し(spark, kuduなど)、MapReduceはオワコンみたいな話をえらい人がパブリックな場でするようになって、もはや枯れた技術となってきているということが分かった。が、大規模なデータを処理するためのアルゴリズムとしては優れており、それを支えるためのストレージ/NWの技術は着実に進歩しているし、個人的にはまだ研究したいと思える技術ではある(まぁproductionで使ったことないし)。

次の5年間

今までは自分の劣等感をモチベーションとした「成長」だけを意識して仕事をしてきたが、これからは作りたいものを作るための技術習得にフォーカスして、作りたいものをやるために仕事をしようと思う。ビジネスのための手段としてwebアプリケーションを作るという意識が強かったが、単純に自分が作りたいと思うものに技術をつぎ込んだ結果、それがビジネスになり自分のお賃金になる。そういうポジションを目指したいと思う。

健康面で気を付けていること

台風がいくつか過ぎ去ってようやく秋らしくなってきた気がする。
そのせいか身の回りでは体調を崩す人が多い。自分も微妙に体調が悪い日はたまにある。

20代の頃ならば何も考えていなくても体調は崩さないし、崩したとしてもすぐに治るから普段から気に掛けてはいなかった。しかし自分の場合、30歳になったのを境に、急に体調の治りが悪くなった。一度体調を崩すと同じ症状が最低2日は続く。頭痛、睡眠障害、異常なだるさ、消化器官の不調、あと典型的な風邪の症状。「あ、ヤバイな」と思った時にはそこから数えて最低2日は同じ症状が続くようになった。認めたくはないが、自分も老化という現象が自己主張をし始める年齢に来たのだなと思う。

これはいかんなと思って体調をようやく気遣うようになり、最近は周りが風邪引いたりしている中であっても体調を維持できている。備忘録として何に気を付けているか書き記しておきたい。

睡眠時間の確保

適切な睡眠時間については諸説あるが、自分は眠りが浅い日が多いのでその分時間でカバーできるように7時間半~8時間は寝るようにしている。具体的には23時ぐらいに寝て6時30分ぐらいに起きるのを標準としているが、コード書いてたり本読んでたりすると気が付くと日付を過ぎていることもあって、寝る時間はマチマチだったりする。それでも睡眠時間はできるだけ一定時間を確保できるように、なるべく早く寝ることに努めている。

睡眠時間よりも起床時間の方が重要だという話も読んだことがあるが、平日の1日を通してみると前日の睡眠時間が不十分だと明らかに日中に眠くなり、パフォーマンスが低下するため、どちらかと言えば睡眠時間の確保を優先していることが多い。

suimin-supple.com

運動する

とか言いつつ10月入ってから仕事の関係で一度もやってなかったりするのだが(よくない。。)、ジムで筋トレとジョギングを週1ぐらいでしている(た)。本当は毎日やった方がいいのだが、英語とか数学とかその他技術書読む時間を夜に確保したい関係でなかなか毎日は難しい状態である。

社会人になってから運動しなくなったという話以前に、そもそも大学入学以降は普段から全く運動などしていなかったのだが、30代になるとその弊害が顕著に出てくるらしい。なので30代になってから運動するのは結構必須っぽい気がする。*1運動の効果は言わずもがなだが、一番大きいのは体を動かすことでストレスが解消できる点だと思う。

この稼業は基本1日中座りっぱなしで、その上ストレスフルな状況に1日中晒されているので仕事終わりには相当ストレスが溜まっている状態になっていることが多い。
1日に受けた体と精神へのストレスはその日のうちに解消できるのが一番いいはずで、それを行う上で軽い運動をするのが一番効率がよいのではないだろうかということで、今後も続けていきたい所存。

野菜を意識して食べる

意識しないと肉ばかり食べてしまうので意識的に野菜を取るようにしている。

コンビニとかスーパーの出来上いのサラダばっか食べてて、本当はそれも添加物と残留農薬だらけであまり良くないんだろうなぁと思うが背に腹は代えられぬ。 一人暮らしなので個々に野菜買ってサラダ作っても食べきる前に野菜が傷む問題がどうしても解決しないので諦めている。

あとはまぁ添加物使ってないと主張する体に良さげな(オーガニックとかエスニックとかっていう冠詞がついた)店舗で食事をするとか。実際自分は週に1回はランチにサラダがやたら多いランチコースがあるアジア系料理のカフェで食事をしている。

あと野菜不足を補うためにベジールという野菜ジュースを定期購入して飲んでいる。これも効果があるかどうかは定かではないが一応飲み続けている。

www.oisix.com

瞑想

サーチ・インサイド・ユアセルフを読んでから習慣的に瞑想をするようになったが、果たして体調にまで効果があるかはよく分かっていない。

しかし、1日にあった出来事を振り返るための時間を強制的に取れたり、高ぶった感情を鎮めるため(まぁ仕事でイライラしてると結構眠れないものである)という目的においてはかなり効果があると感じる。

サーチ・インサイド・ユアセルフ――仕事と人生を飛躍させるグーグルのマインドフルネス実践法

サーチ・インサイド・ユアセルフ――仕事と人生を飛躍させるグーグルのマインドフルネス実践法

イライラしたらなぜイライラしているかを考える

自分の体はどうもストレスに弱いらしく、ストレスフルな状況になると体調を崩しやすくなる傾向にあるようだ。 なので、ストレスを強く感じてるなーと思ったらなぜストレスを感じているのか?を考えるようにして、できるだけ明確にするようにしている。

やり方として、今不満に思っていることをeditorでも紙でもホワイトボードでも何でもいいので書き出してみて、それを眺めて根本の原因を突き詰めていく方法を取っている。大体10個ぐらい書き出せばまぁ何となく原因らしいものは見えてくるので、それは解決すべき問題かどうか、そもそも自分に解決できるのかどうか、どういうやり方で解決するか、というのを考える。

この作業の結果、具体的な解決策が見いだせればそれを実行するし、自分の力ではどうにもならんなと思ったらまぁ諦めてしまうことにしている。仕事のストレスの原因のうち殆どは後者で、どうしようもなく遠い所で発生している問題が、たまたま担当者として自分の所まで降りてきただけだなということがよくある。

自分をほめる

これもストレス緩和のための施策の一つだが、ほめるのである。31歳のおっさんが、自分を。考えただけで気持ち悪いだろう。

でも自分をほめられるのは自分だけなのである。仕事や趣味の活動で他人にほめられた経験なんて殆どない。誰かをほめたことはあるが、それによって相手が満足したかどうかは定かではない。しかし、自分であれば、自分の行動を検証した上で納得のいく評価ができる。その上で、よくやったなと思ったら自分で自分をほめても良い、ということにしている。

ここまで書き出してみて我ながら気持ち悪いことしてるなぁと思うが、実際そうでもしなければやっていけないし、自分を褒めて、例えば本を一冊買うことを許可するとかSteamでクソゲー一本買うのを許可するなんてのは安いもんである。 いくら社会のために仕事をしているという大義はあっても、それを感じられるのは本当に稀である。めちゃくちゃ大変なプロジェクトを何とか達成したとしても必ずしもそれが客観的な評価につながることは稀である。でも一方で、検証した上で自分なりに評価されてもいいと(もしくは評価されるべきでないと)判断することはできる。

自分の一番の理解者は自分なのであると思う。だから、ほめるべき仕事をしたなぁと思ったら自分で自分をほめてやることにしている。結局、歳を取れば取るほどポジティブに自分を明示的に評価してくれる他人がいなくなっていくのが人生というものなのだろうとも最近思うようになった。*2 ちなみにこんな本もあるらしい。まだ読んでないけど。

併せて読みたい

眠くならない飲み物の摂取の仕方が参考になった。

blog.satotaichi.info

*1:のでジム通いは再開したい

*2:ドラえもんも「大人ってかわいそうだね。自分より大きなものがいないもの。よりかかってあまえたり、しかってくれる人がいないんだもの。」と言っていた。

余裕はあるけど今やらなくても良さそうだけどいつかやった方がいいタスク問題

こういうタスクは大体「溜まる」傾向にある。 github issueでいえば いつかやる とか NiceToHave なんていう、何とも第三者から見ると全くプライオリティが想像できないタグが付けられて、そして1か月もたつとissueの存在すら忘れる。*1

自分の場合、こういうのを貯めておくと気持ち悪いので、それほど余裕がなくてもスキを見てさっさと片付けてしまう傾向にある。今日も例外発生時の調査用のログフォーマットを調整するだけのPRを出した。*2

余裕ができると自分はリファクタリングのPRを週に10個近く出していることがある。流石にレビューを依頼される同僚からはドン引きされたのではなかろうかと思うが、プロジェクトの谷間とかで急ぎの仕事がなければそういうことをせざるを得ない時もあるのである。

そのため、自分がアサインされているissueは遅くても1か月にはcloseされている傾向にあるようだ。ようだ、というのは正確に計測した訳ではなく肌感覚だからなのだが、少なくとも3か月以上closeされていないissueに関して言えば自分は1つしか持ってない。これはやらないのではなく色々な事情でcloseできておらず目の下継続中のタスクなのだ。

さて、こういうタスクに関してだが、他のメンバーを見ると結構古いissueをずっと抱えているのをよく見かける。openなissue絶対closeするマンの自分は、時間ができると他の人のissueを見て回って「進捗どうですか?」的質問をして状況を確認し、わざわざその時のステータスをコメントに書いてみたり、アサインされていないものは適当にやれそうな人をアサインしておくことがある。あるいは自分で勝手に巻き取ってやってしまうこともある。自分がやれるタイミングなら自分がやるのが一番早いからだ。

自分の場合、積み残した仕事というのがそもそも自分として許せない存在であり、改めて検討した結果やる必要がないと判断できるならすぐcloseしてしまうし、イシューになっている(githubのissueではなく本来の意味のissue)時点でそれは自分のチームの責務なので、なるはやでcloseに向けて動くのが仕事だと思っている。

こういう積みタスクというのは、クライアントにも同僚にも喜ばれる華々しい機能追加ではなく、大抵はちょっとしたtypoを直すとか、社内用の画面のちょっとしたバグだとか、急いでいたので黙認したrubocopの警告を直すとか、ライブラリのバージョンを上げるとか、拡張性の悪いクラスを細かく分割してspecを書き直す、とか、めったに起きないエラーを調査して直すとか、使ってないクラスを既存コードに影響しないように取り外すとか、使ってないのになぜかDBに残ってるテーブルをdropするとか、not null制約がないとどう考えてもおかしいのに付いてないカラムに付けるとか、そういうやつである。

そのため、会社の売上にも貢献できず、その上誰からも感謝もされず、評価もされにくい貢献だと思われがちなせいか、多くの人は避けたがる。だがその思い込みは恐らく間違いだ。そうこうしているといつか大きな負債を生んで自らを苦しめる(もしくは苦しめられた)例を自分は沢山知っているし、逆に自分が時間がある時に仕込んだ詳細なログのおかげで命拾いしたことが何度もある。これはれっきとした技術的負債の返済であり、将来の自分たちを救うための重要なタスクなのだ、と思ってもらいたい(のだが、一度そういうマインドが染みついた人の思想を変えるのは実際難しい)。

こういうタスクは実際会社の売上には1円も影響しないかもしれないが、将来発生し得る100万円の損害を1万円に抑えられるかもしれない可能性を秘めている。いざ大障害が起きてから「ログが少なくて状況が分かりません」だとか「コードが汚くて修正するのに時間がかかります」などと言ってられるだろうか。そういう目にあってから初めて目を覚ますのでは遅すぎる。

こういう負債をまとめて返済すべく派手にリファクタリングするのはデグレのリスクが大きくなる。そのため、5行とか10行とかの小さなPRを普段から出し続けて地道に改善するしかないのである。GoogleUeyama Ruiさんのブログにも同じような話が書かれている。

blog.sigbus.info

自分はオープンソースに何度かPRを出してマージされたことがあるが、基本的に仕事でコードを書くのとオープンソースのコードをいじるのとは全く同じスタンスで考えている。つまり、変更の意図が分かりやすく、必要な量のコメントが書いてあって、自分以外の誰かが次にいじる時に困らないようにするということを常に守っている。実際、オープンソースにPRを出す練習のつもりで、これまでつらつら述べてきた「誰もやりたがらないタスク」を片付けている部分もある。そう考えればそこまで退屈なタスクでもないし、タイムアタックだといわんばかりに1日に何個片付けられるか挑戦してみたりすることで自分はそれなりに退屈しないでいられるのだが、もしかしたら自分が変わってるだけなのかもなぁという気持ちにもなっている昨今である。

*1:こういうタスクどうやって管理するのがいいのかいまだによく分からないがどうやっても発生してしまう。定期的に棚卸すればいいのだろうか。誰か教えてくれ。

*2:今の会社に入ってからBugsnag入れたりログを増やしたりログを検索できるようにしたり、エラーを可視化する仕事ばかりしている気すらする。この手の運用に強いコードにするためのリファクタリングのコンサル業だけで飯が食えそうな勢いである。