Monthly Archives: 12月 2013

日本にもスタートアップ精神ができてきた(NYTの記事)

ニューヨークタイムズWeb版に日本のスターアップ事情に関する記事「Start-Up Spirit Emerges in Japan」が掲載されたので、それについて部分的に訳しながらコメントします。以下「部分訳」と書いてある部分は、いくつかの文をピックアップして訳したという意味もあれば、大体の要旨を書いたという意味もあります。著作権の問題で、全訳することはできません。

これを書こうと思った動機は、榊原さんのところには色々とお世話になっているというのと、日本人って自分の国の文化や経済が海外からどう見られてるかって話が好きな人多いと思ったので。

(部分訳)
典型的な成功はグレーのスーツを着た「サラリーマン」であった日本において、20代そこそこの起業家は似つかわしく見えないかもしれない。しかし日本経済の復活を信じる人にとって、サムライスタートアップアイランドは大きな意味を持つかもしれない。

起業家精神の復活はまだ未知数ではあるが、アナリストによるとここ2,3年でインターネットやテクノロジー関連のスタートアップは増加しており、サムライスタートアップアイランドのようなインキュベータやアクセラレータと呼ばれる新しいベンチャーファンドのような新たなエコシステムを構築している。

「これは日本の若返りの始まりだ」とユーグレナの創業者のイズモは言う。「若い人たちを自由にさせなければ、日本は弱くなり、またフクシマのようなことが起きれば生き残れないだろう。起業は日本にとって最後のチャンスだ」と。

起業家精神の低下が日本のデフレの原因だとされてきた。ソニーやトヨタやホンダを生んだ国なのに後が続かなかった。

日本は伝統的に製造業のようなブルーカラーの業種においては、起業がうまくいってきた。一方、ソフトウェアなどの知識ベースの業種においては起業の成功例はあまりない。

大体私の認識と合ってます。私は、サラリーマン中心の世の中なのはそれはそれでいいと思っていますが、サラリーマン以外は負け組みたいな風潮には違和感を感じてます。

サラリーマン(salaryman)ってもともと和製英語なんですが、今ではこういう英語としても使われているようで、不思議な力学にしたがって動く日本人ビジネスマンを揶揄する意味も込められているようです。

(部分訳)
サムライスタートアップアイランドの創始者サカキバラは言う、起業家は勇敢さや無心さについて、日本のサムライに学ぶべきだ。彼はまた、ホリエのような派手な消費や公の場での傲慢さを避けるべきだと言う。そうすることが、日本の平等主義の社会には受け入れやすくなる。

ホリエは今でも新しい起業家の間では崇拝されていて、サムライスタートアップアイランドで激励の演説をしている。

榊原さんの方針がよく伝わります。堀江さん(元ライブドア)の以前のやり方を批判しつつ、一方彼の協力も得ているようです。

(部分訳)
すべてがリスキーな世の中においては、自分が自分のボスになって、自分の運命に責任を持ったほうがいい、とグノーシーのフクシマは言う。

大学で修士号を取ろうとしている時にフクシマは、ソーシャルメディアを分析しユーザに興味がありそうなニュースストーリを提示するアプリを売るためにグノーシーを起業した。起業の際に彼は名のあるファームからの仕事のオファーを断っている。

この福島さんの言葉と行動がすべてを表しているような気がします。大企業に入ったからといってリスクが無いわけだはないのだから、だったら自分で自分の将来をコントロールできる道を選ぼうということかと思います。もちろん、大企業に行ったからといって、全く自分の将来をコントロールできないわけではないのですが。

(部分訳)
大企業のいい仕事がすぐに見つかるようなエリート大学の卒業生に良い兆候が見られる。イズモもフクシマも東京大学の出身である。

僅かな例を挙げても本当に良い兆候があるのかどうかはわからないですが、東大に限らず優秀な学生さんには頑張ってほしいものです。


Numpyのブロードキャスティングについて

