非Web系プログラマから見た今後のWeb開発

初めに言っておくが、あまりまとまって無いです。思ったことをつらつらと書いてみます。

私は元々、Javaを好んで使っていた。それは私が大学生の時のことだったので、2006年か、そのあたりだったと思う。その当時メジャーだった言語の中では、Javaは綺麗にオブジェクト指向設計ができる言語だった気がする。標準APIも充実しており、たいていのことは標準APIの範囲で事足りた。

就職してからC#を使い、ちょっと戸惑うところがあった。あまりにも高機能すぎるて、オブジェクト指向設計が破たんしてしまうのではないかと思ったのだった。たしか、J++→J#→C#の開発の流れで、MicrosoftはP/Invokeやデリゲートの導入を進めたがそれは契約と反するとしてSunと対立し、裁判に負けたのでJava関連の機能を省き、C#を独自実装したという流れがあったように記憶している。

その流れの中で、P/Invokeはともかく、「デリゲートはオブジェクト指向言語に不要」という意見があった。また、Java 7 SE開発の際にも、「Javaにクロージャは要るかどうか」でずいぶんと議論になった。

この当時の興味深い資料として、2008年にjava.netで行われたWhich Java 7 closure proposal do you support?というアンケートがあります。このアンケートには約2,000人が答えており、BGGAとクロージャは必要ないという意見に二分されていることが分かります。

詳解 Java SE 8 第2回 ラムダ式 - IT pro

「オブジェクト指向言語にデリゲートやクロージャやラムダ式は要らない」まあ、これは私もある程度同意する。ピュアなオブジェクト指向設計を考えると不要であるし、オブジェクト指向でないような実装方法もできてしまう。これはよくない。と、私は思っていた。それがC#を使った時に初めて感じた違和感だった。

しかしながら結局、Java 8 SEになってラムダ式をサポートした。メニーコアなCPUを使い切るには並列度を高める必要があるし、並列度を高めるためには並列処理の粒度を小さくして、かつ、書きやすくしなければならない。そのためにラムダ式は必須と言ってもいい。Javaはサーバでもよく動いているのだから、Javaでそういう流れが起きたのは当然と言っていい。

ただしこれは遅きに失していると思う。Javaは保守的すぎる。私はそう思うようになった。それはC#の新しい言語機能が思った以上に有用だとわかったからである。C#のラムダ式とLINQに触れたとき、私が長年必要としていたものはこれだったんだと分かった。Pythonでリスト内包表記を覚えたとき、これはC#だけでなくて近代的なプログラミング言語の潮流なんだな、と理解した。だから、いまだに演算子オーバーロードすら実装してくれないJavaに嫌気が差した。Java 8 SEリリースでラムダ式がサポートされてStreamでmap/filterできますよ、なんて言われても何を今さら、と思った。私は今後、Javaを進んで使うことはないだろうと思う。

我々は優れたオブジェクト指向設計をしたいのではなく、優れたシステムを簡潔に実装するためのツールとしてオブジェクト指向設計をするのである。オブジェクト指向に代わるより良い方法があるならば、それを柔軟に利用すべきである。

初めてStruts/JSP/Seaser2に触ったとき

ここからが今回の記事の本題。で、そういう、C#やPythonが好きな私がいきなりWeb開発をしなければならなくなり、仕事で初めてSeaser2に触って、あまりの古臭さにゲロ吐きそうになった。まあ、2014年に入ってから触ったので当たり前なのかもしれないけど。

まず、JavaBeansという仕組みに吐き気を催した。

巷に流布するJavaBeansの仕様は以下のようなものですね。
・引き数なしのコンストラクタを持つ
・プロパティを持つ(setter/getter)
メソッドやイベントを持てたり、シリアライズ可能にできるとかもあるんだけど、重要なのは上記2点だと考えています。

JavaBeansって一体何が嬉しいの?

C#にはこういうことをするためにプロパティという仕組みがある。実行時型情報を持っていて、リフレクションでプロパティを自由に操作できる。明確でわかり易い。いちいちgetter/setterを定義するのは面倒くさい。

