Ruby
JavaScript
Rails
kubernetes

【失敗しない個人開発】創刊30年の雑誌「ゴング格闘技」のWebメディアを本業の傍ら個人開発でリリースするまで


はじめに

2018年の大晦日は那須川天心選手が、伝説のボクシング王者であるフロイド・メイウェザー選手と拳を交えるなど、格闘技が世間を賑わせることになりました。

そんな盛り上がりの中で、ふとしたきっかけで、創刊30年を誇る大手格闘技雑誌『ゴング格闘技』のウェブメディアを個人開発することになりました。(僕自身は一開発者で、運営は従来の紙媒体と同じ方が担当しています)

開発期間は2ヶ月弱、本業の傍らでエンジニア一人が権威ある媒体のウェブ版を立ち上げることになるという珍しい経緯で、予定通りリリースすることができたので、どのように個人開発を進めたのかを紹介していきたいと思います。

20181227_gong.jpg


どのような経緯で開発することになったのか

『ゴング格闘技』は格闘技ファンならば多くの方が一度は目にしたことがある大手媒体です。

様々な要因が絡み雑誌は休刊となったいたものの、SNSの更新は続いており、Twitter、Facebook、noteで計5万人を越えるフォロワーを獲得するなど、今なお有力な情報源として認知されています。

そして編集長が復刊させようと奔走し続けた結果、2018年12月下旬にウェブメディアを、今春には紙媒体を復活させるロードマップが完成しました。とはいえ自分の元へ話が来たのは10月になってからのことで、制作に着手したのは10月の末頃です。

私はフルタイムのエンジニアとして働いていることから、2ヶ月弱でコンテンツを揃え、安定稼働が保証された状態でリリースにこぎつける必要があったことから、考えただけで冷や汗が出るほどのスケジュール感でした。

ちなみに他にデザイナーがいるわけでもなく、デザインやインフラ周りも含めて全て一人で対応することになりました。自分の手を超えた範囲のサービスを短期で個人開発するのはチャレンジングかつ情熱的な取り組みでした。


どんな技術で開発・運用しているのか

gong構成図_03.jpg

ゴング格闘技は主に「Ruby on Rails + MySQL + Redis + ElasticSearch」、インフラはKubernetesで運用しています。

アプリケーション部分については比較的枯れた技術で、私自身も多数の運用実績があるため、一つの失敗(※こちらに後述)を除いて順調に開発を進めることができました。

Kubernetesは個人開発では初めてでしたが、特にトラブルはなく、リリースサイクルを早めつつ安定稼働させられたので導入は成功でした。初期の学習コストはありますが、既に習得している方ならばこの規模で取り入れても十分にメリットを享受できるはずです。

開発環境はDocker Composeで、開発で使用しているコンテナを少しダイエットさせたものを本番でそのまま利用しています。GitリポジトリはBitBucketを使っており、masterブランチにプッシュするとCircleCIが稼働します。

テストが全て通っていればAmazon ECRにコンテナをプッシュし、新しいバージョンのイメージがKubernetesでRolling Updateされる仕組みになっています。

手前味噌ですがモダンな開発・運用スタイルを取り入れており、高い生産性と同時に短期開発にも関わらず、目に見えるバグやダウンタイムのない安定した稼働状況を達成することができました。


  • フレームワーク…Ruby on Rails

  • フロントエンド…Webpacker(Rails向けのWebpack管理ライブラリ)、TypeScript、React、postcss

  • データベース…MySQL、ElasticSearch(関連記事、記事検索)、Redis(アクセス統計)

  • ロギング…Kibana


個人開発で意識した5つのテーマ

私は現在業務ではスクラムでのチーム開発を行っていますが、同様の開発スタイルを今回持ち込んでしまうと、どうしても開発期間が足りません。

