isyumi_netブログ

isyumi_netがプログラミングのこととかを書くブログ

コード書く工程は末端作業か?

お断り
  1. 今起きている何らかの喧嘩は関係ありません。未来に向けて書き残しておこうと思ったから書いています。
  2. 「そうやって現場を大事にしないから日本はだめになったんだ」的な話も、今は関係ありません。それはそれ、これはこれ。あくまでソフトウェア産業の話をします。

 

まず、僕の現状認識を説明する。これは自分が体験したことではない。また、統計など信用のおける証拠があるわけでもない。あくまで、伝聞で得たイメージである。

ソフトウェア産業では10年以上前からプログラミングが儲からない仕事になっていくといわれていた。商品開発とアフターサービスの付加価値が高まり、逆にプログラミングの付加価値が下がるであろう。コーディングはどんどん発展途上国に外注されていく。だから日本人は生き残るために別の道を選ぶべきだ。いまプログラマーとして働いている人は設計や管理職などのキャリアへ進むべきだ。というのが定説だった。

しかし、その変化は起こらなかった。プログラミングの価値はますます増大し、プログラマーは稼げる仕事になっていった。多くの先進的な企業が差別化のために能力の高いプログラマーを高報酬で雇い始めた。

ところが、日本企業の多くがその変化に気づいていない。個人の感覚としてもプログラミングを軽視する感覚が根強い。また、企業の採用・人事制度でもプログラマーは立場が低く扱われている。

というのが僕の認識だ。確かめたわけではないがそこそこ正しいだろう。

 

なぜ、このようなことが起こったのか。この先は完全にただの憶測だ。

まず、プログラミングが儲からなくなっていくと思われていたのはなぜか。それは製造業の影響だろう。製造業にはスマイルカーブという概念がある。企画→設計→製造→流通→マーケティング→アフターサービスという各工程の付加価値はお盆のような形をしており、両端の企画とアフターサービスが一番高く、次いで設計とマーケティングが高く、製造と流通は最も低い。この考え方をソフトウェアに当てはめるとプログラミングは製造工程に当たる気がしてしまうのだろう。ここから得られる教訓は製造業とソフトウェア産業は全然別物であるということだ。少しフォローするなら、これも伝聞だが、プログラム言語がアセンブラCOBOLなどしかなかった時代のソフトウェア産業人海戦術の域を出ておらず、製造業と大差なかったのだろう。そもそもソフトウェア産業などという概念自体がなく、それは製造業の一種と考えられていたのなら、そういう勘違いが発生するのも無理はない。

また、なぜプログラミングの価値が落ちなかったのか。これは大変興味深い謎である。僕がTLで見たのはこんな意見だ。

  1. 優秀な人とそうでない人の差が大きすぎるから
  2. 設計とコーディングが別工程だと思われていたが、実は不可分だったから
  3. ソフトウェア開発が高度化しているから
  4. プログラミング可能なものが増えたから
  5. プログラマー文化の本流はヒッピー文化でもあり、反権威的なプログラマーたちがそういう流れを作ったから
  6. シリコンバレーを中心とする経済圏が過熱しすぎていて相場を押し上げているから
  7. プログラミングは人間の限界を拡張する手段であり(エンハンスメントとかオーギュメントとか難しい言葉を使ってた)プログラミングを覚えた人間は一段上のステージに行くから

どれもあり得そうな話だ。僕は4を推しておく。

最後に、なぜ日本人はその変化に気づけなかったのか。この疑問を考えるには、そもそもアメリカでも気づかれていない可能性を考えなければいけない。今をときめく映画評論家町山智浩さんによればアメリカ人の半分はニューヨークの場所を知らないらしいので、Googleプログラマーが高給であることを7割くらいの人が知らない可能性は高い。であれば、日本だけが遅れているという前提すら危うい。まあしかし、いったんそこは置いておいて考える。まず企業の中では、多くの日本企業の昇進制度上、会社を動かしているのは10年以上前に活躍していた人が多いはずだ。つまり、そういう考え方でうまく回っていた時代の人だ。そうであればプログラミングの重要さを前提とした経営判断がし辛いのは想像できる。個人の問題としては、各国の経済成長の動向が大きいと思う。日本はこの数十年の間に途上国に次々と追い越された。日本が追いかける側の国であればアメリカでプログラミングがもうかっているらしいという情報が入った瞬間にみんながまねしたであろう。しかし、一度製造業で世界を制した成功体験があるだけになかなかそこから抜け出せない考えの人も多いと思う。

 

