2012-05-09
ウェブサービスをゼロから作って成功したこと、失敗したこと
いつもなら寝ている時間なのだけれど、なぜか睡魔がやってこないので過去の思い出をまとめてみる。
去年の2月ごろ、新規案件のウェブサービスに開発メンバーとしてアサインされた。作るべきものが大量にあったため、4チーム(工期中多少増減したが)に分けてドメインごとに作業分担をした。そのうち、ウェブアプリケーション本体(フロント、API、マネージツール)を担当するチームのリーダーが自分の役割だった。
そのプロジェクトは去年の末に一旦の区切りを迎え、自分はそこで退職し、新たな環境に身を置くことにした。それから丸4ヶ月経って、自分が書いたコードと新しい環境で書かれていたコードを見比べて、思うところが多々ある。それらを文章としてまとめたいと思う。
失敗したこと
簡単な骨組みを作ったあと、デプロイの仕組みを作った。php には phar という仕組みがある。これは jar/war のようにウェブサービスに必要なファイルをひとまとめにしてそのまま実行できるようにしたものだ。これを使ってリリースをアトミックに、安全に行えるような仕組みを考えた。だが、これは失敗した。
失敗した理由は、政治的理由半分、手間が半分だ。やはり php というのは置けばすぐ動くと言うのが最大の魅力だと思う。phar に固めるにはワンステップがはいるし、緊急に1ファイルだけ直接書き換えると言うようなこともできなくなる。とはいえ、中途半端にファイルが配布されてエラーになるというのはやはり起こりうる問題であるし、政治的理由が無ければもう少し踏み込みたかったところではある。
テンプレートエンジンとして、Smarty3 を採用した。これは Smarty2 が既に標準のテンプレートエンジンとして採用実績があり、デザイナを確保できると言う理由から互換性の高さを評価して選択した。Smarty2 を選択しなかったのは失われる互換性以上に Smarty3 で追加された機能が魅力的だったからだ(具体的にはテンプレートの継承と javascript における {} 問題の解決)。だが、これも失敗だった。
いろいろな理由はあるのだろうが、結局僕がいる間はデザイナは確保されず、エンジニアがテンプレートを書いていたため、Smarty3 を選択した理由が完全に失われた。欠点は最後まで足を引っ張られた。多くの場合で {} を気にしなくてよくなるとはいえ、稀にミスって破滅したり、デフォルトフィルターが必ず最初に引っかかってしまうために DateTime をフォーマットするときに大変面倒な思いをしたり、eAccelerator との相性が悪かったり、コンパイルされた php がとてもじゃないが読めたもんじゃなかったりで大変苦労した。後になって分かったことだが、別のプロジェクトで Twig の使用実績があったので、こちらを選択した方が苦労が少なかったように思う。
データアクセサを実装する労力をケチった。なんせ作るものは大量にあったので、そこまで手が回らなかった。全体でMVCを強力に分離しているため、そこまで手をかけなくても問題にならないと踏んだのだ。結果として全体で一番足を引っ張る要因になってしまった。
現職ではデータアクセサはこれまでに4回書き直されている。実際に世代を経るたびに目に見えてコードの見通しは良くなっており、逆にフレームワークは初期からほとんど変わっていない。これは完全に自分の判断ミスだ。ウェブサービスの開発においてデータアクセサは非常に重要なポイントであり、手を抜くべきところではない。
リクエストオブジェクトを生成する部分を若干トリッキーな実装で逃げてしまった。詳細は説明し辛いのだが、本来はリクエストがディスパッチされる前に生成するべきであるのだがディスパッチの際に一部情報が生成されるのでそのタイミングでリクエストオブジェクトを生成することにしてコード量を減らした。
これは後にディスパッチを通らないようなコードが生まれたときにハマりそうだなー、その時も自分がコードを書いてれば大丈夫だけれど、そうとも限らないし、と思ってたら案の定思った通りになった。
ログ出力のサポートが十分でなかった。これは完全に納期の問題で、そこまで手が回らなかった。結果として障害(と呼ぶほど大きいものはあまり起きていないのだが、細かな問題はいくつかあった)の調査がやり辛くなってしまった。
API とフロントのコードを分離して、フロントを API へのアクセサにした。これは開発中盤から終盤にかけてオーバーヘッドや API サーバへの負荷の集中といった理由から廃案になった。
DB 設計に失敗した。大規模になったときにマスター分割できるような構造というのを全く意識していない(JOIN は使っていないが 大嘘だった。JOINも使ってるしGROUP BY をそこそこ多用しているので危うい)ので、月間UUが1000万人を超えるようなモンスターサービスになったときには破滅が訪れる。
CPD と静的解析を常にかけていたが、効果を実感できなかった。これは静的解析にどうでもいいものが引っかかってしまって、それをその一部分だけ結果から除外するいい方法が見つからなかったことと、常に高い意識を持って問題となるコードを書かないようにしていたからだと思う。
働きすぎた。23連勤、月間出勤数26日という無茶をした結果体を壊して1ヶ月半ほどの休職が必要になった。自分の体力の限界を踏まえた上でペース配分をするべきだった。
成功したこと
役割分担が効果的に行えた。序盤〜中盤にかけては自分が基盤部分を実装し、残りの二人がその上に乗っかるアプリケーション部分を書くという分担をした。基盤の実装者と利用者が違うため、客観的に使い辛い部分を指摘され、改善することができた。また、分担によって把握していないコードが増えるという悪影響があるが、これに関してはお互いが書いたコードは必ず読む(分からないコードがあるのは問題である)という意識の共有ができていたので問題にならなかった。
前述の通り自分は体を壊してしまったのだけれど、この時も致命的な問題には発展しなかったし、退職の時もスムーズだった(と、自分は認識している)。
テストを充実させた。全体を通して8割強程度のカバレッジをキープできた。これはTDDにこだわりすぎない(結果的にテストが成果物に含まれれば後でも先でもそこまで問題ではない)、フレームワークにテストをサポートする機能を実装する(HTTP リクエストをシミュレートする等)、早い段階からテストを書いて、テストがあることを普通にする、jenkins ビルドの際に継続的に計測した等の施策の効果だと思う。
結果として、序盤は基盤のコードを大きく3回書き換えることになったが非常にスムーズであったし、中盤には API から機能の移動が発生したが大きな問題にならなかったし、ドラスティックな変更に耐えうるコードを作れた。
MVCを強制的に分離する仕組みを作った。Model には引数の無い get メソッド以外パブリックなメソッドは存在しなく、View には一度だけ呼べる set と getResponse しかない。この仕組みのおかげでコードの役割が強制的に分離され、疎結合が保たれた。また自動的にテスタブルなコードになり、テストの充実にも貢献した。
DI コンテナを実装した。前述の通り全体的に結合が疎であり、全てコードで解決しようとすると非常にコード量が多くなり、見通しも悪くなってしまう。DI コンテナによって最小限の記述量でそれらを解決できるようになった。
Autoloader を使った。これはもう常識というか、require_once が許されるのは小学生までと言うか。git を使った。これももう常識というか、新規プロジェクトで svn が許されるのは00年代までと言うか。
これらについてまじめに書くと、require_once とか人間が書くと絶対しくるし重複も発生するし見通し悪いし全くいいことがない。git もコミットがコミットとプッシュと言う2段階になることでここの作業が独立して行えるようになって一つ一つの作業のサイズが小さくなるし、マージが賢いしブランチと言う概念が最初から存在するし svn に劣る部分が一つもない。
どちらとも言えないこと
言語として php を選択したこと。メンバーに php に習熟している人間がいないため、あえて php を選択する意味があったのかと言うと、微妙な線である。
フレームワークを自分で実装したこと。これは基本的にやってはいけないことで、自分も避けるためにいろいろと調べたのだけれど、あの時点で php5.3 でまともに動く実績のある軽量フレームワークと言うのは存在しなかった。あの時点で silex を選択するべきだったのかは判断できないところだと思う。
現職のコードと比較して
現職のコードと前職で自分が書いたコードと言うのは、全く違う歴史を経てきたものであり、実際に全く違うコードになっている。しかしながら思想的に似ている部分は多々ある。MVCの強制分離やオートローダーというのは全く同じ思想だし、最近は git に移行してリリースプロセスを発展させているというのも全く同じだ。
前述のようにデータアクセサに力を入れていると言うのは歴史的にそこが重要であると言う認識があったからだと思う。前職のコードでもそこがネックになってしまっていたので、あれが数年歴史を経ていくと同じようなものが生まれると思う。
逆に、テストやDIなどの手法は自分が書いた方が優れている。これはコードを書く人数が少なかったからというのと、ゼロからコードを書くことが可能だったということが要因だと思う。
また、工期の問題でショートカットしたログや、大規模サービスのためのDB設計などは比べ物にならないほど現職の方が発展していて、非常に勉強になる。
まとめ
いま自分は非常に難しい問題を抱えている。それは、コードが書けないと言う問題だ。自分がどんなコードを書いても、それに対して問題点が思い浮かび、それを解決する方法が思いつかない。この状態を自分は「潔癖性」と呼んでいるのだが、なかなか乗り越えることができない。
何にせよコード力は様々なシチュエーションで沢山のコードを書かなければ育たないので、ある程度足りないコードでもとにかく書くことが重要であると言うのは頭では分かっているのだが、なかなか踏み出すことができない。これは、一度書いたコードを修正するのは新しいコードを書くことの何倍も難しいという事実からくるものだと思う。
幸運にも今は一度書かれたコードを修正して、さらにそれをもとに新しいコードに移行するという作業をやっているので、このプロジェクトをやり遂げればこの「潔癖性」を克服できるのではないかと期待している。
- Twitter / @slightair
- Twitter / @hatebuit
- Twitter / @730tks
- Twitter / @itpress_biz
- Twitter / @hatena_news
- Twitter / @WebServiceInfo
- Twitter / @spmarknet
- Twitter / @qio_op
- Twitter / @inoinobot
- Twitter / @kohex
- Twitter / @tk0451
- Twitter / @NEWS_0
- Twitter / @programming_j
- Twitter / @yuya_presto
- Twitter / @kumechang
- Twitter / @dai_yamashita
- Twitter / @wakeari_maru
- Twitter / @wPGnews
- Twitter / @tyudon
- Twitter / @020spade
- Twitter / @240k5
- Twitter / @unagidaisuki23
- Twitter / @jomu
- Twitter / @chada_com
- Twitter / @ryo_dev
- Twitter / @bot_yarou
- Twitter / @webdesignStatio
- Twitter / @yadokari23eor
- Twitter / @asip2k25
- Twitter / @webdev_topics
- Twitter / @RtestR
- Twitter / @katzchang
- Twitter / @yusuketsuiki
- Twitter / @kimihito_
- Twitter / @wata_d
- Twitter / @mikage_sbm
- Twitter / @gab_km
- Twitter / @design_manabu
- Twitter / @_____test
- Twitter / @pctoolnews
- Twitter / @_newsrss
- Twitter / @trdnow
- Twitter / @NohKo3
- Twitter / @postnewshatena
- Twitter / @hatebuhot_it
- Twitter / @NooKo3
- Twitter / @anakahala
- Twitter / @puzzeljp
- Twitter / @hellowebworld
- Twitter / @yossiepon
- Twitter / @w3qreader
- Twitter / @php_j
- Twitter / @kuronzero
- Twitter / @RSS_hateb_Roy
- Twitter / @yossiepon2
- Twitter / @nobu666
- Twitter / @mhag
- Twitter / @html_coderoid
- Twitter / @ueno_lab2
- Twitter / @kumonopanya
- Twitter / @heavenshell
- Twitter / @__gfx__
- Twitter / @sugyan
- Twitter / @koi_zoom1
- Twitter / @hiroyukim
- Twitter / @amari3
- Twitter / @moeretamamokuro
- Twitter / @room445
- Twitter / @340mits
- Twitter / @SAPPOROlovers
- Twitter / @rss_for_me
- Twitter / @020spade
- Twitter / @kizuki_jpn00
- Twitter / @naoto_koide
- Twitter / @tweetreaderdesu
- Twitter / @crssfeed
- Twitter / @sbm2tw
- Twitter / @NohKo3
- Twitter / @memememomo
- Twitter / @NooKo3
- Twitter / @apfelkinoko
- Twitter / @tyataro
- Twitter / @suprisenews
- Twitter / @hatebu_hotentry
- Twitter / @100hatebu
- Twitter / @hotentryinfo
- Twitter / @sostso
- Twitter / @hatebu
- Twitter / @musicthanks
- Twitter / @WD_Tips
- Twitter / @Spring_MT
- Twitter / @net_watch
- Twitter / @lioninoil2000
- Twitter / @asip2k25
- Twitter / @RSS_hateb_Roy
- Twitter / @jsMagazine
- Twitter / @frecce
- Twitter / @ShimojiKjp
- Twitter / @_tk84
- Twitter / @mizoguche
- Twitter / @yuroyoro
- Twitter / @toniokatanuki
- Twitter / @mikeda
- Twitter / @tabizou
- Twitter / @tell_k
- Twitter / @parappa_bot
- Twitter / @aileron
- Twitter / @setoh2000
- Twitter / @api_j
- Twitter / @Hiraku
- Twitter / @b2r
- Twitter / @sugimoto1981
- Twitter / @HolyGrail
- Twitter / @takochuu
- Twitter / @masa6127
- Twitter / @yktyshr_Reader
- Twitter / @htbit_bot
- Twitter / @5jida2jimu1a
- Twitter / @wasaist
- Twitter / @Bass_Voice_XYZ
- Twitter / @bethbeth321
- Twitter / @cutmailbot
- Twitter / @aysz_
- Twitter / @con_mame
- Twitter / @yosshi
- Twitter / @yoshiori
- Twitter / @kent013