先日ありがたいマサカリを頂いたのを機に、numpyのブロードキャスティングについてあまり理解してなかったなと思い、改めてまとめてみることにする。以下自分の理解をまとめたものだが、一応初心者向け解説のつもり。わかってる人は読まなくていい。

解説

Numpyでは例えば、以下のように1次元配列とスカラー値の演算ができる。

>>> from numpy import *
>>> a=arange(5)
>>> a
array([0, 1, 2, 3, 4])
>>> a*5
array([ 0,  5, 10, 15, 20])

こういうのをブロードキャスティングと呼ぶ。

これは、2次元と1次元の計算に限らず、また掛け算に限らず他の四則演算でも似たようなことができる。なので、以下足し算に限定して例示する。例えばこの場合。

>>> a=arange(10,130,10).reshape(4,3)
>>> a
array([[ 10,  20,  30],
       [ 40,  50,  60],
       [ 70,  80,  90],
       [100, 110, 120]])
>>> b=arange(1,4)
>>> b
array([1, 2, 3])
>>> a+b
array([[ 11,  22,  33],
       [ 41,  52,  63],
       [ 71,  82,  93],
       [101, 112, 123]])

これは式で表すと、
$$ c_{i,j}= a_{i,j} + b_j (0\leq i <4, 0\leq j<3)$$ となる。この計算はaのシェイプ(4,3)の2つめの軸のインデックスの数(この場合3)がbのインデックスの数と一致してる場合に限られる。そうでないと次のようにエラーになる。 [text] >>> a=arange(10,130,10).reshape(4,3) >>> b=arange(5) >>> a+b Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: operands could not be broadcast together with shapes (4,3) (5) [/text] この場合、aのシェイプが(3,4,1)ならエラーにならない。 [text] >>> a=arange(10,130,10).reshape(4,3,1) >>> b=arange(5) >>> c=a+b >>> c array([[[ 10, 11, 12, 13, 14], [ 20, 21, 22, 23, 24], [ 30, 31, 32, 33, 34]], [[ 40, 41, 42, 43, 44], [ 50, 51, 52, 53, 54], [ 60, 61, 62, 63, 64]], [[ 70, 71, 72, 73, 74], [ 80, 81, 82, 83, 84], [ 90, 91, 92, 93, 94]], [[100, 101, 102, 103, 104], [110, 111, 112, 113, 114], [120, 121, 122, 123, 124]]]) >>> c.shape (4, 3, 5) [/text] これは何をやっているかというと、\(\{a_{i,j,0}\}_{0\leq i <4, 0\leq j <3}\), \(\{b_k\}_{0\leq k <5}\)(aが(4,3,1)という形であることを示すためにわざと余計なインデックス0を加えている)に対し、\(\{c_{i,j,k}\}_{0\leq i <4, 0\leq j <3, 0\leq k <5}\)を、 $$c_{i,j,k}=a_{i,j,0} + b_k$$ で計算していることになる。 では、aが(4,1,3)というシェイプの時に、1次元配列bを同じように足し算したいときは、そのままではできなくて、bを(5,1)という形に変形するとできるようになる。 [text] >>> a=arange(10,130,10).reshape(4,1,3) >>> b=arange(5) >>> c=a+b Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: operands could not be broadcast together with shapes (4,1,3) (5) >>> b=b.reshape(5,1) >>> c=a+b >>> c.shape (4, 5, 3) >>> c array([[[ 10, 20, 30], [ 11, 21, 31], [ 12, 22, 32], [ 13, 23, 33], [ 14, 24, 34]], [[ 40, 50, 60], [ 41, 51, 61], [ 42, 52, 62], [ 43, 53, 63], [ 44, 54, 64]], [[ 70, 80, 90], [ 71, 81, 91], [ 72, 82, 92], [ 73, 83, 93], [ 74, 84, 94]], [[100, 110, 120], [101, 111, 121], [102, 112, 122], [103, 113, 123], [104, 114, 124]]]) [/text] この場合、\(a_{i,0,k}\)と\(b_{j,0}\)に対して $$ c_{i,j,k}=a_{i,0,k}+b_{j,0}$$ を計算していることになる。 これは一般の次元で考えられるが、その場合の規則は、末尾の側から見ていって1)次元の大きさが一致している場合、または2)片方の次元の大きさが1の場合、についてブロードキャスティング演算ができることになっている。 以下の例は本家ページからの抜粋だが、OKなケースとしては以下の例がある。