初めてC#を使ったときはプロパティなどという、たかがgetter/setterを置き換えるだけの目的で言語機能を拡張してしまうC#の思想のほうが嫌だったが、しかし使ってみるととても表記が簡潔になる。だらだらとsetter/getterだけで何行も書く必要はなくなる。言語機能としてサポートするだけの価値はある。

次に嫌気がさしたのが、Seaser2のS2Daoだった。RDBMSにクエリを投げるために、引数をEntityと呼ばれるPOJOオブジェクトで指定してやる。それは直観かつ簡潔で良い仕組みだと思う。けれども、クエリを生成するところは結局SQLを書かねばならず、表記方法も特殊だ。

SELECT * FROM emp WHERE empno = /*empno*/

このようにコメントでプロパティ名を指定してあげることで渡したEntityの内容を自動設定する仕組みになっている。もしempnoが空ならばWHERE節ごと削除されて実行される、などという機能もある。

C#だったらEntity Frameworkがあった。EFを使えばラムダ式でクエリを書いてPOCOでデータを取得できる。書いたラムダ式はうまいこと式木に変換され、SQLに変換され、必要になった時に遅延実行される。EFは実行速度が出ないという弱点はあったけれども、方向性は間違っていないと思う。実装は改良していけるが、一度決めた設計思想はなかなか変更できない。大幅に設計思想を変更したら、それは別のフレームワークだ。

それを考慮すると、私はS2Daoの設計思想がまず間違っていると思う。SQLに簡単にパラメータを流し込めるのではなく、簡単にクエリを書けるという方向に落とし込むべきであった。しかしそれはSeasarが悪いのではなく、ラムダ式の無いJavaで実装しなければならないという背景と、制作時期を考えれば仕方ないことであると思う。

余談だが、Querydslは素晴らしいインタフェースを持つ。いわゆる「流れるようなインタフェース」で直観的にクエリを記述でき、POJOで結果を受け取れる。ラムダ式の無いJavaでよくもまあここまで頑張ったものだと思う。

初めてJSFに触ったとき

「次世代のエンタープライズ向けWebフレームワークはJSFにGrassfishだ」と某社のそれなりに偉い人が言っていた。あはーん。そうですか。どーせ肥大化した巨大なフレームワークで使い勝手悪いんだろ。と思っていた。

実際使ってみると、思った通り肥大化した巨大なフレームワークで、ローカルPCでGrassfishを起動するのも大変な有様だった。HTMLやAjaxの面倒臭い部分を完全に隠ぺいしていて、ページの部分更新なんかがとても簡単に書けるのだけど、吐き出されたHTMLは難解で意味不明だった。Faceletsも覚えることが多く、たとえばcomposite componentもXML名前空間の書き方に「こういうディレクトリ構成の時はこういうネームスペースにすること」みたいな規則が覚えにくくてどうも違和感を感じる。

しかしながら、XHTMLに独自タグを組み合わせて画面設計するという手法自体は間違ってない気がする。composite componentも前述のような違和感があるものの、再利用可能な画面部品を製作できるという点が優れていると思う。

が、よくわからないけど、最近のWebフレームワークであればみんなできて当然な機能である気がする。

初めてnode.jsに触ったとき

初めてnode.js + Expressフレームワークに触れたとき、これは楽ち~んだな。と思った。Scoket.ioを使えばWebSocketでリアルタイムに更新するようなページを数行で実装できてしまう。サーバもクライアントもJavaScriptだし、ライブラリ/フレームワークによるサポートも強力なので、サーバ/クライアントの違いを意識することすらあまりないと言っても過言ではない。

しかしながら、やはり動的型付け言語というのが、メリットでありデメリットでもあると思う。動的型付け言語は、大規模なアプリケーションを作るのには不適である。開発が大規模になり、メンバが増え、影響するシステム要素、コード、モジュールが増えると動的型付け言語ですべてのプログラムを実装するのはどんどん難しくなる。

