仙石浩明の日記

2006年5月4日

プログラマ 35歳 定年説 hatena_b

「プログラマ 35歳 定年説」、みなさんも一度は 聞いたことがあるのではないかと思います。 35歳ぐらいになったらプログラミングなんて仕事は若い人に譲って、 マネジメントをやりなさい、 という趣旨ですね。

その理由として、体力的な面だとか、 歳をとってくると新技術を覚えられないとかが、 あげられるようです。 実際、多くの企業でプログラミングは新人ないし外注の人 (最近はオフショアも増えてきました) の仕事とされ、 中堅社員はマネジメントや上流行程を担当することが多いようです。

そして、この定年説に真っ向から異を唱える主張が、 ここ 10年くらいずーっと続いています。 よくもまあ、こんなに長い間、 アンチ定年説が唱え続けられるものだと感心してしまいますが、 やはりこれは定年説に異を唱える人が多いにもかかわらず、 世間一般では 35歳を越えるあたりで、プログラマ人口が減るからなのでしょう。 寄る年波には勝てず、とか上司の強い勧め、とかで 泣く泣く (?) プログラミングの現場から離れる人が多いのでしょう。

というわけで、よくあるアンチ定年説を今さら唱えても仕方がないので、 ここでは逆に、あえて肯定してみることにします。

高度な知的労働は、歳をとってもできる傾向があるようです。 長年の経験が役に立つから、 老化する部分を補える というわけですね。 医師や弁護士、芸術家に音楽家、棋士や料理人などなど。 この伝でいけば、プログラマも仲間に入れてもらえそうな感じがしますし、 実際 35歳を越えても若い人を圧倒するパフォーマンスで コーディングしている人も大勢います。 だからこそ「プログラマ 35歳 定年説」は間違いだ、 と声高々にみなさん主張されるのでしょう。

しかし、もし他の「高度な知的労働」においても「定年」が存在したとしたら?
プログラマにも定年があってもおかしくはないのではないでしょうか?

そんなバカな。外科医とかだと手元が狂うと危ないから、 自主的な引退はあるかも知れないけれど、 弁護士や芸術家に定年があるなんて聞いたことがないぞ。

確かにその通りなのですが、 医師や弁護士は国家試験に合格しなければなることができません。 国家試験に合格できずに医師や弁護士になるのをあきらめる人も沢山います。 将棋の棋士はもっと厳しくて、23歳までに初段になれないと 奨励会を退会しなければなりません。 芸術家や料理人は、腕を認められなければ食べるのにも困ってしまうかも知れません。

それが何か? 定年のあるなしと関係あるの?

いえ、ですからプログラマの 35歳というのは、 医師や弁護士にとっての国家試験であり、 奨励会の棋士にとっての 23歳であり、 芸術家や料理人にとっては、師匠に認められるか否か、 ということなのではないでしょうか? つまり、他の「高度な知的労働」の職種では、 定年うんぬん以前に、そもそもその職業につくこと自体に壁があります。 どんなにその職業に従事したいと思っても、 国家試験に合格できなかったり、 初段になれなかったり、 師匠に認められて弟子にしてもらえなかったりすれば、 35歳よりもっと早い段階でその道をあきらめることが多いのではないでしょうか。

確かにプログラマってのは、ピンからキリまでいて、 キリのほうは際限がないなぁ。 本当は 35歳といわず、もっと早い段階でプログラマには向いてないことを 自覚したほうが幸せなのではないか、と思うような人もいるよなぁ。

ですよね? こういっちゃミもフタもないんですが、 いくらプログラミングの才能がない人でも、 体力さえあればそれなりに役にたっちゃうことも多いんですよね。 ぶっちゃけ、レベルが低いプログラムでもいいなら、 プログラミング言語の文法を一通り知ってさえいれば誰でも書けちゃいますし、 デバッグ環境さえ整っていれば、デバッグもなんとかなる。 もちろん時間は優秀なプログラマの何倍、何十倍もかかりますけどね。 そもそもこの業界、慢性的な人手不足ですから、 猫の手でもいいから借りたい。 何十倍も仕事が遅くてもいいから手を借りたい。 ただ、あくまで「猫」なんで、デスマーチに耐えられる体力はないと困る。

なるほど。そういえば「アンチ・プログラマ35歳定年説」を唱える人って、 優秀なプログラマが多いような気がする。 優秀ならばパフォーマンスは体力とは関係ないから、 35歳なんて関係ないと言い切れる。 そもそもプログラマに向いている人と、まるで向いてない人が、 いっしょくたに「プログラマ」と呼ばれているのが問題なのか。

そういうことなんだと思います。 プログラマに向いているか向いていないか判断できない人が、 プログラマの上司をやってる、 というのも一因でしょうね。 医師, 弁護士, 棋士などには、足切りのための絶対的な評価基準がありますし、 芸術家や料理人などには、師匠がいて、能力の有無をきちんと判断してくれる。 プログラマだけが基準なしに、ただ若いというだけで、 いっぱしのプログラマ気取りになってしまう。 師匠がいれば、「プログラマを気取るのは百万年早い!」って 怒鳴ってくれるんでしょうけどね。

まとめると、 プログラマには少なくとも二種類あって、 他の「高度な知的労働」の職種と同様に定年がない「優秀なプログラマ」と、 若いだけが取りえの「見習いプログラマ」とを、 きちんと区別する必要があると。 で、「見習いプログラマ」は 35歳といわず、もっと早い段階で、 プログラミングに向いてないことを自覚し、 もっと自分に向いている仕事を見つけるべきだ、ということですね。

そして「優秀なプログラマ」には、 きちんとその能力を評価し、育てていく環境を整えるべきでしょう。 私の感覚では、本当にプログラミングに向いている人って、 20人に一人くらいしかいないように思えるんですよ。 だからこそ、そういう希有な人材には最高の環境を提供してあげたい、と思います。

Filed under: 元CTO の日記,技術と経営 — hiroaki_sengoku @ 17:06
2006年5月3日

昭和記念公園

五月連休ということで、今日は妻と一緒に昭和記念公園へ行ってきました。 自宅から近いということもあり、よく行く公園です。 自宅から南武線で立川駅まで行き、青梅線に乗り換えて一駅、 西立川駅で降りると、すぐ昭和記念公園の西立川口です。