A      (4d array):  8 x 1 x 6 x 1
B      (3d array):      7 x 1 x 5
Result (4d array):  8 x 7 x 6 x 5

A      (2d array):  5 x 4
B      (1d array):      1
Result (2d array):  5 x 4

A      (2d array):  5 x 4
B      (1d array):      4
Result (2d array):  5 x 4

A      (3d array):  15 x 3 x 5
B      (3d array):  15 x 1 x 5
Result (3d array):  15 x 3 x 5

A      (3d array):  15 x 3 x 5
B      (2d array):       3 x 5
Result (3d array):  15 x 3 x 5

A      (3d array):  15 x 3 x 5
B      (2d array):       3 x 1
Result (3d array):  15 x 3 x 5

ダメなケースとしては以下の例がある。


A      (1d array):  3
B      (1d array):  4 # 最後の次元が一致してない

A      (2d array):      2 x 1
B      (3d array):  8 x 4 x 3 # 最後から2つ目の次元が一致してない

こういうのを見ると、2次元配列の掛け算(*)がなぜ行列の意味の掛け算ではなくて要素ごとの掛け算になっているかがわかると思う。それは、ブロードキャスティングの特殊な場合にすぎない。

最後にちょっと複雑な例:


A     : 4 x 1 x 2 x 1
B     :     3 x 2 x 3
Result: 4 x 3 x 2 x 3

について計算例を示す。

これは、\(\{a_{i,0,k,0}\}_{0\leq i<4, 0\leq k<2}\)と\(\{b_{j,k,l}\}_{0\leq j<3, 0\leq k<2,\; 0\leq l<3}\)に対して $$ c_{i,j,k,l}=a_{i,0,k,0}+b_{j,k,l} $$ を計算することを意味する。ここで、3つ目のインデックスだけが両方で使われていることに注目する。 実際の計算は以下の通り。 [text] >>> a=arange(10,90,10).reshape(4,1,2,1) >>> a=arange(100,900,100).reshape(4,1,2,1) >>> b=arange(18).reshape(3,2,3) >>> a array([[[[100], [200]]], [[[300], [400]]], [[[500], [600]]], [[[700], [800]]]]) >>> b array([[[ 0, 1, 2], [ 3, 4, 5]], [[ 6, 7, 8], [ 9, 10, 11]], [[12, 13, 14], [15, 16, 17]]]) >>> c=a+b >>> c array([[[[100, 101, 102], [203, 204, 205]], [[106, 107, 108], [209, 210, 211]], [[112, 113, 114], [215, 216, 217]]], [[[300, 301, 302], [403, 404, 405]], [[306, 307, 308], [409, 410, 411]], [[312, 313, 314], [415, 416, 417]]], [[[500, 501, 502], [603, 604, 605]], [[506, 507, 508], [609, 610, 611]], [[512, 513, 514], [615, 616, 617]]], [[[700, 701, 702], [803, 804, 805]], [[706, 707, 708], [809, 810, 811]], [[712, 713, 714], [815, 816, 817]]]]) >>> c.shape (4, 3, 2, 3) [/text] これではぱっと見ではわかりづらいと思うが、3つ目のインデックスだけが重なっていることの影響は以下のように確認できる。 [text] >>> c[:,0,0,1] array([101, 301, 501, 701]) >>> c[0,0,:,1] array([101, 204]) [/text] つまりc[:,0,0,1]では、すべて一の位と十の位が同じだが、c[0,0,:,1]については一の位と十の位が要素によって異なっている。つまり1つ目のインデックスが変わっても加算されるbの要素は変わらないが、3つ目のインデックスについては、インデックスの値によって加算されるbの要素が変わっていることが確認できる。