では、今僕たちはどうするべきか。お断りにも書いたが「現場を大事にするべきだ」みたいなわかりやすいスローガンに回収しないほうがいいと思う。もちろん、それはそうなのだが、残念ながら付加価値の多寡というのは必ずある。職業に貴賎はないが給料は違う。個人戦略としてどこが儲かるかを判断する必要はあるし、企業としてもどこで差別化するかを見極めて、誰がやっても同じな業務は安く抑え、勝因になりえる業務に強い人を雇うのは当然のことだと思う。少なくとも僕らはプログラマーとして能力を伸ばすべきだ。管理職やマーケ職の方が付加価値が高くなる可能性よりもプログラマーの付加価値が伸び続けるほうに張りつつ、しかし、それが逆転する兆候があればすぐに身を翻す機敏さも持ち合わせなければいけない。

ポリテックと型システム

金融とテクノロジーフィンテックで行政とテクノロジーがゴブテックなら、政治とテクノロジーの融合はポリテックだそうだ。

政治をよくできそうな技術として、どうも政治家さんにはAIがモテモテのようだが、型システムの方が味がいい気がする。

例えばこの意見を見てどう思うか?

最近少年犯罪が増えているので、若者は全員自衛隊に入って根性を鍛えるべきだ。

そういう考え方の是非はともかく、前提知識として少年犯罪自体は減っている。だから、最初からこういう意見は取り合わなくていい。

また、これはどうだろうか?

 自民党に投票しなかったということは民主党を支持するってことだな?

 これは必要条件と十分条件の区別がついていない人だ。

黙秘するということは犯行を認めるということですね?

これは三値論理と言う。三値論理的にこの主張は間違っている。

 

型システムを使えばこういう無駄な議論に時間を使わなくてよくなる。「わかりきった事実誤認に基づいた意見」「真偽や善悪以前に理屈が通っていない理屈」をあらかじめ排除できる。

どこか一箇所に型定義リポジトリを作りそこに自明の知識をプールしておけばいい。

「少年犯罪は減っている」「東京は出生率が低い」「女子高生が産婦人科に行く理由」「日大と日本体育大は別」など、間違えそうなことは事前に登録しておいてあげれば無知を晒して恥ずかしい思いをする人も減るだろう。

思い切って

  • 政治家は国会で型チェックを通らない話をしてはならない
  • 国民は政治家に型チェックを通らない要望をあげてはならない

というルールにしておけばいい。

 

逆に誰でも簡単に意見が言えてよくなるという効果もあると思う。

例えば、映画は犯罪率を低下させるのではないかと思っている人がいたとしよう。しかし、実際にそうなのか調査結果がどこにもなかったとする。こう意見を言ったとする。

映画には犯罪率を低下させる効果がある。経済発展のために映画を見るべきだ。

映画を見るべきだというのはその通りだと思うが、話の前後に繋がりがない。これでは相手にしてもらえない。

そこで

映画には犯罪率を低下させる効果がある。犯罪率の低下のために映画を見るべきだ。

とか

映画には犯罪率を低下させる効果がある。犯罪率の低下は経済発展に効果がある。経済発展のために映画を見るべきだ。

などであれば事実関係はともかく理屈は通っている。「じゃあ映画に本当に犯罪率低下の効果があるか、犯罪率低下に経済発展の効果があるか調べるから予算くれ」と話を繋げられる。このように事前に理屈を整頓する機会を与えられることによって、意見を言いやすくする効果があるのではないか。

 

 

 

ブラウザに自動ドネーションAPI機能がほしい

以前からコンテンツ提供者にもっとお金が支払われるべきだと思っていた。

こういう仕組みがほしい。

  • 僕がChromeに「毎月5,000円をコンテンツ提供者に支払う」と設定する
  • Chromeは俺の行動履歴からどのサイトにどのくらい滞在しているかを集計する
  • 月締めで滞在時間に応じて自動的に5,000円を各サイトに分配する

この方法が必要な理由はこうだ。

  1. たとえサイト運営者がPaypalなどでドネーションの申し込み口を設けていたとしても、いちいちそれをクリックするのがめんどくさい
  2. ある程度お金を払っていることを忘れられるUXでないともったいないと感じてしまう(i-Modeの論理)

 

人生の5箇年計画

やりたいことがいっぱいありすぎるので一覧を作る。

5箇年計画と書いたが、実際には何年かかるか全く見当がつかない。

 

  1. 数学検定準一級
  2. TOEIC800点
  3. レッドコーダーになる
  4. Webフロントエンドのベストプラクティスホスティングサービスを作る
  5. Dartのサーバーサイドフレームワークを作る
  6. ビジネスロジックからDBのクエリを自動生成するツールを作る
  7. ALT SQLを作る
  8. 数学検定一級
  9. Google Cloud Certified を取る
  10. 応用情報技術者をとる
  11. データベーススペシャリストをとる
  12. ネットワークスペシャリストをとる
  13. セキュリティスペシャリストをとる
  14. LPIC Level3になる
  15. この辺で年収1000万になる
  16. CPUを作る
  17. OSを作る
  18. 言語を作る
  19. RDBの理論面を勉強する
  20. 型システムの理論面を勉強する
  21. 素晴らしい数式処理システムを作る
  22. 暗号論を勉強する
  23. 新しいWebの仕様を策定しバーナーズリーの後継者になる
  24. 絵を描けるようになる
  25. 何か楽器ができるようになる