しかし機能の洗い出しやスケジューリングなしに外堀からガリガリと開発して、予定通りに個人開発が終わることは稀です。私は具体的に次のようなポイントで開発を進めることにしました。


  • 着手前に必要なページ、機能を全て洗い出し、コンセンサスを得る

  • デザインカンプは作らずコーディングしながらデザインをする

  • 開発速度を最優先しながら、品質をツールで担保する

  • 「追加機能がほしい症候群」にやられないよう、完成時期を前倒しする

  • 思い切って新しい技術にも挑戦する

それぞれどのようなものか紹介していきます。


着手前に必要なページ、機能を全て洗い出し、コンセンサスを得る

新規サービスを立ち上げる時に最も気をつけていることで、必要なページを事前に共有しておけないと容易にスケジュールが遅延する要因になります。

例えば利用規約やプライバシーポリシーの用意を忘れてリリース直前に着手するとなると、先方にお願いをして、場合によっては法務のチェックも入ることから、コーディングは数時間で終わるにも関わらず、リリースまで1週間前後待たされることもあります。

事前に期限日までに送ってもらうことを着手前から共有しておくことで、このような遅延を防ぐことができます。

また開発を進めていくうちに「あれが足りない、これが足りない」と、片付ける以上に新規タスクが増えることもあるので、詳細な機能要件を完璧に揃える必要はないものの、必要なページや主要機能くらいは事前にリストを作り、不足がないかを共有しておくと良いでしょう。

ただしこれは経験がモノを言う部分が大きく、初めて間もない人がスケジュールを守るのは至難なので、いきなり手を動かしてしまっても良いと思います。

自分もエンジニアなので手を動かすことが好きでそこに傾倒しがちですが、個人開発では自分がディレクターでもあるので、計画を疎かにしないよう気をつけています。


デザインカンプは作らずコーディングしながらデザインをする

私のメイン業務はエンジニアリングですが、UIデザインもある程度専門的に学習していることもあり、UIからコーディングまでシームレスに開発できることを強みとしています。

スクリーンショット 2019-01-01 18.22.42.jpg

本来時間があればSketchやPhotoshopでデザインカンプを作成してからコーディングをするところですが、今回はとにかく時間がないことから、この規模では初めてインブラウザデザインに挑戦しました。

RailsではテンプレートにSlimを、CSSはWebpackerを通じてpostcssを採用しており、Webpack Dev Serverのホットリロードを活かせるので、コーディングとデザインをシームレスに行うことができました。

私自身が学生時代からゴング格闘技を購読していたこともあって、ゴング格闘技のブランディングを自分なりに感じ取り、それを配色やシェイプに活かせたので、フリーハンドで描くかのようなインブラウザデザインでもスムーズに開発を進められました。またデザイン段階からコンポーネント単位で組み立てることを意識するので、「デザインは良いけど部品があちこちに散らばる」といった課題は事前に潰すことができます。

試行錯誤しながら進めるには向かないですが、デザイナーがイメージを鮮明に持っていればインブラウザデザインは大幅な工数削減になることを実感しました。


開発速度を最優先しながら、品質をツールで担保する

チーム開発では鉄則のチケット管理や、ソースレビューを前提としたコミット・PR粒度は今回のケースには当てはまりません。

自分の使える時間が限られた中で、とにかくユーザーに満足してもらえるメディアを作ることが最優先です。1日8時間まとめて取ることはできないので、朝の出勤前、仕事の昼休み中、帰宅後など細切れの時間を総動員して開発を進めていきますが、作りかけでスッキリしないまま翌日を迎えることが当たり前の状態となります。

時にはコーディングとして最適解でないまま実装することもありますが、保守性やカバレッジ率の水準を守りながらも、時間あたりで最速の実装ができることを心がけました。

「水準」とはなんぞやと思われるかもしれません。究極的には自分の価値観に依存するものの、linterやカバレッジ率によってある程度担保することができます。今回は急ぎの開発になりましたが、緩めのlinter(Rubocop、tslint)を設定し、コミット時のフックにlintを通すようにしています。