それは、コンパイル時の型チェック、IDEによるコード補完などの機能を使うことが不可能だからである。こういうのは動的型付け言語である限りはどうしようもない。

まだJavaScriptに触れて日が浅かったある日、私は他人が作ったJavaScriptライブラリ中にある関数から値をもらい、データを加工して表示するコードを書いていた。しかし、どうやってもプログラムが動作しない。与える引数がおかしいのかと試行錯誤していたが、そしてようやく、私が要求していた関数getResponseData()は実際には存在せず、正しい関数の名前はgetReponseData()だったとわかった。つまり、「他人が作ったJavascriptライブラリ」に含まれる関数名にはスペルミスがあったのだ。たったこれだけのことを調べるのにとても苦労した私はがっくりとうなだれた。静的型付け言語であれば、書いた瞬間にIDEが警告を出してコンパイルする以前に誤りであるとわかるケースである。

少なくとも巨大なソフトウェア作成では、動的型付言語は使い物にならない。 railsが流行ったのは、単に「簡単に、短い行数で、開発者が一人であっても」所望のプログラム作成ができたからであって、 おそらくは、たかだか一万行程度が限界である。 これらのスクリプト言語は、この程度の「さっと書いて、使った後は捨ててしまう」といった用途にのみ向いている。 これを超える、複数の人間で作成するような複雑なシステムでは動的言語は明らかに不適当である。

Ruby対Scala - クリプトメディア技術メモ

上記のようなことを書いているページがあったが、全面的に同意する。この意見はきっと多くの人にとって支持されるであろうと思うのだがどうだろうか。

ちなみに、勘違いしてほしくないのは「静的型付言語は動的型付言語よりも優れている」と言いたいのではない。単に、得意とするシーンが異なるだけである。しかし、「さっと書いて、使った後は捨ててしまう」といった用途に静的型付言語のみを適用することの困難さよりも、巨大なシステムを作るのに動的型付言語のみを使う困難さのほうが圧倒的に大きいとまでは言えると思う。

次世代のWeb開発環境全般に求める機能

以上のようなことを思いつくままにぱらぱらと書いてみた。これらを踏まえて考えると、次世代のWeb開発環境には以下のような特性を持った言語/フレームワークが存在することが望ましいと考える。

・マルチパラダイム言語を利用できること
・クライアントサイドでも静的型付け言語を選択できること
・高度なO/Rマッパ、もしくはオブジェクト指向DBが利用できること
・環境の構築とデプロイが簡単であること
・コードを一切書かずに開発できる環境があること

以下、一点ずつ述べる。

マルチパラダイム言語を利用できること

先に述べたように、システムを実装する(=現実世界の問題をうまく計算機を扱う)ためには、オブジェクト指向設計を行うことですべてが解決するわけではなく、もう少し別の要素を加える必要がある。たとえば、オブジェクト指向、関数型プログラミング、静的型付けと動的型付けの選択が可能であること、などが同時に利用できる言語が望ましいと思う。

私が触れたことのある言語で、これに近い言語はScala、Dart、C#(.NET 4.0以降)であると思う。

クライアントサイドでも静的型付け言語を選択できること

先に述べたとおり、大規模なシステム開発では静的型付け言語を利用できることが望ましい。最近ではクライアントサイドのパフォーマンスの向上がめざましい。スマートフォンのCPUの進化、JavaScriptエンジンの強化などがそれにあたる。したがって、クライアントサイド、つまり、ブラウザが担当する処理もそれなりに増えてきたし、これからも増えていくことが当然予想される。

その際に障壁となるのは、やはりJavaScriptしか選べない、つまり動的型付言語でしか開発できないという点ではないだろうか。jQueryを使って非同期に検索結果を取ってきて表示させる、程度であれば全く問題ないし、むしろ動的型付言語であるほうが簡潔に書けるだろう。しかし、ちょっと複雑なことをやらそうとすると、静的型付言語がほしくなってくる。