30才までに終わるかな。

外部からSSH接続を受け付けるプロダクトのSSH設定

今、デベロッパー向けにこういうサービスあったらいいなーと思っているものがある。

その中でSSH接続を受け付けたいなと思ったから実現方法を考えた。

WordPressホスティングしてvimで記事を編集できるようなものをイメージして欲しい。

 

ルール

・1人1Docker コンテナ

・コンテナを実行するEC2インスタンスを運営側がいつでも変更できるようにする

・サーバーの状態の保持は別問題

・HTTPも別問題

・ユーザー名は全ユーザーで1意、ホストは共通

 

サインアップしたユーザーにDockerコンテナを用意し、そこにSSH接続を受け付けるようなものを作りたい。

 

せっかくなので、1Dockerに複数のユーザーを用意できるようにしたかった。それができると「管理者」「編集者」「記者」等のロールを作れる。しかし、難易度が上がりそうなので今回は諦め。

 

コンテナを実行するEC2インスタンスを運営側がいつでも変更できるようにしたい。

特定のホスト名、ポート番号で待ち受け続けないといけないと運用がきつくなる。

例えばもっと安いEC2インスタンスが発表されたらこっそりコンテナを移動したい。

また、使われてないコンテナを勝手に落としておき、次にアクセスが来たときに空いているインスタンスで立ち上げる仕組みも入れられるようにしたい。

そのため、生EC2アドレスをユーザーに渡すのではなく、SSHプロキシのアドレスを渡してそこからEC2にブリッジする。

 

インスタンスを立ち上げなおしたときにユーザーが変更したファイルの保持をどうするかは、ネットワーク設計とは別問題なので今日は気にしない。それはアプリケーション設計の話だ。

大方 /var/hoge/fuga 以外を変更しても保存は保障しませんよといっておけばいいだろう。

 

HTTPでも受け入れられるようにしたいが、そっちはそっちで話がでかすぎるので別で考える。

 

ユーザー名は全ユーザーで一意にする。

さっきも書いたが本当はホストごとに名前空間を区切って

- admin@user1.some-service.com

- author@user1.some-service.com

- editor@user1.some-service.com

- junior-staff@user1.some-service.com

という風にロールわけをできるようにしたい。

しかし、後述のライブラリがまだHostに対応していないので諦めた。

 

とりあえず

- user1@some-service.com

- user2@some-service.com

というユーザー名体系にする。

 

実現方法

これはまだ試していなくて、こうすれば大丈夫だろうと僕が今想像していることである。

 

用意するもの

・ELB

・セキュリティゾーン

・EC2を3台以上

 ・内2台はSSHプロキシ

 ・もう一台はDockerのせるインスタンス(アプリケーションインスタンスと呼ぶ)

  ユーザーが増えてきたらこのインスタンスを増やしていけば良い

・DockerImage

 

まず、ELBから2台のSSHプロキシサーバーに大してTCPロードバランスを設定

外向きのホスト名はssh.some-service.comで統一する。

 

SSHプロキシサーバー内で下記SSHプロキシツールを起動

https://github.com/tg123/sshpiper

 

アプリケーションインスタンスでDockerを起動

このとき、内向きのSSHポートは22とする。

外向きは可変にする。

docker -p 3333:22 等

 

これで準備は完了。

新しくユーザーがサインアップしたらどこかのアプリケーションインスタンスでDockerを起動する。

鍵ペアを生成し、Docker内に渡すと同時にSSHプロキシにそのインスタンスのホスト名、ポート名、鍵を設置。

ユーザーに秘密鍵を渡す。

 

これでできるんじゃないかな?

ビジネスロジックのコードからDBのクエリを自動生成することは可能か?

最近取り組もうと思っていること

ビジネスロジックのコードを解析することでこのようなことができるのではないか?

  • このコードを実行するために必要なDB上のデータの範囲を特定する
  • そこからDBへのクエリを自動生成できるのではないか

という疑問だ。

 

前提とするコード

ビジネスロジックの前段階で必要なデータを落としてくる

僕はビジネスロジック内でDBにアクセスするのではなく、その前にデータを落としてきておいて、そのデータを引数に取る事にしている。

 // こうではない