「追加機能がほしい症候群」にやられないよう、完成時期を前倒しする

愚痴ではないのですが、クライアントに完成物もしくはその直前に見せると、多くの場合で当初の要件にはなかった追加機能を求められることになります。

それによる開発遅延を何度か経験したことがあるので、今回の場合は12月下旬がリリース日でしたが、12月上旬には当初の機能を全て盛り込み、ステージングで稼働する状態に持っていきました。

そしてリリース時に必要となる初期コンテンツの入稿を試してもらったところ、多くの追加機能の要望が来たので、リリース日となる27日までに追加機能の実装とテストを行いました。

クライアントが専門家でない場合、顧客がほしいものはいくら文書にまとめても実際に見てみないと分からないことがしばしばあるので仕方がないことです。お互い納得しないままリリースすると関係に傷が入ってしまうので、あらかじめバッファーを設けて、その期間内に追加機能の要望を取捨選択するようにします。

遅くとも開発期間の7割くらいに差し掛かったら、クライアントに完成物を見せるのが良いと個人的に考えています。


思い切って新しい技術にも挑戦する

これは自分のポリシーになりますが、納期を早めることばかり考えると、今ある技術を切り売りすることになり、せっかくプロジェクトを経験しても技術的成長が限定的になってしまいます。そのため時間が厳しい中でも「今このタイミングだから取り入れられること」は積極的に導入しました。

今回はRailsの新規案件ということで、WebpackのRails向け管理ツールであるWebpackerを初めて導入しました。Webpackの設定が辛い話はあちこちで上がっていますが、Webpackerを使うと設定を大幅にショートカットできるので、結果的に成功でした。

逆に唯一のしくじりエピソードとしてはRails 5.2で導入されたActiveStorageを取り入れようとしたことです。

Railsの画像管理ライブラリとしてはPaperclipが最有力でしたが、deprecatedとなり、ActiveStorageが推奨となったことがGithubのプロジェクトページに明記されています。

スクリーンショット 2019-01-01 19.45.05.jpg

これを受けてActiveStorageに移行しようとしましたが、仕様が特殊で、とりわけ画像がpublicなURLを持たず、時限式の署名URLを取得するという仕様に悩まされました。

これはGithubのIssueで議論されているのですが、CDNでキャッシュを配信する時等に問題となるほか、ページあたりの画像数が多ければ署名URLを多数発行することでパフォーマンス劣化にもつながります。

メディアサイトはとにかくスループットが命で、パフォーマンスのために様々な工夫を凝らしている中、署名URL生成に費やされる時間やリクエスト数は決して見過ごせないものでした。

モンキーパッチで対応することも可能ですが、工数の増加だけでなく保守性に不安を抱えるのを嫌い、結果的にActiveStorageを導入することは開発終盤に断念し、Paperclipに出戻りしました。

「〜がこう言っている」という伝聞ベースではなく、本番運用をあらかじめ考えておけば未然に防げた、しくじりエピソードとして今後の開発に活かしたいと思います。


最後に

今回開発・運用することになった『ゴング格闘技』ですが、年末には万単位の多くのアクセスが寄せられたものの、S3を使用しない(※)最小構成ながら、ほとんどのリクエストを100ms前後で捌くなど、とても安定した稼働を実現しています。

(※メディアサイトではサイトの特性上、リクエストあたりの画像が多く、S3のデータ転送量が財布を直撃するので、今回はLightSailに画像サーバーを設置しました。画像は定期的にS3へ低頻度アクセスストレージにバックアップを取ります。)

個人開発となるとどうしても品質やスケジュールの担保が難しいと聞きますが、今回紹介した事例を参考にして頂き、皆さんが個人でも新たなサービスに携わるキッカケになれば幸いです。

また最後に宣伝ですが、長らく食わず嫌いしていたSNSを2018年後半になってようやく(!)始めたので、興味があればフォローして頂けると嬉しいです。