ところが今日は青梅線に乗った段階でいつもと違う... いつになく混みあっています。 これはもしかしてみんな昭和記念公園? 悪い予感は的中し、 昭和記念公園の西立川口の前には人人人... おまけに入場券の券売機の前には長蛇の列が... ;-(

私も妻も大阪育ちなので、とても「いらち」です。 とてもじゃないけどこんな長蛇の列に並んで待つなんてことは不可能です。 残念ですが公園に入るのはあきらめることにしました。

ところが、今日は来る途中のスーパーで買った弁当を持ってきていました。 すぐ食べないと悪くなってしまいます。 どこかに食べるところはないかと、 公園の外周に沿って立川の方へ歩き始めました。 すると、自転車専用の入口がありました。 自転車で来た人はここから公園へ入れるようです。

ということは、もしかして入場券の券売機もある?

はやる心を抑えて入口のところまで行ってみると、 確かに券売機があって、別に自転車専用というわけでも無さそうです。 無事、入場券二枚を get ! しました。 西立川口に戻って、長蛇の列を尻目に公園に入ることができました!

昭和記念公園 ポピー花畑

アイスランドポピー満開の花畑の向こうに、 人で埋め尽された「みんなの原っぱ」が見えます。 11ヘクタールもある原っぱが、こんなに人だらけになるとは...

Filed under: その他 — hiroaki_sengoku @ 22:19
2006年5月3日

勝ち組になるには

先月 4/14 19:30 NHK で、特報首都圏「就職戦線異状あり・格差社会の不安」と 題する番組があった。 新卒の学生さん達が「勝ち組になる」ことを目指して 就職活動を行なっているのだという。

そりゃ、勝てるものなら勝ちたいと思うのは人の常なので、 これから社会に出ていこうとする学生さん達が、 将来勝ち組になれるような就職先を選ぼうとするのは至極当然のことだと思う。

ところが

学生さん達曰く、「勝ち組になるため、儲かっている会社に就職したい」。

オイ、それは根本的に間違ってるぞ、と言いたくなる。 儲かっている会社、それはお金を産み出す仕掛けが確立している会社である。 つまりできるだけ属人性を排した、回り続ける仕掛けが確立している会社である。 このような会社が新卒採用を行なうのは、 仕掛けを維持するために「歯車」の補充を行なうためである。

すでに出来上がっている仕掛けは、それが陳腐化するまでは、動き続けるだろう。 動き続けるには若い人を採用し育てることが必須だから、 そういうことまでも含んだ仕掛けである。 このような仕掛けに参加すれば、 仕掛けの中で活躍できるレベルまで育ててもらえるし、 仕掛けが動き続ける限りは安泰である。

しかしそれは「勝ち組」ではない。 勝ち組なのは、放っておいても回り続けるその仕掛けを作り上げた人であって、 いったん仕掛けが回りはじめたとき、 その人はその仕掛けの中にはいないはずである。 それが「属人性を排する」という意味である。 「属人性を排する」すなわち「置き換え可能」な人材で 仕掛けを回すことであり、 置き換え可能な人材は「勝ち組」では有り得ない。

したがって、勝ち組になるには、 出来上がって回りはじめた仕掛けに歯車として参加するのではなく、 そういう仕掛けが無いところに参加し、 仕掛けを作る側にまわらなければならない。

もちろんどうやって仕掛けを作るべきか最初は分からないだろう。 当然、失敗することもある。 しかしそうやって無から有を作り出す経験を積んだ人材は決して、 置き換え可能ではない。 仕掛けを作り上げようと悪戦苦闘する組織は、属人性のカタマリである。 将来の勝ち組になるには、 そういった組織の中に身を置かねばならない。

More...
Filed under: 技術と経営 — hiroaki_sengoku @ 07:52
2006年5月2日

VPN-Warp 入門編 (6)

前回は、WWW ブラウザで VPN-Warp へアクセスする方法を紹介しました。 基本的には https://リレーサーバのホスト名/パス という URL を WWW ブラウザでアクセスすれば、 relayagent に設定したフォワード先の WWW サーバへつながるわけですが、 二点ほど注意すべき点があります。

  1. 「https」と「http」の違い
  2. ホスト名の違い

WWW サーバに直接アクセスする場合の URL を、 例えば http://intra/path としましょう。 この場合、WWW サーバへ送られるリクエストヘッダは、 次のようになります(説明の都合上大幅に単純化しています):

GET /path HTTP/1.1
Host: intra

一方、VPN-Warp 経由 (つまり、ブラウザ → リレーサーバ → relayagent → WWW サーバ) でアクセスする場合に、 WWW サーバへ送られるリクエストヘッダは、URL が https://relay.klab.org/path ですから、 次のようになります:

GET /path HTTP/1.1
Host: relay.klab.org

両者を比べると、「Host: 」フィールドの部分が異なりますね。 多くの WWW サーバには、バーチャルホストと呼ばれる機能があって、 一台の WWW サーバで、いろんなホスト名の URL を受け付けて、 ホスト名に応じて異なるページを見せることができるわけですが、 要はこの「Host: 」フィールドを見て、 配信するコンテンツを切替えているわけです。

したがって、「Host: relay.klab.org」のままでは、 「intra」のコンテンツを見ることができないケースが多いでしょう。 この (2) ホスト名の違い の問題を解決するために、 relayagent には「Host: 」フィールドの書き換え機能があります。 この場合でしたら、 「-h intra」オプションを追加指定することにより、 リクエストヘッダの「Host: 」フィールドを「intra」に書き換えます。

これで少なくともリクエストヘッダは、直接アクセスする場合と、 VPN-Warp 経由でアクセスする場合が同じになりました。 コンテンツの HTML 文書で URL を絶対指定したりしない限りは、 普通にブラウズできるでしょう... か?

コンテンツが全て静的なページから構成されているのであれば、 その通りなのですが、 普通は PHP や Java などを使用して動的に生成するページもあるでしょう。 その場合、PHP インタプリタやサーブレットコンテナは、 いま生成中のページの URL がなんであるか把握していて、 ページを出力するときに、絶対指定を出力してしまうケースがあるので 注意が必要です。

特に、(1) 「https」と「http」の違い は留意しておくべきでしょう。 WWW サーバにとっては、80番ポートでアクセスを受け付けているわけですから、 PHP インタプリタないしサーブレットコンテナは、 「http」なページを出力中だと認識しているはずです。 ところが WWW ブラウザにとっては、アクセスしている URL は「https」です。 WWW サーバがレスポンス中に「http」な URL を出力してしまうと、 ブラウザはその URL をたどれなくなります。

とはいえ、動的なページを出力する際に絶対 URL を指定してしまうと、 コンテンツの URL を変更したいときも不便ですし、 そもそも相対 URL で済むところをわざわざ絶対指定する必然性もないので、 普通の Web アプリケーションを使う限りはあまり問題とはならないようです。 WWW サーバのレスポンスを全て監視して、 適宜 URL の書き換えを行なえば解決できる問題ではあるのですが、 ごく少数の絶対 URL の書き換えのためだけに、 全てのレスポンスを監視するのは、 パフォーマンスの点で割が合わないと言えそうです。

むしろ問題となるのは、リダイレクトです。 リダイレクトの場合、WWW サーバは次のようなレスポンスヘッダを返します (説明のため大幅に単純化しています):

HTTP/1.1 301 Moved Permanently
Location: http://intra/path

このような 301 レスポンスを受け取ると、 WWW ブラウザは「Location: 」フィールドで指定された URL のページを 表示しようとします。 そして「Location: 」フィールドは絶対 URL で指定されます。

WWW サーバは、ページの URL は「http://intra/...」である と認識していますから、 同じホスト名の URL へリダイレクトを行なう場合 「Location: 」フィールドには「http://intra/...」が指定されます。 これをそのまま WWW ブラウザに伝えてしまうと、 WWW ブラウザは「http://intra/...」すなわち VPN-Warp を介さずに 直接 WWW サーバへアクセスしようとしてしまいます。

この問題を解決するために、 relayagent にはレスポンスヘッダの「Location: 」フィールドを書き換える機能が あります。 すなわち「-H」オプションを指定すると、 「Location: 」フィールドのホスト名とフォワード先のホスト名 (今回の例の場合は intra ですね) が一致する場合、 それをリレーサーバの URL へ書き換えます。 前述したレスポンスヘッダの場合であれば、

HTTP/1.1 301 Moved Permanently
Location: https://relay.klab.org/path

に書き換えるわけです。これでめでたく WWW ブラウザは リダイレクト先のページも、VPN-Warp 経由でアクセスするようになります。

Filed under: システム構築・運用 — hiroaki_sengoku @ 15:13
2006年5月2日

健全なニート

人は働かねばならない、と誰が決めたんだろう?
働かなくても生活できるなら、働く必要はないわけで、
ただ単に、勤労は国民の義務だ、なんて言っても意味がない。

[技術]Agdaの紹介Flashから引用:

定理証明ツールが動いているところ、生まれて初めて見た。感動。自分もつかってみたいが、ドカタSEなのでそれよりやらないといけないことがあるのが悲しいところ。資産が50億ぐらいあったらニートになって毎日こんなことばっかりしたいな。これ、俺の夢(←駄目人間)。

50億もなくても生活できると思うが、 まったくお金を稼がずに生活しようと思うと、 数億は必要だと思われるので、 なかなか簡単ではない。

しかしながら、 技術者の生産性には人によって 3桁の差があるわけで、 並みの技術者の数倍の生産性がある人なら 大勢いるだろう。 そういう人たちにたとえば一日3~4時間だけ 会社の利益に直結することをしてもらって、 一日の大半の時間は好きなことをやってもらう。 もともと生産性が高い人なら、3~4時間の労働だけでも、 生活に困らないくらいの給料を出すことができる。

ほとんどの時間、 引きこもって好きなことをしているように見えるわけで、 まあニートみたいに見えるかもしれないが、 向いてない仕事を嫌々やるより、 好きなことをとことん突き詰めるほうが、 よほど健全だと思うし、 実は社会への貢献度合いも高くなる可能性が高い と思うのだが、どうだろうか。

Filed under: 技術と経営 — hiroaki_sengoku @ 12:08
2006年5月1日

EPOLL_CTL_ADD を行うタイミング

epoll版 stone で EPOLL_CTL_ADD を行うタイミングについて。

基本的には doAcceptConnect 内で行う。 つまり、中継元からの接続を accept し、 中継先へ connect するルーチン。 このルーチンは、Pair リストへの挿入 (insertPairs) を行う 唯一のルーチンでもある。 このルーチンで EPOLL_CTL_ADD を行わないのは、以下の場合のみ。

(1) proto_close フラグが立っている場合
エラー等の原因で close 要求が行われている場合、 中継の必要がないから、EPOLL_CTL_ADD も行わない。
(2) 中継先の Pair かつ proto_noconnect フラグが立っている場合
proto_noconnect フラグが立っているのは 中継を行わない Pair であることを示す。
この場合は、EPOLL_CTL_ADD を行う必要はない。

ところが、stone を proxy として用いるとき、 proto_noconnect フラグが立つ。 これは中継元からリクエストヘッダを受け取るまでは 中継先が決まらないため。 このとき、(2) に該当するので EPOLL_CTL_ADD が行われない。

このため、stone.c 2.2.2.20 では、proxy として用いたとき EPOLL_CTL_ADD が行われず、 中継先への connect 完了を検知できなくなってしまっていた。

中継先が決まった段階で EPOLL_CTL_ADD を行うよう修正。

$Id: stone.c,v 2.2.2.21 2006/04/29 07:32:28 hiroaki_sengoku Exp $

Filed under: stone 開発日記 — hiroaki_sengoku @ 18:24
2006年5月1日

livedoor blog 生ログ取得スクリプト (2)

昨日書いた「livedoor blog 生ログ取得スクリプト」を使って、 毎日前日の生ログを取得する cron を仕掛けておいたのですが、 月が代わった途端に問題発覚(^^;)。 livedoor blog の生ログって、月単位なのですね。 つまり、生ログを参照するページの URL は、

http://analyzer.livedoor.com/log/raw?page_id=22222&y=2006&m=4

などとなっていますが、「m=4」を指定しない限り 4月分のログは表示されない (今月分のみ表示される)、 という仕様のようです。 livedoor blog の有料プランを始めてから初めての月代わりだったもので...

というわけで、生ログを取得する際に日付を指定できるように修正してみました。

livedoor.pl gcd date 2006-04-30 raw_log 100 -

などと、「date YYYY-MM-DD」ないし「date YYYY-MM」を指定することにより、 生ログを取得する日付ないし月を指定できます。 「raw_log 100 - 」は、生ログ 100ページ分 (2000行) を標準出力 (「 - 」) に出力する、という意味です。

生ログを遡っていく途中で、指定した日付と異なる日付の生ログが得られたら、 それ以上の取得をストップするので、ページ数は多めに指定しておいて大丈夫です。 また、ついでに「ブログのエクスポート(バックアップ)」もサポートしました。

livedoor.pl gcd export_reserve

で、「エクスポートファイルの作成」を行ない、

livedoor.pl gcd export バックアップファイル名

で、エクスポートファイルをダウンロードして 「バックアップファイル名」で保存します。

以上の機能は、 livedoor blog の有料プランを選択していないと使用できませんが、 スタイルシート(CSS)および HTMLのテンプレートの取得は、 ブログのデザインを custom に設定していれば使用できます。

まず、livedoor blog の管理ページで、 「カスタマイズ/管理」-「デザインの設定」を選び、 デザインとして「カスタマイズ」を選択したときの URL を確認します。 例えば

http://cms.blog.livedoor.com/cms/design/edit?blog_id=1600549&id=11111

といった感じの URL になると思います。 「blog_id=1600549」および「id=11111」の数字を、 スクリプト先頭の

"BlogID" => 1600549,
"ID"     => 11111,

の部分に設定しておいて、

livedoor.pl gcd css CSSファイル名

と実行すれば、スタイルシート(CSS) が「CSSファイル名」で保存されます。 「css」の代わりに、 「index_tmpl」「article_tmpl」「category_tmpl」「monthly_tmpl」 を指定すれば、それぞれ「トップページ」「個別記事ページ」 「カテゴリアーカイブ」「月別アーカイブ」の HTMLテンプレートを 保存できます。 一度のスクリプト実行で一括して保存することもできます:

livedoor.pl gcd css blog.css index_tmpl blog.html \
        article_tmpl blog_article.html \
        category_tmpl blog_category.html \
        monthly_tmpl blog_month.html

「スタイルシート(CSS)」を、「blog.css」へ、
「トップページ」を、「blog.html」へ、
「個別記事ページ」を、「blog_article.html」へ、
「カテゴリアーカイブ」を、「blog_category.html」へ、
「月別アーカイブ」を、「blog_month.html」へ、
それぞれ保存します。

livedoor.pl (livedoor blog 生ログ & CSS/テンプレート 取得スクリプト)

#!/usr/bin/perl
use LWP::UserAgent;
use HTTP::Request::Common;
use CGI qw/unescapeHTML/;

%blogs = (
    "gcd" => {
        "User"   => "hiroaki_sengoku",
        "Pass"   => "xxxxxxxx",
        "BlogID" => 1600549,
        "ID"     => 11111,
        "PageID" => 22222,
    },
    "klab" => {
        "User"   => "klab_sengoku",
        "Pass"   => "yyyyyyyy",
        "BlogID" => 1631449,
        "ID"     => 33333,
        "PageID" => 44444,
    },
);

&help unless $_ = shift;
if (exists $blogs{$_}) {
    my $blog = $blogs{$_};
    $User =   $$blog{"User"};
    $Pass =   $$blog{"Pass"};
    $BlogID = $$blog{"BlogID"};
    $ID =     $$blog{"ID"};
    $PageID = $$blog{"PageID"};
} else {
    &help;
}

$ua = new LWP::UserAgent;
$ua->agent("Mozilla/5.0 (Windows; U; Windows NT 5.1; ja)");
$ua->env_proxy();
$ua->cookie_jar( {} );
my $res = $ua->request(POST "http://member.livedoor.com/login/index",
                       [ "livedoor_id" => $User, "PASSWORD" => $Pass,
                         ".next" => "", ".sv" => "" ]);
while (my $type = shift) {
    if ($type eq "date") {
        $_ = shift;
        if (/^(\d\d\d\d)-(\d\d)(?:-(\d\d))?$/) {
            $Year = $1;
            $Month = ($2 + 0);
            $Date = $_;
        } else {
            die "date must be YYYY-MM-DD: $_\n";
        }
    } elsif ($type eq "css" ||
             $type eq "index_tmpl" || $type eq "article_tmpl" ||
             $type eq "category_tmpl" || $type eq "monthly_tmpl") {
        my $file = shift;
        open(OUT, ">$file") || die;
        my $url = "http://cms.blog.livedoor.com/cms/design/edit"
            . "?tmpl=$type&blog_id=$BlogID&id=$ID";
        my $req = new HTTP::Request GET => $url;
        my $res = $ua->request($req);
        if ($res->content =~
            /\<textarea .*name=\"content\" [^\>]*\>([^\<]+)\<\/textarea\>/) {
            my $content = unescapeHTML($1);
            $content =~ s/\r\n/\n/g;
            print OUT $content, "\n";
        }
        close(OUT);
    } elsif ($type eq "raw_log") {
        my $npage = shift;
        ($npage =~ m/^\d+$/ && $npage >= 1) || &help;
        my $file = shift;
        &help unless $file;
        open(OUT, ">$file") || die;
        my $url = "http://analyzer.livedoor.com/log/raw?page_id=$PageID";
        if ($Date) {
            $url .= "&y=$Year&m=$Month";
        }
        my $prepat = '\<td\b[^\>]*\>\<strong\>\<small\>';
        my $postpat = '\<\/small\>\<\/strong\>\<\/td\b[^\>]*\>';
        my $datematch = 0;
        pages: for (my $i=1; $i <= $npage; $i++) {
            my $req = new HTTP::Request GET => "$url&p=$i";
            my $res = $ua->request($req);
            my $datepat = '\d\d\d\d\-\d\d\-\d\d \d\d\:\d\d\:\d\d';
            my $date;
            for (split(/(\<small\>$datepat\<\/small\>)/o, $res->content)) {
                if (/^\<small\>($datepat)\<\/small\>$/o) {
                    $date = $1;
                } elsif (/^\<\/th\>\s*\<\/tr\>\s*/) {
                    my @record;
                    for (split(/\<\/tr\>\s*/, $')) {
                        my $column;
                        if (/$prepat(.*)$postpat/o) {
                            if ($1 eq 'URL') {
                                $column = 0;
                            } elsif ($1 eq 'リファラ') {
                                $column = 1;
                            } elsif ($1 eq 'ブラウザ') {
                                $column = 2;
                            } elsif ($1 eq 'リモートホスト') {
                                $column = 3;
                            } else {
                                die "Unknown column: $_\n";
                            }
                        }
                        if (/\<td\b[^\>]*\>\<small\>(.*)\<\/small\>\<\/td\b[^\>]*\>/){
                            $_ = $1;
                            s/\<\/?a\b[^\>]*\>//g;
                            if (/,/) {
                                s/\"/\"\"/g;
                                $_ = "\"$_\"";
                            }
                            $record[$column] = $_;
                        } elsif (/^\<\/table\>/) {
                            last;
                        } elsif (! /^\<tr\>\s*\<th\b[^\>]*\>/) {
                            die "Unknown format: $_\n";
                        }
                    }
                    if (! defined($Date) || $date =~ /^$Date/) {
                        $datematch = 1;
                        print OUT $date, ",", join(',', @record), "\r\n";
                    } elsif ($datematch) {
                        last pages;
                    }
                }
            }
        }
        close(OUT);
    } elsif ($type eq "export_reserve") {
        my $url = "http://cms.blog.livedoor.com/cms/import/mt/export_reserve";
        my $req = new HTTP::Request GET => $url;
        my $res = $ua->request($req);
        if (! $res->is_success) {
            print STDERR "fail to reserve export";
            exit 1;
        }
    } elsif ($type eq "export") {
        my $file = shift;
        open(OUT, ">$file") || die;
        my $url = "http://cms.blog.livedoor.com/cms/import/mt/export";
        my $req = new HTTP::Request GET => $url;
        my $res = $ua->request($req);
        print OUT $res->content;
        close(OUT);
    } else {
        &help;
    }
}
exit 0;

sub help {
    print STDERR "Usage livedoor <blog> <opt>...\nblog: ",
    join("\n      ", keys %blogs), "\n",
    'opt:  date YYYY-MM
      date YYYY-MM-DD
      css <file>
      index_tmpl <file>
      article_tmpl <file>
      category_tmpl <file>
      monthly_tmpl <file>
      raw_log <n> <file>
      export_reserve
      export <file>
';
    exit 1;
}
Filed under: プログラミングと開発環境 — hiroaki_sengoku @ 08:45
2006年4月30日

勤労の義務

「勤労の義務」は、 言うまでもなく日本国憲法第27条に規定されている、 日本国民の3大義務の一つであるが、 そのそろこの勤労に対する固定観念をかえていくべきなのではないか? 来るべき共産社会へのビジョンは、 Web2.0 で初めて見えてきたわけでは決してなく、 何十年も前から様々な形で繰り返し描かれてきた。 例えば、

断絶への航海 ハヤカワ文庫 SF (586)
ジェイムズ・P・ホーガン (著), 小隅 黎

などの近未来小説などの形でも。 SF においては、StarTrek を引き合いに出すまでも無く、 未来には貨幣経済が存在しないという設定のほうが、 むしろ普通となっている。

にもかかわらず、いまだに資本主義以前の ナイーブな勤労観がはびこっているのは、 憲法に基づく洗脳教育のたまものなのだろうか?

人力検索はてな から引用:

「年収300万の自分にとって最高に面白い仕事」か「年収1000万の自分にとって最高に面白くない仕事」どちらかを一生しなくてはならないとしたらあなたはどちらを選びますか?その理由は?

この質問者にとっては、面白くても、面白くなくても、 仕事は仕事なのだろう。 そこにはストックという概念すら存在せず、 ただフローを得るための勤労という思い込みに囚われている。

まずバランスシートを義務教育で教えるべきなのだろうか?
それとも、貨幣経済を飛び越して Web2.0 を教えるべきなのだろうか?

Filed under: 技術者の成長 — hiroaki_sengoku @ 11:19
2006年4月30日

livedoor blog 生ログ取得スクリプト (1)

すでに誰かが絶対に書いているはずとは思ったのですが、 探すよりも書いた方が早そうだったので、 livedoor ブログの生ログを取得する perl スクリプトを 書いてみました。 ついでに、デザインをカスタマイズしたときの、 スタイルシート(CSS)やHTMLのテンプレートも取得できます。 例えば、

livedoor.pl gcd raw_log 10 log.csv

などと実行すれば、10 ページ分 (200行) の生ログを、 CSV 形式でファイル「log.csv」に保存できます。 また、

livedoor.pl gcd index_tmpl index.html

などと実行すれば、インデックスページのHTMLテンプレートを、 ファイル「index.html」に保存できます。 第一引数「gcd」の部分には、 ブログのアカウント名 (スクリプトの先頭部分で定義しています) を 指定してください。 私の場合、 「GCD 日記」と 「仙石浩明CTO の日記」の 二つのブログアカウントがあるので、 それぞれ「gcd」と「klab」という名前で定義しています。

余談ですが、二つのブログを書いているのは、 個人用と会社用とを区別しようというわけではありません。 もともと私のなかでは趣味と仕事の境界線が曖昧なので、 個人と会社でブログを区別しようとしても混ざってしまうでしょうから、 区別することに意味があるとは思えません。 じゃ、なぜ二つのブログなのかと言えば、 「GCD 日記」のほうが よりメモ的でネタを蓄えておき、ある程度考えがまとまったものを 「仙石浩明CTO の日記」へ 書こう、というのが そもそもの意図でした。

やっつけ仕事なので、突っ込みどころ満載(^^;) のスクリプトだとは思いますが、 livedoorブログをお使いの方はご利用頂ければ幸いです。 もちろん、ご利用の際は先頭部分のユーザID & パスワード等を 適宜修正してください。 また、スクリプト中で日本語を使っているので、 このスクリプトは EUC-JP で保存する必要があります。

livedoor.pl (livedoor blog 生ログ & CSS/テンプレート 取得スクリプト)

#!/usr/bin/perl
use LWP::UserAgent;
use HTTP::Request::Common;
use CGI qw/unescapeHTML/;

%blogs = (
    "gcd" => {
        "User"   => "hiroaki_sengoku",
        "Pass"   => "xxxxxxxx",
        "BlogID" => 1600549,
        "ID"     => 11111,
        "PageID" => 22222,
    },
    "klab" => {
        "User"   => "klab_sengoku",
        "Pass"   => "yyyyyyyy",
        "BlogID" => 1631449,
        "ID"     => 33333,
        "PageID" => 44444,
    },
);

&help unless $_ = shift;
if (my $blog = $blogs{$_}) {
    $User =   $$blog{"User"};
    $Pass =   $$blog{"Pass"};
    $BlogID = $$blog{"BlogID"};
    $ID =     $$blog{"ID"};
    $PageID = $$blog{"PageID"};
} else {
    &help;
}

$ua = new LWP::UserAgent;
$ua->agent("Mozilla/5.0 (Windows; U; Windows NT 5.1; ja)");
$ua->env_proxy();
$ua->cookie_jar( {} );
my $res = $ua->request(POST "http://member.livedoor.com/login/index",
                       [ "livedoor_id" => $User, "PASSWORD" => $Pass,
                         ".next" => "", ".sv" => "" ]);
while (my $type = shift) {
    if ($type eq "css" || $type eq "index_tmpl" || $type eq "article_tmpl" ||
        $type eq "category_tmpl" || $type eq "monthly_tmpl") {
        my $file = shift;
        open(OUT, ">$file") || die;
        my $url = "http://cms.blog.livedoor.com/cms/design/edit"
            . "?tmpl=$type&blog_id=$BlogID&id=$ID";
        my $req = new HTTP::Request GET => $url;
        my $res = $ua->request($req);
        if ($res->content =~
            /\<textarea .*name=\"content\" [^\>]*\>([^\<]+)\<\/textarea\>/) {
            my $content = unescapeHTML($1);
            $content =~ s/\r\n/\n/g;
            print OUT $content, "\n";
        }
        close(OUT);
    } elsif ($type eq "raw_log") {
        my $npage = shift;
        ($npage =~ m/^\d+$/ && $npage >= 1) || &help;
        my $file = shift;
        open(OUT, ">$file") || die;
        my $url = "http://analyzer.livedoor.com/log/raw?page_id=$PageID";
        my $prepat = '\<td\b[^\>]*\>\<strong\>\<small\>';
        my $postpat = '\<\/small\>\<\/strong\>\<\/td\b[^\>]*\>';
        for (my $i=1; $i <= $npage; $i++) {
            my $req = new HTTP::Request GET => "$url&p=$i";
            my $res = $ua->request($req);
            my $datepat = '\d\d\d\d\-\d\d\-\d\d \d\d\:\d\d\:\d\d';
            my $date;
            for (split(/(\<small\>$datepat\<\/small\>)/, $res->content)) {
                if (/^\<small\>($datepat)\<\/small\>$/) {
                    $date = $1;
                } elsif (/^\<\/th\>\s*\<\/tr\>\s*/) {
                    my @record;
                    for (split(/\<\/tr\>\s*/, $')) {
                        my $column;
                        if (/$prepat(.*)$postpat/o) {
                            if ($1 eq 'URL') {
                                $column = 0;
                            } elsif ($1 eq 'リファラ') {
                                $column = 1;
                            } elsif ($1 eq 'ブラウザ') {
                                $column = 2;
                            } elsif ($1 eq 'リモートホスト') {
                                $column = 3;
                            } else {
                                die "Unknown column: $_\n";
                            }
                        }
                        if (/\<td\b[^\>]*\>\<small\>(.*)\<\/small\>\<\/td\b[^\>]*\>/){
                            $_ = $1;
                            s/\<\/?a\b[^\>]*\>//g;
                            if (/,/) {
                                s/\"/\"\"/g;
                                $_ = "\"$_\"";
                            }
                            $record[$column] = $_;
                        } elsif (/^\<\/table\>/) {
                            last;
                        } elsif (! /^\<tr\>\s*\<th\b[^\>]*\>/) {
                            die "Unknown format: $_\n";
                        }
                    }
                    print OUT $date, ",", join(',', @record), "\r\n";
                }
            }
        }
        close(OUT);
    } else {
        &help;
    }
}
exit 0;

sub help {
    print "Usage livedoor <blog> <opt>...\nblog: ",
    join("\n      ", keys %blogs), "\n",
    'opt:  css <file>
      index_tmpl <file>
      article_tmpl <file>
      category_tmpl <file>
      monthly_tmpl <file>
      raw_log <n> <file>
';
    exit 1;
}
Filed under: プログラミングと開発環境 — hiroaki_sengoku @ 09:07
2006年4月29日

面接FAQ: 仮に、何をしてもいい、と言われたら、何をしますか? hatena_b

弊社の面接で(私に)よく聞かれること、 面接官自身が語る面接攻略法。 今までお話ししてきたことをいったんまとめてみます。

(1) 高い技術力って例えばどんなことですか?
志望動機が「技術を学びたい」なのに、 肝心のその技術がどういうものかイメージできていない、 言い換えれば自分が何をしたいのか実は分かっていない、 これはかなり問題ですよね?
(2) 何か質問はありませんか?
面接を受けに来て、なにも質問しようとしない人がいます。 分かっていないのに分かったつもりになってしまう、 これは技術者にとって致命的なことです。 どこまでも自分が分かっていないことを自覚し、 探求し続ける習慣が無ければ、スキルアップは覚束ないでしょう。
(3) 前職でいくらもらっていましたか?
前職給に基づいて給与を決めるため、というわけでは決してなく、 KLab の給与体系にスムーズに移行できるか判断するためです。

今回の質問は、

生活するために働かねばならない、ということが仮になかったとしたら、
仮に、一週間何をしてもいい、と言われたら、何をしますか?

なぜこんな質問をするかと言えば、自分は本当は何をしたいのか 考えてもらいたいからです。

自分が何をしたいか? そんなことは分かっているに決まっているじゃないか、 お金に困らないなら好きなことをするさ、と多くの人は言うでしょう。

じゃ、好きなことって何ですか?

仕事しなくてもいいのなら、あれもしたい、これもしたい、...

その中で一番やりたいのは何ですか?

一日中、寝続けたい。
浴びるほど酒を飲みたい。
何も考えずにぼーっとしていたい。
...

まあ好きにやっててください。(^^;)
もちろん気分転換は必要なので、 時にはハメを外すことも必要でしょう。 でも、何かをやっているからこその気分転換ですよね。 年がら年中、気分転換し続けるわけにはいきませんよね。

だから~、普段は嫌な仕事をやってて、 おまけに残業続きで忙しくて忙しくて、 たまに休日があっても疲れているから何もする気が起きず、 あ~ 仕事しなくてもいいなら好きなことができるのに~

いえ、だからその「好きなこと」ってなんでしょう?
というのが質問なのですが... (-_-;)

一生の時間のうちのかなりの時間を仕事に費やすのですから、 一生かけて取り組みたいと思うようなことをすべきだと思うのです。 好きなことをやっててお金がもらえれば苦労はしない、と多くの人は言うでしょう。 でも、本当に一生かけてもやりたいほど好きなことってありますか?

もしあるなら、その「好きなこと」をやってても生活できない と判断するのはなぜでしょう? 一生かける覚悟があるなら、大抵のことは成し遂げられるでしょう。 嫌々仕事をしたって、好きでやってる人に勝てるはずはありません。 鶏口となるも牛後となるなかれ、 好きなことをしよう

もしそれほど「好きなこと」がないのなら、なぜ探さないのでしょう?
一生かけて嫌々仕事をするのでしょうか。何のために?
生活のため、というのは自分自身への いいわけ なのでは?

- o -

今日から五月連休ですね。 会社によっては 9 連休のところもあるとか。
あなたは何をしますか?

More...
Filed under: 自己啓発 — hiroaki_sengoku @ 09:41
2006年4月29日

SSL_connect に時間がかかる場合の挙動

epoll版 stone のバグを長いこと放置していた... orz
(1) getident
(2) SSL_connect に時間がかかる場合の挙動

(1) のバグは、sd = socket() するまえに epoll_ctl(epfd, EPOLL_CTL_ADD, sd, &ev) していた、という単純バグ。なぜ epoll_ctl が失敗しなかったというと sd を初期化していなかったので、たまたま有効なディスクリプタだったのだろう。これは順序を入れ替えるだけで fix.

(2) は、少し複雑。stone は中継先に connect する際、どのタイミングで接続が確立するかで処理のパスが変わる。たとえば connect が EINPROGRESS を返すと epoll/select 待ちするが、connect を呼び出した時点で接続が確立すれば、直接 readWrite ループへ遷移する。

SSL がからむともっと複雑になる。SSL_accept が完了するまで中継先が決まらない場合もあるし、TCP接続が確立しても SSL接続がなかなか確立しない場合もある。SSLハンドシェークに時間がかかる場合、SSL_accept / SSL_connect は readWrite ループで呼び出されることになる。

SSL_connect が完了する前に中継元からの入出力が行われてはまずいので、readWrite ループ内の epoll/select 待ちでは中継元側のディスクリプタは監視対象からはずしているわけだが、SSL_connect が成功した時点で監視対象に含めなければならない。select 版では正常に動作するのだが、epoll 版では監視対象に含め損なっていた。

     ret = SSL_connect(ssl);
     if (ret > 0) {     /* success */
+       Pair *p = pair->pair;
        /* pair & dst is connected */
        pair->proto |= (proto_connect | proto_dirty);
+       if (p) p->proto |= proto_dirty; /* src */

接続元側 Pair の proto に、proto_dirty フラグを設定することにより、epoll でも監視対象に含めるようになる。 これで epoll 版も正常に動くようになったはず。
現在 senri で動作検証中...

$Id: stone.c,v 2.2.2.20 2006/04/28 21:50:24 hiroaki_sengoku Exp $

Filed under: stone 開発日記 — hiroaki_sengoku @ 07:44
2006年4月28日

Haskell

遅ればせながら Haskell で遊んでいます。 KLab の技術者の中にも、手続き型言語の世界に どっぷりつかっていて他の世界を知らない人は いるので、 tech ML (技術者向の KLab 社内メーリングリスト) で Haskell の紹介をしてみました。

~~ tech ML に投げたメールここから ~~
Subject: [tech:8480] Haskell

仙石です。

唐突ですが、Haskell って知ってますか?

私は面接した人に教えてもらった ;) のですが、最近流行りの関数型言語です。
ブログを見てると、あちこちで話題になっていますね。
入門用のページ:

  やさしい Haskell 入門 (バージョン98)
  http://www.sampou.org/haskell/tutorial-j/index.html

「やさしい」と書いてますが、関数型言語を初めて学ぼうとする人には敷居が
高いかも知れません。まずは簡単な Haskell プログラムを見てみましょう。

------------------------------------------------------------------------
guusuu x
  | x `mod` 2 == 0  =  True
  | otherwise       =  False
------------------------------------------------------------------------

これは guusuu(x) という関数の定義です。

  x を 2 で割った余りが 0 ならば、guusuu(x) = True
  それ以外ならば、                guusuu(x) = False

と読みます。簡単ですね? ;)
早速実行してみましょう。
上記プログラムを test.hs というファイル名で保存しておいて、
Haskell 処理系である ghci コマンドを実行します。

------------------------------------------------------------------------
senri:/home/sengoku/tmp % ghci
   ___         ___ _
  / _ \ /\  /\/ __(_)
 / /_\// /_/ / /  | |      GHC Interactive, version 6.4.2, for Haskell 98.
/ /_\\/ __  / /___| |      http://www.haskell.org/ghc/
\____/\/ /_/\____/|_|      Type :? for help.

Loading package base-1.0 ... linking ... done.
Prelude> :load test.hs
Compiling Main             ( test.hs, interpreted )
Ok, modules loaded: Main.
*Main> guusuu 2
True
*Main> guusuu 7
False
*Main>
------------------------------------------------------------------------

「:load test.hs」というのが「test.hs」を読み込むためのコマンドです。
「guusuu 2」を実行すると、guusuu(2) の値である True が出力されていますね。
これだけだと、あまり能がないので、偶数列を表示させてみましょうか。

------------------------------------------------------------------------
*Main> take 10 [1,2..]
[1,2,3,4,5,6,7,8,9,10]
*Main> take 10 [x|x <- [1,2..], guusuu x]
[2,4,6,8,10,12,14,16,18,20]
------------------------------------------------------------------------

「take 10」というのはその後ろのリストの先頭 10 個の要素を取り出す関数で
す。「[1,2..]」というのは自然数列のリストですね。take を使わずに [1,2..]
を表示させようとすると、無限に自然数列を表示します。

------------------------------------------------------------------------
*Main> [1,2..]
[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22, ...無限に続く...
------------------------------------------------------------------------

途中で止めるには control-C を押します。
さて、

  [x|x <- [1,2..], guusuu x]

という書き方は、グラフ理論の輪講に参加している人や、述語論理を学んだこと
のある人にはおなじみの書き方じゃないでしょうか。自然数列の中で、
述語 guusuu(x) が True であるような x のみ取り出したリスト、という意味です。

じゃ、このプログラムはどうでしょう?

------------------------------------------------------------------------
hurui [] = []
hurui (top:rest) = top:(hurui [x|x <- rest, x `mod` top /= 0])
------------------------------------------------------------------------

関数 hurui は引数としてリストをとります。「[]」は空リストです。つまり要
素が何もないリストですね。引数が空リストならば hurui [] の値も [] です。

引数が [] でない場合は、引数のリストを、先頭 top と残り rest に分解します。
例えば hurui [3,5,9,11,13] の場合、top が 3 で rest が [5,9,11,13] です。

# このあたり、lisp を知っている人にはおなじみの概念ですね

次に top と (hurui [x|x <- rest, x `mod` top /= 0]) をつなげたリストを、
hurui の値として返します。「:」がリストを作るための演算子です。

# lisp で言うところの cons と言えば lisp を知っている人には簡単ですね

では (hurui [x|x <- rest, x `mod` top /= 0]) とは何でしょう?

「/=」というのは等しくない、という演算子です。C で言うところの「!=」です
ね。つまり、rest の中で「x `mod` top /= 0」が True になるものを取り出し
たリストを求め、これを引数として hurui を再帰呼出しして求めたリスト、と
いうことになります。

top が 3 で rest が [5,9,11,13] でしたから、3 で割って余りが 0 でない
(つまり 3 で割り切れない) もののリスト、ということになります。9 以外は 3
で割り切れないので [5,11,13] ですね。これを引数として hurui に与えます。
つまり、3 (top の値) と hurui [5,11,13] の値をつなげたリストが答になりま
す。

同様に hurui [5,11,13] の値は、5 と hurui [11,13] (11 も 13 も 5 では割
り切れないから) の値をつなげたリストですね。というのをどんどん再帰的に繰
り返すと、hurui [3,5,9,11,13] の値は [3,5,11,13] になります。実際に試し
てみましょう:

------------------------------------------------------------------------
*Main> hurui [3,5,9,11,13]
[3,5,11,13]
------------------------------------------------------------------------

スルドイ人はすでに分かっていると思いますが、
この関数は「エラトステネスの篩」です。したがって、

------------------------------------------------------------------------
*Main> take 20 (2:hurui [3,5..])
[2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71]
------------------------------------------------------------------------

などと実行することにより、20個の素数を列挙することができました。

どうです? 面白いでしょう?

関数型言語を知っている人は、たぶん Haskell もすぐ使いこなすことができる
と思いますし、関数型言語を知らない人は、ぜひこの機会に知ることをオススメ
します。なぜなら関数型言語を知らないプログラマってプログラミング言語の世
界の半分しか知らないわけで、Haskell を学ぶことにより世界が大きく広がると
思うからです。

関数型言語を初めて学ぶ人は、まずは

  入門Haskell―はじめて学ぶ関数型言語
  向井 淳 (著)

あたりを読むのがいいかも知れません。私の机の上に置いておくので、興味ある
かたはどーぞ (先着一名様限定)。

この本は、副題にもあるように関数型言語を初めて学ぶ人向けに書かれているの
で、イマイチ本質を外しているんですよねぇ... だから本音ではオススメな本で
はない ;-) のですが、関数型言語へのとっかかりとしてはよいのかも知れません。

#13425                                                          仙石 浩明
https://www.gcd.org/sengoku/             Hiroaki Sengoku <sengoku@gcd.org>
~~ tech ML に投げたメールここまで ~~

このメールに一番早く反応したのは、KLab の開発に参加いただいている 協力会社 H 社の CTO の T さんでした。 T さんとはご無沙汰していたのですが、 彼も私と同様に Haskell で遊んでいたことが分かって、 さすがと思った次第。

# 協力会社さんに負けずに頑張ってね > KLab 社員

~~ T さんのメール(一部抜粋)ここから ~~
ご無沙汰しております。
H 社の T です。

Hiroaki Sengoku wrote:
> 唐突ですが、Haskell って知ってますか?

なぜか私も、今月の頭ぐらいにとあるブログで知って(YAPC::Asia 2006のPugs関連
のエントリだったような…)

>   入門Haskell―はじめて学ぶ関数型言語
>   向井 淳 (著)

を購入して、ひそやかに楽しんでおりました。
そして、いやあ、これは、自分だけ楽しんでいるには余りにももったいないので、
(H社内の)勉強会のネタにしようと宣言していた矢先に、投稿を拝見しまして、
つい反応してしまいました。
~~ T さんのメール(一部抜粋)ここまで ~~

KLab でも Haskell 勉強会やりましょう!

Filed under: プログラミングと開発環境 — hiroaki_sengoku @ 06:32
2006年4月27日

新卒採用 (2)

先日紹介した学生さんを対象とした会社説明会

KLab(株) CTO 仙石 (KLabセキュリティ(株) CTO を兼務) と語る、
「技術者の成長にとって一番役に立つ会社」
「技術者が自ら伸びていくことができる会社」とは?

日時: 5/9(火) 13:30~15:00
場所: KLab(株) 本社会議室 (六本木ヒルズ森タワー 20F)

が、おかげさまで満席につき二回目が実施されるそうです。

日時: 5/15(月) 13:30~15:00
場所: KLab(株) 本社会議室 (六本木ヒルズ森タワー 20F)

会社説明会といいつつ、要は私といろんな話題についてお話しましょう、
というノリですので、私の日記 (CTO日記もよろしく) を見て、
波長が合いそうだと思ったかた、ぜひ登録をお願いします。

5/8 追記: 上記説明会は満席につき、現在は 5/30実施の説明会への登録を受付中です

Filed under: 技術者の成長 — hiroaki_sengoku @ 15:40
2006年4月27日

面接FAQ: 前職でいくらもらっていましたか? hatena_b

弊社の面接で(私に)よく聞かれること、 面接官自身が語る面接攻略法の三回目。

多くの企業同様、KLab の面接でも「前職でいくらもらっていたか」は 当然のように質問します。 が、それは前職給に基づいて給与を決めるため、というわけでは決してなく、 KLab の給与体系にスムーズに移行できるか判断するためです。

世間一般的には、まだまだ年功序列の給与体系のようで、 年齢が高くなるとどうしても給料が高くなる傾向にあります。 例えば先日面接した人は、 5年前から成長が止まっているように見受けられました。 5年間さしたる進歩もないのに、 給料のみが上がってしまっている、 いまさら 5年前の給料で雇うのも無理な話ですし、 5年間成長が止まっていた人が転職で突然伸び始める可能性は 低いと判断せざるを得なかったので 不採用となりました。

もちろん「成長が止まっていた」というのは私から見た主観であって、 当人からすれば 5年間の間にいろいろ経験を積んできたと思っているのでしょう。 実際、職務経歴書にはいろいろなプロジェクトが列挙してありました。 しかし、どのプロジェクトについても別段思い入れがあったわけでもないようで、 「印象深かったプロジェクトを、一つだけでいいので事細かに詳しく説明して下さい」と お願いしても、どういう製品を開発したのか説明するだけだったのです。 私としては、どう言う点を困難と感じ、どのように工夫し、何を学んだか等々を 聞きたかったのですが、 ついにそういう話は聞けずじまいでした。

給与の経路依存性と二極化」から引用:

キャリアって「何をやったことがあるか」もあるけど 「前職でいくらもらっていたか」というのも大きいんだよね. 誰も絶対的な給与水準の感覚なんてないから, どうしても「前職でいくらもらってましたか?」というのが重要な基準となる.

そうでしょうか? KLab には KLab の「絶対的な給与水準の感覚」があります。 もちろん面接の短い時間で、 どのくらいの能力を持っている人か正確に把握することは困難ですが、 実際に 3ヶ月~半年も一緒に働けば、 同じ職種の誰より上で誰より下の実力、 なんてのは誰の目にも明らかになるものだと思います。 だから KLab で前職給を聞くのは、 給与を決める際の基準にしようというわけでは決してなく、 KLab の給与体系にソフトランディングさせることが可能か判断するためです。

同ページから再度引用:

人事がまともなら移行期間は前職の給与だけど, だんだんと企業内部の給与水準に収束させる訳だけど, 誰かの給与を下げるためには結構まじめに説明可能な管理をする必要があって, なかなか大変だったりする. そうやって経路依存的な問題で所得が二極化しているのに, それを実力主義だとか抜かしたら大きな間違いだ.

給与を下げるのは実際大変です。しかし、 実力主義を標榜する以上、 成長にともなって給料が大幅に上がる人が出てくる一方で、 変化に適応できずに給料が下がる人が出てくることは避けられないことです。 「結構まじめに」どころか大変な労力をかけて話し合い、 お互い納得した上で降格を実施しています。

確かに面接では把握し切れなかった能力の低さが入社後に発覚し、 実力と給与のミスマッチが生じるケースも無いわけではありませんが、 不幸にもそういうケースが起きてしまったときは誠心誠意全力で対応し 是正するよう努めています。 お互い納得しあうまで何時間も話し込むこともあるわけですが、 それは実力主義を維持するために当然払うべきコストだと思っています。

Filed under: 技術と経営 — hiroaki_sengoku @ 07:41
2006年4月26日

技術者の上司は技術者であるべき

CTO日記に書いた、 「実力主義・能力主義」が (思いの外 ;) 多くの方々に読んで頂けているようだ。 私がこだわっていることから 3つ紹介しているのであるが、 ほとんどのかたが、「技術者の上司は技術者であるべきです」に 注目しているのが興味深い。 勤務時間の10%以内であれば上司の許可を得ずに何をやってもいいという「どぶろく制度」 よりも上司が技術者であることのほうが関心が高い、というのは 現在の上司が理解がないと感じている人が多い、 ということを反映しているのだろう。

上司に自分の能力を正しく評価して欲しい、と 思うのは技術者として自然な感覚だとは思うのだが、 注意すべき点が二つほどあるように思う。

一つめは、視野狭窄に陥らないという点。
もう一つは、 現実問題としては、上司が技術者でない人が存在する、という点。

まず一つめの点であるが、 「上司に自分の能力を正しく評価して欲しい」と思うとき、 上司と自分との関係しか見ておらず、 しかもそれは自分からの視点のみであって、 相手がどう思っているかという視点が欠けているのではないか? 会社という運命共同体の中で自分がどのような位置にあり、 自分としてはどのような役割を果たすのか明確になっているだろうか? また、 上司はどのような考えで自分を評価しているのか、 その評価にはどのくらい妥当性があるのだろうか? そういう視点からみたとき、 見え方が変わってこないか自省すべきだろう。

もう一つの点、上司が技術者でない人の存在について考えてみる。 まっさきに CTO が思い浮かぶ。 確かに CTO の上司は社長であり、多くの会社で社長は技術者ではない。 しかし、よほど小さい会社でもない限り、 CTO 以外にも上司が技術者でない人は沢山いる。 そのような人達は、技術者以外の視点も持って、 上司や職位が同じレベルの他部門の人達と調整を行なわなければならない。 そういう調整能力と、部下を正しく評価し育成できる能力と、 両方兼ね備えた人材が豊富にいるのであれば問題無いが、 一般的にはかなり困難だろう。

だから、そういうポジションに技術者を登用する場合に、 調整能力を重視するのか、部下の育成能力を重視するのか、 考えなければならない。 この育成能力というのは、技術が分かっていることとはまた 別の次元だったりするので、さらに話は難しい。

Filed under: 技術と経営 — hiroaki_sengoku @ 19:39
« Newer PostsOlder Posts »