func getUserSalesSum(req Request, db DB) Response {
sales := db.QuerySalesByUser(req.UserID)
return sum(sales)
}
// こう
func onRequest(req Request, db DB) Response {
sales := db.QuerySalesByUser(req.UserID)
return getUserSalesSum(req, sales)
}
func getUserSalesSum(req Request, sales []Sale) Response {
return sum(sales)
}

 これはgetUserSalesSumがスローテストにならないようにするためだ。

また、DBへのクエリが正しく書けたかということとこのビジネスロジックが正しく書けたかということは本来別問題なのに、同時にテストしなければいけなくなる。だからビジネスロジックに入る前に必要なデータは全部落としておくべきだ。

 

ビジネスロジックでも同じフィルターを書く

前述のこのコードは悪いコードの例である。よく「バッドコードの例」として紹介される。

func getUserSalesSum(req Request, sales []Sale) Response {
return sum(sales)
}

暗黙の前提が存在することについてだ。

このコードでsalesという値はあるユーザの購買履歴だけを含む配列ということになっている。しかし、このコードにそのメッセージ性がない。このコードを読んだ人は普通「このsalesはすべてのsaleが入った配列だな」と感じると思う。

そこで、いくつかの手を打つ。例えば引数名や関数名を工夫することがある。また、Assertを書くというのもただしい選択の一つだろう。しかし、僕はビジネスロジックにもフィルターを書くのがいいと思う。

func getUserSalesSum(req Request, sales []Sale) Response {
sales = filterUserSales(sales, req.UserID)
return sum(sales)
}

 

こうしたほうがビジネスロジックの1行1行が仕様と1:1で対応するようになるのでわかりやすい。

また、前者はこの配列が特定の条件でフィルターされていることに依存していたコードだが後者は必要なデータが入っていればそれ以外のデータが入っていても正常に動くコードになる。後者のほうがキャッシュなどとの相性が良くなる。

またクエリを書くことを手抜く余地が出る。例えば国コード一覧というのがあったとする。それは高々200個くらいの値しかないはずだ。そうであれば、毎回DBに取りに行くより手元のインメモリに持って置きたいかもしれない。そういう時に特別なコードを書かなくてよくなる。

DB上のすべてのテーブルを一つのオブジェクトツリーにまとめる

このコードの問題点の一つとして使うデータの種類が増えれば引数が多くなってしまう点がある。ユーザー情報や購買情報や位置データやあれこれ……

そこで、DBのテーブルを全部一つのオブジェクトツリーに含めてしまうといいと思う。

type Table struct {
Users []User
Products []Product
Sales []Sale
}

func getUserSalesSum(req Request, table Table) Response {
sales := filterUserSales(table.Sales, req.UserID)
return sum(sales)
}

さて、「ビジネスロジックの前段階で必要なデータを落としてくる」でビジネスロジックが参照透過性を手に入れた。そして、「ビジネスロジックでも同じフィルターを書く」でテーブルが特定の条件に依存するコードから。さらに一つのオブジェクトツリーにまとめることによって、テーブル全体の関数に変わった。

HTTPリクエストハンドラとは

ここで一つの結論に到達した。

HTTPハンドラとは、HTTPリクエストとDB上のすべてのデータに対してHTTPレスポンスを返す純粋関数である。

POSTメソッドなどのこともあるので、HTTPレスポンスとDB書き込みコマンドを返す関数であるということにしておく。

自動生成

 

おさらいだが、このビジネスロジックは引数でDB上のすべてのデータを受け取った前提で書いてある。しかし、実際に必要なのはその一部分である。その部分は必ず入っていなければいけないが、それ以外の部分にデータが入っていても入っていなくても動作は変わらない。逆に混ざっているとまずいデータというのはありえない。

たとえば上記のこのコードの場合だ。

type Table struct {
Users []User
Products []Product
Sales []Sale
}

func getUserSalesSum(req Request, table Table) Response {
sales := filterUserSales(table.Sales, req.UserID)
return sum(sales)
}

func filterUser(sales []Sale, id interface{}) []Sale {
var results []Sale
for _, s := range sales {
if s.UserID == id {
results = append(results, s)
}
}
return results
}

このコードを見れば誰でもUsersとProductsにデータが入っていても入っていなくても同じ動きをすることはわかる。

また、Salesにreq.UserIDのすべてのSalesが入っていなければならないが、UserBさんやUserCさんのデータはあってもなくても同じということもわかる。

僕の問題意識は、「人間がこのコードを読めば必要なデータと不要なデータを判定できるならコンピューターに計算させることができないか?」である。

UsersとProductsが不要であるということはASTを見ていけば結構簡単にできそうだ。しかし、req.UserIDのSalesだけが必要だ、ということはどうすれば判定できるのだろうか。

今のところ、すべての「値」に対して「その値を作るのに必要な値」を挙げていき、それをツリー上にすればある程度可能かと思ったが自信がない。

 

誰か教えてくれ。