altjsと呼ばれる言語がいくつか提案されていて、そのなかのいくつかに静的型付言語と動的型付言語が選択できるような機能を持った言語(DartやTypeScript)がある。それはやはりそのような開発側からの要求があったからこそであろう。

高度なO/Rマッパ、もしくはオブジェクト指向DBが利用できること

私がソフトウェアの勉強を始めてから今に至るまで、ずっと疑問であったのがSQL文の存在である。SQL文は構造化されていない構文であり、お世辞にもわかり易いとは言えない記述方法である。ちょっと複雑なクエリを書こうとするとどんどんSQL文が長くなっていく。

そもそも80年代に定義されたフォーマットを、なぜ2010年代になっても後生大事に使っているのか全く理解できない。代替手段が用意されているならばまだわかるが、主要なRDBMSはまずSQL問い合わせを代替する方法がない。SQLで問い合わせをするという手続きがまず本質的でなくて本来は不要な作業であるはずだ。

我々がやりたかったことは、指定した条件にマッチするタプルの集合を取ってくる操作であるが、それを実現するためにSQLという言語はあまりにも不適であるとおもう。覚えるのが大変だし、覚えた後もSQLやRDBMSのチューニングなどといってさらに知識を要求させられる。我々がやりたかったことはデータベース屋の勉強ではなくして、単にデータがほしかっただけである。なぜわざわざプログラミング言語に加えてSQLを覚えなくてはならないのか。

さらに、RDBMS自体もこれが本当に使いやすいのか疑問である。まず「リレーショナルモデル」を標ぼうしておきながら、その実装方法があまりにもユーザー任せでチープだと思うのは私だけだろうか。たとえば1対多リレーションを表現するためには「片方の表から片方の表の主キーを外部参照する」という方法を用いるが、1対多のリレーションを表現したいだけなのにこんなテクニックじみた操作をしなければならないこと自体がおかしいと思う。多対多のリレーションなんかもっとひどくて、わざわざ中間テーブルを一つ作成しなければならないのである。リレーショナルモデルを標ぼうするくらいなら、そんな単純作業はシステム側で自動的に処理してユーザーから隠ぺいしろよ、と思う。

さらに輪をかけて腹立つのが、ここまでやってあげてるのに、いざ問い合わせの段になったら「TABLE_A INNER JOIN TABLE_B ON TABLE_A.ID = TABLE_B.ID」などとしょーもない、分かりきった文字列を書かなくてはならないのである。で、バカが外部結合と内部結合の違いも判らず、問い合わせの最中に何万というタプルが発生してしまうような爆発的なSQLを書いてパフォーマンス上の問題が発生したりするのである。

というか、そもそもこのIDというのも腹立つ表現であって、「それぞれのレコードで重複しない値をID列として用意しておく」みたいな決まりきったパターンの意味が分からない。世界中で何億回と繰り返し作られたID列を見てなぜシステム側がそれをサポートするということをしないのだろうか。私は初めてOracleを触ったとき、ID列を作るに当たりどうやって重複しない値を取ればいいんだっ、と思い、調べたところ「シーケンスを使え」などと書いてあって心底あきれた。んなもん実装する手間で、なんで自動的にIDを取って入れてあげる機能を実装できるだろっ。あほか。

それ以外にも、たとえば複合主キーを定義した時はデフォルトだと索引を作ってくれなくてフルスキャンになるので複合インデックスを作らなきゃいけないとか、そういう点にもうんざりする。んなもん、複合主キーを作った段階で複合インデックスを自動で作っとけよ。と、思う。

それらすべてを使う側が許容し、やっとこさDBを使う段になり、プログラム中のなかのオブジェクトを永続化したいな、と思うと、「リレーショナルモデルとオブジェクトモデルの間にはインピーダンスミスマッチがあるからねー」なんつって、いちいちデータ保存するためだけにO/Rマッパとか特殊なフレームワークを導入して使い方を覚えないといかん、もしくは、お世辞にも使いやすいとは言えないデータベースドライバの使い方とお作法を覚えないといかんのである。