感想

一見複雑な仕様に見えたが、実際に手を動かしてみるとさほど難しくないし、自然な仕様にも思えてくる。でもどこがどう「自然」なのかはまだうまく日本語で説明できない。

参考文献

Pythonで数値計算のコツ:for文書いたら負けかなと思っている

転職してから1年とちょっとが経ち、Pythonをメイン言語としてからも同じくらいが経った。最近やっとnumpy/scipyの使い方のコツがわかってきたと思うので、マサカリ飛んでくるのを覚悟でなんか書いてみようと思う。

転職して初めてPythonを使ったというわけではない(実際wafのwscriptとかは書いたことある)が、まあでもほぼ初心者同然だった。学習曲線でいうとPythonはすごく良い言語だと思う。Python本体の言語仕様については、わりとすぐに覚えることができた。だが一方、numpy/scipyについては、そう簡単ではなく習得するにはそれなりに時間がかかったと思う。

ケーススタディ

たとえば\(N\times M\)行列\(B\), \( M\times L \)行列\( C \), \( M \)次元ベクトル\(a=(a_k)_{1\leq k \leq M}\)が与えられて
$$r_{ij}=\sum_{k=1}^{M} a_k b_{ik}c_{kj}$$
により行列\(R=(r_{ij})\)を計算したいとしよう。