私に言わせれば、データベース屋は実応用をわきまえず、椅子にふんぞり返って「僕らの考えた高尚なソフトは、君らにはちょっと難しいかもしれないね」と言っているに等しいと思う。memcachedやNoSQL関連の技術が登場して世の中を騒がせたことを思い出してほしい。単純で高速なキーバリューストアの仕組みなんて、もっと以前からRDBMS内にオマケで実装してあってしかるべきだと思う。DB屋はもっと謙虚な心を持つべきなんじゃないだろうか。

たとえばOracleのクライアントなんかがその性格をもっともよく表した代表例であると思う。環境変数にORACLE_HOMEを追加しなきゃ動かないとか、OSの流儀をわきまえず、WindowsであればCドライブの直下にアプリケーション本体を格納させるだとか、いたるところに勝手にログファイルを吐き出したり、実行プログラムのパスにカッコが入ると実行できなかったり、もう窓から放り投げたくなってくる。

ちょっとヒートアップして長くなってしまったが、言いたいことはただ一つ。プログラム側から扱いやすいデータベースシステムがあるべき、というただ一点である。たとえばオブジェクト指向データベースであったり、問い合わせと永続化の処理を抽象化した高度な機能を持つO/Rマッパがこれに当たる。

たとえば.NETフレームワークのEntity Framework + POCOは求めるものにかなり近い。以下のように直観的でわかり易いクエリを書ける。

var max = db.Person.Where(p=>p.Age > 30).Select(p => p.Salary).Max();

EFで唯一残念なのがパフォーマンスの問題だ。Entity Frameworkを使用して複雑なクエリを投げたり、リレーションがいくつか連なったデータを挿入したり更新したりするときに速度が出なくて結局ネイティブSQLを叩いたりする。複雑な処理をしているのでしょうがないとは思うが、今後の改良に期待する。

理想を追い求めるのであれば、O/Rマッパーではなく、オブジェクト指向DBを採用すべきだと思う。しかしオブジェクト指向DBはプロプラなものがほとんどだ。私が知る限り、オープンソースなものはdb4Objectsのみである。

Scala と ODBMS の合わせ技が効率的すぎる

上記にdb4ObjectsとScalaを組み合わせたサンプルコードが乗っているが、こういう風に書けるのが理想的な状態ではないだろうか。

db.query{ c:Customer => c.name.contains("o") }.foreach(println);

環境の構築とデプロイが簡単であること

作業環境の構築にかかるコストというのは結構大きい。Windowsを使用すると、いまだにJavaとEclipseとTomcatをネットから落としてきて・・・から始まる。一人や二人で開発しているのであれば許容できるコストかもしれないが、プロジェクトが大規模になるにつれて構築コストも管理コストも大きくなってくる。

Linuxを使用するとこれはずっと改善される。パッケージ管理システムで特定のコマンドをたたきさえすれば開発環境の準備は整う。けれどもこれも完全な方法ではなく、パッケージで提供されるのが古いバージョンであることが多いため、最新バージョンを利用するためには自分でコードをgitやSVNで取得してコンパイルしたりしかるべきファイルをしかるべき場所に置くなどの作業をする必要がある。また、一般的な会社においては個人がLinuxを使用するということにハードルがある。業務上の多くの場面でMS製品、特にOffice製品が動かないと仕事にならない。すると個人に与えられるPCというのはまずwindowsになる。その上さらに2台目のPCを与えるというのはコストの面でなかなか難しい。

仮想化も手軽な方法ではあるが、そもそも仮想マシンを動かすのにある程度のマシンパワーが必要になる。その様なPC(サーバー)を買い与えてくれる職場であれば、そもそも2台目のPCを買い与えているような気もする。

で、ここに全く別の解がある。RubyのRubyGem、PythonのPypi、Node.jsのnpmなどは独自のパッケージ管理システムを備えており、依存性の解決や最新バージョンの取得も容易であるのに加え、パッケージをプロジェクトのディレクトリにインストールすることも可能であるので環境を汚さない。環境を汚さず、プロジェクトの管理ディレクトリ内ですべて完結するということはすなわちデプロイも簡単であるということにつながる。デプロイが簡単であることは必須条件ではないが、簡単であることに越したことはない。

コードを一切書かずに開発できる環境があること

これまで一切触れてこなかった概念であるが、日本ではこういった要求が強くなる可能性が高いと私は考えている。

日本の大規模なソフト開発会社、特にSIerはちょっと特殊である。ソフトウェアの知識がない、もしくは、80年90年台に主流だった古い知識しか持っていないという人間がプロジェクトマネジメントを行い、かつ、詳細設計のレベルにまで口を挟んでくることである。

派遣PG時代の思い出
本当にあった怖い話 「派遣PG時代の思い出」

上記には、何とも非常識な企業内ルールが大企業の中でまかり通ってしまっている怖い例がたくさん列挙されているが、残念ながらプログラマだったら誰しも一度は経験するのではないだろうか。私の会社でも、「コード中の変更はコメントアウトしてリビジョン番号と変更者氏名を併記すること」「オープンソースソフトウェアを使ってはいけない(ただしLinuxはオープンソースソフトと見なさない)」などという謎ルールに遭遇したことがある。「DBが全部Varchar」はよく遭遇する。半ば当たり前ともいえる。

これが「世の中にはすげー職場があるもんだな。ははは。」で済めば良いんだけど、それで済まないような気がしてならない。というのは、ここにもう一つ問題があって、ソフト業界にいる人が皆ソフトに興味を持ち、モチベーションを高く保っているとは限らないということ。

自分から新しい技術を勉強して、それをどうにか業務に生かそうとする向上心の高い人は実はあんまりいない。私の経験上で言えばまず間違いなく数%のオーダーである。で、モチベーションの高くないプログラマが非常識なルールで縛られる環境で働くとそれが当たり前のように身に沁みついてしまう。

結果としてプログラマーは出来るプログラマーとダメなプログラマーに二分される。今後のソフトウェア開発技術は間違いなく高度化する。並列処理、分散処理、新しい言語と概念、非同期通信・・・。いまやちっちゃなベンチャーでもHadoopを使ってデータマイニングする時代である。そういう世界でダメプログラマーの食い扶持というのはどんどん少なくなってくる。

企業としては生産性を高めるためにダメなプログラマーを切って優秀なプログラマーを募集したいというのが当然の流れだけど、そもそも優秀なプログラマーの絶対生息数が少ないうえに「仕事ができないから」という理由で正社員の首を切ることを良しとしない社会構造になっている。

したがって、どうやってそういうダメプログラマーに仕事をさせるかというと、もう、コードを打たせないということに尽きるのではなかろうか。すなわち、コードを一切書かなくてもWebシステムが開発できる環境が必要になってくるのではなかろうか。

・・・と、このように書くと、いかにも侮蔑的なような印象となってくるが、そうでもない。コードを書かないということはバグが入り込む余地がずっと減るという点で、ソフトウェア開発に携わるすべての人間に恩恵があるのである。優秀なプログラマであっても、単純な画面や機能で構成されるシステムの開発をするときは、コードを書かなくても済む手段を使いたいと思うだろう。なぜならばそのほうが工数を削減できるから。これに尽きる。

「GUIでプログラムを作れる」、これはMicrosoftが最も得意とする分野だと思う。今でいうと、LightSwitchなんかがこれに相当すると思う。

まとめ

なんだかすごく長くなったうえにまとまりが無いが、以下に再掲する5つのポイント、

・マルチパラダイム言語を利用できること
・クライアントサイドでも静的型付け言語を選択できること
・高度なO/Rマッパ、もしくはオブジェクト指向DBが利用できること
・環境の構築とデプロイが簡単であること
・コードを一切書かずに開発できる環境があること

これらを兼ねそろえた(組み合わされた)開発環境が今後主流になってくるのでは、と思う。

ちなみに、記事全体を通じて親Microsoft寄りなニュアンスになっている気がするが、別にそういう意図はない。