例えばC言語で書くなら(C#やJavaでもほぼ同様に)

for (i=0; i<N; i++) {
  for (j=0; j<L; j++) {
    d=0.0;
    for (k=0; k<M; k++) {
      d+=a[k]*b[i][k]*c[k][j];
    }
    r[i][j]=d;
  }
}

と書くのは割と自然なはずだし、最速ではなかったとしても計算速度もそれなりに出ると思うのだが、Pythonでこれを「直訳」して以下のようにしてしまうのは最悪である。

def compute1(N,M,L,a,b,c):
    r=np.empty((N,L))
    for i in xrange(N):
        for j in xrange(L):
            d=0.0
            for k in xrange(M):
                d+=a[k]*b[i,k]*c[k,j]
            r[i,j]=d
    return r

Pythonでは、for文でループしながら配列内の要素を参照するのがとても遅いので、行列積の関数などを使って配列要素への直接参照を減らしたほうが高速になる。

そこで、
$$R = \sum_{k=1}^M a_k b_{\cdot k} c_{k\cdot}$$
(ただし、ここで\(b_{\cdot k}\)は\(B\)の\(k\)列目列ベクトル、\(c_{k\cdot}\)は\(C\)の\(k\)行目行ベクトル)と同値な式を考えてやると、numpyの行列積の関数が使えて高速になる。そのコードがこちら。

def compute2(N,M,L,a,b,c):
    return sum([a[k]*np.outer(b[:,k],c[k,:]) for k in xrange(M)])

ところがもっと高速にする方法がある。
$$
A=\begin{pmatrix}
a_1 & & & \\
&a_2 & & \\
& & \ddots & \\
& & &a_L
\end{pmatrix}
$$
という行列を考えてやると、\(R\)の式は
$$R=BAC$$
と同値である。この\(A\)を疎行列として構成して計算すれば、\(L\)が大きい時でもメモリを大量消費することもない。なので、ここでscipy.sparseを使う。そのコードはこちら。

def compute3(N,M,L,a,b,c):
    aa=sparse.diags([a],[0])
    return np.dot(b,aa.dot(c))

ここで面白いのは、1次元配列を疎行列形式に詰め直すのは余計オーバーヘッドがかかるような気がするが、それをカバーしてあまりあるほど疎行列✕密行列の関数が高速だということ。

とここまで自力で考えたのだが、最初のポストのあとにこんな指摘があった。


要素積の仕様とか、全くわかってなかった。やっぱりnumpy難しい。ここらへんはまた勉強しなおしてブログでも書こうかと思う。そして、指摘していただいたしましまさん、ありがとうございました。(望みどおりマサカリが飛んできたわけですが…)

なので、こうするのが一番いいらしい。

def compute4(N,M,L,a,b,c):
    return np.dot(b*a,c)

では実際にベンチマークをしてみる。ベンチマーク用のコード(全体)はこうなる。

import numpy as np
import scipy.sparse as sparse
import time

def compute1(N,M,L,a,b,c):
    r=np.empty((N,L))
    for i in xrange(N):
        for j in xrange(L):
            d=0.0
            for k in xrange(M):
                d+=a[k]*b[i,k]*c[k,j]
            r[i,j]=d
    return r

def compute2(N,M,L,a,b,c):
    return sum([a[k]*np.outer(b[:,k],c[k,:]) for k in xrange(M)])

def compute3(N,M,L,a,b,c):
    aa=sparse.diags([a],[0])
    return np.dot(b,aa.dot(c))

def compute4(N,M,L,a,b,c):
    return np.dot(b*a,c)

def main():
    np.random.seed(0)
    N=10
    M=10000
    L=20
    a=np.random.random(M)
    N_ITER=10
    b=np.random.random((N,M))
    c=np.random.random((M,L))
    t=time.time()
    for _ in xrange(N_ITER):
        r1=compute1(N,M,L,a,b,c)
    tt=time.time()
    print "compute1 : %.3f sec" % (tt-t)
    t=time.time()
    for _ in xrange(N_ITER):
        r2=compute2(N,M,L,a,b,c)
    tt=time.time()
    print "compute2 : %.3f sec" % (tt-t)
    t=time.time()
    for _ in xrange(N_ITER):
        r3=compute3(N,M,L,a,b,c)
    tt=time.time()
    print "compute3 : %.3f sec" % (tt-t)
    t=time.time()
    for _ in xrange(N_ITER):
        r4=compute4(N,M,L,a,b,c)
    tt=time.time()
    print "compute4 : %.3f sec" % (tt-t)
    # Confirm the results are the same
    eps=1e-10
    y=(r1-r2).reshape(N*L)
    assert np.dot(y,y)<eps*N*L
    y=(r1-r3).reshape(N*L)
    assert np.dot(y,y)<eps*N*L
    y=(r1-r4).reshape(N*L)
    assert np.dot(y,y)<eps*N*L

main()

実行結果はこうなった。

compute1 : 19.018 sec
compute2 : 1.546 sec
compute3 : 0.030 sec
compute4 : 0.021 sec

このように計算時間で大差が出るのは、Pythonの数値計算系ライブラリは内部でFORTRANやCで書かれているからで、計算過程ではできるだけPython側でデータを取り出さない方が速くなる。

まとめ

Pythonで行列・ベクトル関連の計算を速くするには以下のようなことを気をつけるとよい。

  • できるだけ多次元配列や疎行列のデータ型に入れてからライブラリ関数で計算する。計算中にPython側での要素へのアクセスはでるだけ避ける。
  • そのために前処理が重くなっても、多少メモリを散らかしても気にしない。結局安くつくことが多い
  • コードを書く前に代数的に同値な変形を考え、行列の積・和だけで表現できないか考える。そのとき、疎行列もうまく活用する。

つまり、for文書いたら負けかなと思っている。リスト内包表記もできれば避けたい。

あと、そのコード誰が保守するんだ?っていう質問には、聞こえないふりで対応する。

更新履歴:
2013/12/20 23:31 しましまさんの指摘を受け、加筆しました。

ハッカソンに参加してきました:そして優勝してきた

ハッカソンに出席してきました。これです。
忘年会ハッカソン JOYSOUNDを展開する(株)エクシングが特別提供する『言語解析API』を駆使したアプリを開発せよ!!

こちらにもブログに書いている人がいるので参照してください。
JOYSOUNDの会社が主催する言語解析APIハッカソンに行ってきた

実は、開発者向けイベントというのは何度か参加したことがあるものの、ハッカソンという形式は初参加でした。今回はエクシングさんが特別に言語処理APIを公開するということなので、それにそそられました。

ということで、


と言っている割には嬉々として出かけて行きました。

エクシングさんが提供するAPIは、文節解析や係り受け解析をしてくれるもので、詳しくは書けませんが非常に精度が高い印象を受けました。

そしてそれを使って私が作った作品が、題して「欲望センサー」。ツイッターで「〜したい」とつぶやかれている対象で、頻度順に上位からランキングして表示するもの。デモをした実績でいうと、例えば、「食べたい」ものだと「ラーメン」や「寿司」が上位に、「買いたい」だと「本」や「グッズ」が上位に。

これは、ツイッターの検索APIとエクシングの係り受け解析を利用した単純なものなんですが、文節の処理とか、ストップワードの処理とか、細かいところはそれなりに頑張ってます。

デモで、最後に「揉みたい」で調べたところ、圧倒的一位が「おっぱい」に。まあそれは想定の範囲内なんですが、上位になぜか「ノーブラヒートテック」というのが入り、場内騒然と…

その後懇親会があったのですが、「あっ、あのノーブラヒートテックの人ですね?」っていう感じでアイデンティファイされてしまいました。(いや、私の発言ではないのですが…)

一応、マーケティングとかに役立つのではという真面目な文脈で開発したんですけど、最後は笑いに走ってしまったのは不本意であるような、本意でもあるような…まあ、みなさん楽しんでいただいたようなのでよかったです。

最後に、良かったと思うアプリにみんなで投票するんですが、なんとそれで優勝してしまいました。得票の半分くらいはノーブラヒートテック(とつぶやいた人?)のおかげなんじゃないかと思っています。エクシングさんからはJOYSOUNDのクーポン券をいただきました。

反省点ですが、本当はウェブアプリとしてブラウザで動かそうとしていたところ、開発が間に合わなくて結局コマンドラインでのデモになってしまいました。もともとフロントエンドの技術は弱くて、特にJavaScriptは書けないこともないのですがほぼシロウトなので手間取りました。サーバ(集計結果をjsonで返すところ)までは完成したのですが、クライアントの受け手側が最後までうまく動かず断念しました。別にその方面のプロフェッショナルになるつもりはないのですが、たまに「素振り」くらいはやっておいたほうがいいかもしれないですね。

エクシングのAPIが利用できるのは、あのハッカソン限定だそうで、私の作ったアプリが陽の目をみることはなさそうですが、あのような興味深いAPIを公開して頂いたエクシングさんには改めて感謝します。また、会場を提供して頂いた、そして寝起きの某M嬢(笑)にも会わせていただいたサムライインキュベートさんにも感謝します。

生産性が高いとは、適切な人が適切なことをするということ

こんなツイートがあった。

知識がないからそういう無駄な作業している人って沢山いるんだろうなあ、っていうのが大方の反応だったかと思う。

いや、そうじゃないだろ。私の意見はこれ。

プログラマがちょちょいとスクリプト書けば数分で終わるような仕事をまる一日かけてやってるようなのは、今までに何度も見てきた。若い頃は、なんでそんな無駄なことするんだろうと思ってたが、今の意見はちょっと違う。

プログラマはプログラマらしい仕事をしなければいけないと思う。でも、例えば事務職の人が一日かかる仕事が、プログラマがやると10分で終わるからといってその仕事をプログラマに任せるべきかというとそうではない。やりたいことを伝えるのに時間がかかるというのもあるが、それより大事なのはプログラマの気持ちの問題。そんな雑務押し付けられるとモチベーションが下がるし、他の仕事からの気持ちの切り替えにもコストがかかる(いわゆるコンテクストスイッチのコストの問題)。いろいろなところでコストがかかって、そういうのが大きな生産性低下につながる。

プログラマのコンテクストスイッチのコストについてはこちらを参照。
プログラマーが会議を嫌いな理由
(この参照先のPaul Grahamの記事の日本語訳を探したが見つからなかった。情報求む。)

ポール・グレアム「クリエイターのスケジュールと経営者のスケジュール」

仕事を奪うのはかわいそうとか、そういう感情的な話をしているのではない。プログラマがやると10分できる仕事を非プログラマが一日かけてやるのはそれなりの経済的合理性がある。

元ツイートの人は事務職じゃなくてデザイナーのようだけども、そういうことができるスキルが期待されていなかったというだけではないかな。期待を上回るスキルがあることは素晴らしいことだと思うのだけれど。

修正履歴:
当初見つけられなかったPaul Grahamのエッセイの翻訳が見つかったので、リンクを貼り直しました。指摘いただいた@saitotetsuyaさんに感謝です。

今度こそ若いエンジニアへ

これ読んだ。
読むために生まれ:若いエンジニアへ
言っていることには異論はない。突貫工事を続けて長時間労働しても効率が良くないというのは確かにそうだ。しかしこれは、タイトルに反して若いエンジニアに向けたメッセージではなく、マネージャー層に向けたメッセージではないか。若いエンジニアを長時間こき使うっていうのはリソースの使い方としては最悪ですからね、ちゃんとマネージメントしましょうね、と言っているように見える。

突貫工事は確かによくない。でもある一定の割合で突貫工事は発生する。そして、若手エンジニアが突貫工事にアサインされることは多い。しかも若手は大抵自分で仕事を選ぶ権利はない。若手、特に新人が突貫工事/炎上案件に巻き込まれた時にできる最善の策といえば、1)きちんと貢献できるように、でも体は壊さない程度に働く、2)開発プロセスがちゃんとしているかどうか見極め、ダメだと思ったら積極的に改善点を指摘する、3)絶望的な状態なら逃げる(他の会社に行く)、というくらいではないだろうか。どれもそう簡単ではないのだが。

なので、私なりの意見として、若手(とくに新人)に向けたメッセージを改めてまとめてみる。

プロセスについて:

  • 炎上案件に巻き込まれて遅い帰りが続いたとしても、すぐにブラック企業だと決めつけないでちょっとは我慢してみよう。もちろん体力の限界まで頑張る必要はない。無理なときは無理と言おう。
  • 完璧なプロセスなんてどこにも存在しない。問題は少しずつでも改善するかどうかだ。大炎上案件があったとしても、その後真剣に反省して改善しようとしている組織なら未来はある。そして改善策について自分でも意見を言おう。
  • 残業・休日出勤が常態化していて、しかもだれもそれを反省していなく改善しようとしていない組織ならば、早く逃げたほうがいい。
  • 個々人がミスをしないように気をつけるのは当然だが、バグによるトラブルを個人のせいにするような組織なら気をつけたほうがいい。特にリリース後のバグについて、「お前のミスのせいで損害を受けた」みたいなことを言われる組織なら早く逃げたほうがいい。だってそれは個人ではなくてプロセスの問題だから。

スキルについて:

  • 上記のようにプロセスはとても大事。でも、なんでもプロセスの改善で何とかなるということを言い出すおじさんはあまり信じないほうがいい。
  • どんなにプロセスが良くても、ソフトウェア産業は個人のスキルがないとなにも作れない。スキルを磨くことを忘れないようにしよう。会社が提供する研修がすべてではない。
  • 上記リンク先ブロクには、「エンジニアの創造性を引き出すものは、長時間労働ではなく集中である」とあるが、集中よりさらに創造性を引き出すのはスキルである。

ということです。みんな頑張ってね。