退職して560万円相当の工数をかけてお金を稼ぐサービスを開発した
あとで読む
※ 前職の月額単価80万円を基準に、7ヶ月分の工数として560万円相当と換算。
BizRank
※キャッシュがなくてコールドスタートとなる場合があります。その場合レスポンスが返るまでに2~3分ほどかかります。ご了承ください。
ビジネス書籍を紹介するサービスです。
自分が書籍を購入する際に、SNSでバズっている書籍をなんとなく購入しがちという所から、影響力ある人が薦める、おすすめの一冊を独自のアルゴリズムで評価してランキング形式で紹介します。
1月から退職して設計を始めて8月27日に人生初サービスをリリースする事ができました。
ずっと個人サービスを持ちたかったので、アラサー無職で途中コロナ(はじめてなった、喉が今まで感じた事ない痛みで唾も飲めなく相当苦しかった。)になりながらもリリースまで漕ぎ着けて一安心しています。
実際に使ってみると見た事ないビジネス書籍がたくさん並んでいて、とても面白いです。おそらく普通に検索しているだけでは辿り着けないような書籍ばかりです。 ですが、それなりに影響力のある人が紹介しているため良い本に違いないです。
開発方法も実務に近い内容になっているので、初学者の方への参考にもなると思う内容です。
サービス、記事にコメントくれたら作った人が喜ぶかも😄
追記
3ヶ月運用してみての、その後をZennに書きました! 【無料公開】560万円かけて開発したサービスのソースコード
運用費を広告で賄う事が出来ず毎月赤字の状態で苦戦しております。
ビジネスモデル
時間をかけて作成するのでマネタイズも考えました。
アフィリエイト報酬型 にしました。
仕組みはシンプルで紹介する書籍にAmazonのアフェリエイトリンクを貼り付けて、購入があれば収益が発生する仕組みです。収益の使い道はサービスのサーバー代を差し引いて、残りの10%をユーザーに還元したいと考えています。
人気のある書籍プレゼントキャンペーンを行えたらと思います。最初は登録ユーザーからランダムに選んでメールでやり取りして発送するみたいな簡素なやり方を予定しています。
開発の背景
背景は2つあります。
エンジニア職とビジネス職の溝があった
ソフトウェアエンジニアとしてのキャリアを通じ、エンジニア職とビジネス職の意見が異なる事から対立しているように感じました。
この経験から、両者の視点を理解し、お互いに尊重し合える人材の必要性を強く感じました。
以前の私はプログラムコードが書けて物作り出来れば良いと考えていました。
個人としては決めたものを手抜きせず納期までに作る事に全力を尽くしていました。
しかし民間である以上、それらを続けるにはお金を生み出す事が必要であるとビジネス部門の話を聞いて思いました。
ビジネス部門の視点で言うと出来上がった物をどうお金に変えるのかについて全力を尽くしており、自分とは見ている視点が違いました。
エンジニアもビジネス視点があった方が仕事への理解も高まり溝が少しでも埋められると思い、ビジネスに関して興味を持ちました。
仕事への解像度を高めるためにもビジネス書籍を読みたいと思うようになりました。
技術力に自信がなかった
私はサービスの立ち上げを経験した事がなく、既存サービスに対して機能追加・不具合修正・パフォーマンス改善を行っていました。
ユーザ認証やインフラ構築など知らない事が多く常に自信のない中で業務を行っていました。
そのためか不確実性の高い機能追加に対してWBSを書いたりする事が苦手でした。
前職ではサービスの立ち上げは難しかった事もあり、技術選定、要件定義、UI設計、テスト設計、コーディング、インフラ構築を一貫して行いました。
BizRankの開発を通じて、以下のスキルに挑戦しました
- 書籍評価アルゴリズム
- 結合・単体テスト
- UI/UX設計
- 可読性の高いコード作成
- アーキテクチャ設計(書籍データ取得基盤)
- セキュアな認証システムの設計
- スケーラブルなバックエンド設計
- 効率的なCI/CDパイプラインの構築
これらの中には、今まで経験したことのない難しい課題もありました。でも、一つ一つ乗り越えていくうちに、少しずつ自信が出てきました。
実際に運営されているサービスと比較するとまだまだ稚拙な部分もありますが、自らサービスを企画してリリースできた事を嬉しく思います。
やっとソフトウェアエンジニアとしてスタートラインに立てた気がします。
この開発を通じて得た知識と経験を活かし、再びキャリアを積みたいと考えています。
開発方法
※前職での経験を元にしています。他の企業では別の方法を取っている可能性もありますがWEB開発会社の場合、多くは似た方法だと思います。下記は全てソフトウェアエンジアが担当する部分になります。
要件定義
サービスとして必要な機能を洗い出す、スプレッドシートに書き出して、その機能が本当に必要か精査しながら進めていく。実務ではPM・デザイナー・顧客を含めて相談しながら追加したり減らしたりしながら進めていきました。 BizRankの場合は自分一人なのでNotionに必要な機能を書き出して進めました。
ドキュメント作成
前職ではMarkdown形式で下記の項目を埋めていました。BizRankの開発ではNotionを使用。
- 機能設計(どんな機能を持つか)
- UI設計
- DB設計
- テスト設計(観点:機能を担保するため確認すること)
- テストケース(スプレッドシートに手動で確認する項目を記載)
- 詳細設計(どう作るか)
テストケース作成
上記のようスプレッドシートに手動でテストする手順をまとめていく。
- ブラウザ
- 端末
- 権限
- 下記の項目は抽象的で自分でも完璧に意味は分かってないです。QAエンジニアの方教えて欲しいです。
- 大項目(テストしたい要素があるページ)
- 中項目(どの要素)
- 小項目(さらに細かい場合、中項目の中でもどこなのか小項目は後ろに増やしていける、そして最後は表示確認なのか動作確認なのか等確認したい動詞が最後来る)
- 前提条件(テストする前に満たしておきたい条件)
- テスト手順(テストを行う手順、ここがしっかり書かれていればテスト自体は誰でも実施出来るただ、慣れたテスターにとってはここよりも大〜小項目をしっかり書いた方がテストを素早く進められるし書く側としても記述量が少なくて楽。)
- 期待値
- 結果
WBS(見積もり - ガントチャート)
ドキュメントの詳細設計・テストケースを元に完了までの見積もりを立てる。
見積もりはスプレッドシートで作成する。要件定義のスプレッドシートを元にテストケース実施分の工数を入れていけば、その日どこまで進めれば見積もり通り終われるか目処を立てる事が出来る。
機能が大きくなればなるほど、見積もり通りに終わる事はなく、レビュー後の手戻り(コードレビュー・ドキュメントレビュー)、体調不良、要件定義漏れ、影響範囲考慮漏れ、謎のバグ、担当者失踪、顧客失踪などにより見積もり通りに終われるのは珍しい。
そのためバッファという上記を考慮した期間を工数に含める事が出来るが、そのバッファさえも突き抜ける事がある。そうなると毎日の進捗報告は胃を痛める事になる。
これだけ見ると見積もりする意味ないのではと思うが、見積もり通りに開発が進まなくてもどこまでなら終わっているか視覚的に顧客へ説明しやすいため使われている。
なのでいつまでに終わるか示した表というより、どこまでなら終わっているかを示す表という意味合いでみんなが認識して下さるとエンジニアは胃を痛めずに済むかもしれない。
実装
ここまでやってついにコードを書き始める事ができる。
ここではひたすらドキュメント読み込んでコードを書く。
コードレビュー
プルリクエストでmasterに追加したい機能の細かい単位を作成して、別のエンジニアに確認してもらう。プルリクを作成するとCIが動作して変更箇所に対して単体テストが実行される。それをパスしたらコードレビューに進む。
BizRankでは自分一人なのでレビュー箇所で再度、自分のコードにミスがないか二重チェックしている。あんまり精度は良くない。レビュー待ちがないので開発速度はその分早い。
結合テスト
機能が実装できたら、開発環境(stg環境)にデプロイして結合テストケースの項目に沿ってテストを実施していく。BizRankの開発環境はローカルのみになるので、ローカルで確認することになる。
マージ
結合テストもパスしたらいよいよマージしてmasterに機能追加する。
この工程を何度も行いながらサービスを開発していく。
技術スタック
システムアーキテクチャ
※上司が機能追加等でこのような構成図を描いていてカッコよかった。複雑度で言うと負けているが、今回自分も同じような構成図を描く事が出来て嬉しい。
ユーザ視点
ユーザがリクエストを投げるとCloudflare CDNからキャッシュされたページが返る、その後ログイン等、APIリクエストはCloud RunのRuby on Railsに投げられる。その際コールドスタート発生(最低インスタンス数0のため)するので時間が掛かる。対策として稼働時間チェックで定期的にリクエストを飛ばしてインスタンスを起動させているが、15分間隔だとコールドスタートが発生する事が多い。
開発者視点
開発者はBizRankリポジトリのコードを変更するとGitHub Actionsワークフローに設定した処理、単体テストが行われ問題なければビルド・デプロイが行われる。このプロセスを繰り返しながら機能追加・不具合修正を行なっていく。
そして定期的に書籍データを更新するためのBotをローカル環境で実行する。(将来的にここも自動化したい)これによってFirestoreの書籍データが更新される。更新されたデータはAPIのデプロイ時DBに取り込まれる。
- フロントエンド
- Next.js
- biome(リンター・フォーマッター)
- Jest(テスト)
- Zod(バリデーション)
- Next.js
- バックエンド
- Ruby on Rails
- jwt(認証)
- rubocop(リンター)
- RSpec(テスト)
- Ruby on Rails
- プロキシーサーバー
- TypeScript
- biome
- TypeScript
- データベース
- MySQL(RDB)
- Firestore(NoSQL)
- 書籍データ取得基盤
- Node.js
- Puppeteer (ウェブスクレイピング)
- biome
- Jest
- Node.js
- その他
- Docker
- GitHub Actions(CI/CD)
- Cloud Run(デプロイメント)
- Cloudflare R2(ストレージ)
- Cloudflare Workers(プロキシサーバー・Cron)
- Cloudflare CDN
前職の環境がGoogle Cloudで馴染みがあるので、GCPを採用しました。GitHubのリポジトリに変更があった際にワークフローでCloud Run、Cloudflare Workersにデプロイされるような仕組みになっています。
ER図
rails-erdというライブラリを使って生成しました。 bundle exec erd
を実行するとPDFファイルで生成されます。実行する前に graphviz
をコンテナにインストールしておかないと下記のエラーがでます。
rake aborted!
Unable to find GraphViz's "dot" executable. Please visit https://voormedia.github.io/rails-erd/install.html for installation instructions.
/usr/local/bundle/gems/rails-erd-1.7.2/lib/rails_erd/tasks.rake:13:in block (2 levels) in <main>'
/usr/local/bundle/gems/rake-13.1.0/exe/rake:27:in <top (required)>'
Tasks: TOP => erd => erd:generate => erd:check_dependencies
(See full trace by running task with --trace)
機能一覧
ランキング生成
BizRankはNoteに投稿されているビジネスに関連する記事から、書籍データの収集・その後、記事投稿者の影響力、いいね数、投稿日を参考に書籍の評価をしてランキングを生成します。
評価の参考にした記事、書籍に関連するハッシュタグを書籍の詳細ページで確認する事が出来ます。
ログイン
従来のメールアドレス・パスワードとGoogle認証によるログインが可能です。
サービスとユーザ間での認証にはjwtを採用しております。インフラがCloud Runを使用したサーバーレス構成のためステートレスでセッション管理を行えるjwtにしました。
いいね
気になった書籍に対していいねをする事が出来ます。
いいねした書籍はプロフィールページに表示されURLを共有する事で自分が気になっている書籍を紹介する事が出来ます。
実装した理由は主に2つある
- サービス利用ユーザが気になっている書籍を把握するため、このデータを使ってさらにアルゴリズムの精度を上げたい。
- 影響力のあるユーザがどんな書籍をいいねするのか、ユーザが見れるようにしたいため。ユーザプロフィールにアクセスすればいいね一覧を閲覧する事ができる。今はなき機能だがX(Twitter)ではユーザのいいね一覧を見る事が出来た。影響力のあるユーザがどんな書籍を気になっているか知見として収集したい。
プロフィール
プロフィール画像、自己紹介文、SNSリンク、個人のウェブサイトリンクを設定する事が出来ます。リンクを共有するだけで手軽に自身のプロフィールを共有する事が出来ます。
Xにてコメントの投稿
ログインを行わなくても気になる書籍について記録を残しておきたい場合、Xにてコメント付きで投稿するのは効果的です。
#BizRank が投稿に含まれていると運営者・BizRankユーザ間でおすすめのビジネス書籍について討論することが出来ます。 この機能を使って、ポストしてくれたら作った人は喜ぶかも😄
OGP
SNSで紹介して頂く際、ユーザの目に入るように作成しました。
書籍詳細リンク共有時
ユーザープロフィールリンク共有時
それ以外リンク共有時
工夫した点
Dockerコンテナ内で完結した開発環境
Biome, Rubocop, RSpec, Jestの実行がコンテナ内のライブラリを利用しているため、Dockerさえインストールすれば、ホストにRuby, Node.jsのインストールが必要なくホスト環境を汚さずに開発が行えます。
そのために多少荒技なシェルコマンドを書いたりしましたが、端末に囚われず、Windowsの開発環境にも移行しやすく大変気に入ってます。
Google認証
Firebase Authを経由すると遅い という投稿を見かけたので、Google CloudのOAuthを使って認証部分の実装を行いました。最初OmniAuthというライブラリを検討して実装していました。しかし、途中でOmniAuthは認可フローにおいてセッションを使用する事が発覚、今回のサーバーレスアーキテクチャには使えない事が分かり手戻りが発生しました。
OmniAuthを使用すると設定するだけで認証・認可に必要なコントローラーを生成してくれて、小難しい認証部分の実装を楽にしてくれると思いましたが、駄目でした。
そもそも認可処理のstate管理にはセッションが一般的に使われ、サーバーレスアークテクチャ(インスタンスが終了する際、メモリに保存されたセッションが破棄されるため)と相性が悪く機能実装する事が出来ないと思い絶望しました。
途中Auth.js(NextAuth.js)を使ってフロントエンドだけサーバーレスを諦める構成にするか悩みました。
考えている中で、stateにjwtを使えばセキュリティを担保しながらサーバーレスの構成で実装出来る事を思いつき認可フローを自前で実装してGoogle認証機能を追加する事が出来ました。
一番自信がない部分の実装だったため、とても知見が溜まりました。
N+1
Cloud SQLの費用はとても高く無駄なクエリーは発行出来ないため、書籍一覧、ユーザのいいね一覧で発生していたものは includes
を使って対応しました。
Polymorphic関連付け
Likeモデルを拡張性のある実装にしました。これを行う事で書籍以外のいいね、コメント、ユーザー記事などのモデルが増えた際、1つのテーブルで管理する事が出来ます。
単体テスト
実務経験を得て大きいシステムを開発する時はテストがないと、変更するたびにバグを埋め込んで、収集が付かなくなる事を学びました。
なのでBizRankの開発ではテストコードをたくさん書きました。
その結果もありフロントエンドの繋ぎ込み、機能追加で収集が付かなくなる事態には遭遇しませんでした。テストが通れば他の機能に影響するような大きなバグにも見舞われませんでした。
テストは偉大です。
fetchリクエストをaxios風に使うwrapper関数を実装
Zennの開発をしているエンジニアさんがaxiosをやめたらパフォーマンスが上がったという記事を投稿していたのを見て同じように実装してみました。
Next.jsは通常のfetchを拡張しているのでfetch → fetch(Next.js拡張) → axios風にラップしたfetch関数という構図になっています。
マークダウンレンダリングをremarkから自力でレンダリングしている
マークダウンを取り扱う際 react-markdown
を使用するとブロックリンクのレンダリングで実装するのが辛くなるという記事を見かけて、リポジトリを参考にマークダウンをレンダリングするコンポーネントを実装しました。
これによりマークダウンのレンダリングを効率よく、汎用的に実装出来るようになりました。今後のコメント機能を実装する際にも使用する予定です。
Next.jsベンダロックインによるページの表示速度低下
Next.jsと聞くとISR(一定期間ごとにページを新規で生成、それまでは事前に生成されたページを返す。そのためページ表示が高速), SSG(事前に生成したページを返す)が使える事でページ表示が高速に行えています。
しかし、この機能はVercelでしか動かない。
そのためCloud RunにデプロイするにはSSRで実装する事が必須となります。
ただ、これだとリクエストを受け取ってページ生成するのでレスポンスが遅くなる。
工夫策としてオリジンサーバーの前にCloudflare CDNを立てて書籍一覧等の更新が少ないデータはEdge Cache TTLを使用して、近くのサーバーからページのキャッシュを返すようにしています。これによりオリジンサーバーへのリクエスト数を減らし運用費を節約しながらSSRによる表示速度低下を解消しています。
Cloud Runにカスタムドメインを当てるとレイテンシーが増加する
Cloud Runにカスタムドメインを当てるとレイテンシーが増加する問題があるためリバースプロキシーにカスタムドメインを設定してリバースプロキシーサーバー経由でCloud Runにリクエストが飛ぶようにしました。
ロードバランサーを使う方法もありましたが、個人で運用するには高額なためCloudflare Workersにリバースプロキシサーバーを立て対応しました。これにより無料でレイテンシー増加問題を解決しました。
書籍評価アルゴリズム
書籍の評価は3つの要素から行われます。
Note記事のいいね数>記事の公開日>記事を書いたユーザの影響力となっており左から順に優先度が高く評価に大きく影響を与えるように設計されています。
まだまだシンプルなアルゴリズムですが、精度を高めていけたらと思います。
世の中に似たようなサービスGoogle・Yahoo, Netflix・Blockbuster, Instagram・Flickr, TikTok・Vineはあるけれど、人気とそうでないものへ分けた理由に優れたアルゴリズムとそうでなかったものに分かれていると考えています。
自分はエンジニアとしてそれくらいアルゴリズムがサービスにとってのコアな部分だと考えていて、そのような機能を実装出来た事を嬉しく思っています。
GitHub Actionsでソースコード変更のたびに単体テスト・ビルドチェックを行う
実務でのソースコード変更、ソースコードチェックの体験が良かったので、導入しました。
デバッグがローカルで行えず大変でしたが、一度設定を書いてしまえば後は楽です。
実行速度を改善するためにnode_module, bundleはキャッシュするようにしています。
これが無料で使えるのに驚きを隠せません。さすがMicrosoftです。
リリースタグを振ったバージョン管理
記念すべきv1.0.0公開直後
デプロイは手動で行われるようにしたいため、リリースノートの公開と同時にデプロイのワークフローが走るようにしました。このバージョンを上げながら機能追加をしていきたいと思います。
UI
figmaでUIを書き起こしてから実装を行なっていました。
ダークモード対応
端末のダークモード設定状態によって変わるようになっています。
気持ちの良いアニメーション
クリックしてそれに対して報酬があるような作りにしています。
ページネーション
作ってみてわかったのですが、なかなか複雑で難しい実装でした。ページが進むにつれて ...
の部分を左右に表示させたり、カーソルの位置を正しい状態に保つロジック部分が難しかったです。
ZOZOタウンのページネーションにもバグがあったりしたので、見た目よりも難しい事が分かります。
クリック促進
ユーザの視点が書籍購入ボタンに向かうように定期的に光るアニメーションが入っています。
少しだけ情報を見せる事でクリックを促進させるような作りにしました。NetflixのUIを参考にしました。
デザインを損ねずに情報をユーザに届ける
ユーザーの行動経路に自然と情報が入るような設計を心がけました。
視覚的に分かりやすいユーザー遷移図
グラフなどを用いる事で視覚的にも分かりやすいようにしています。
前職の同僚から嬉しいコメントを頂いた
サービスを作りきれたら、見て頂きたかったのでリンクを送信したら、すぐ見てたくさんコメントをくれました。本当にありがとうございます。こだわったUI、アニメーションの部分の動きが楽しいと言って頂けて嬉しかったです。
この辺りの改善が必要だと思ったので、ロードマップに追加して修正していこうと思います。
- ランキングのスコアリング表示をもう少し目立たせる
- 「書籍が紹介されていた記事」の文言の配置、情報を追ってくフローに合わせて
- 書籍詳細でのフッター写り込み
- パスワードのプレスホルダー非表示、入力誤認回避
- バリデーションエラーメッセージ用の幅、要素の高さが可変しないようにする
- プロフィールページ設定からアイコンに写真を設定できる事をユーザーに分かりやすいようにする
最後に
ここまで読んでくださってありがとうございます。記事にいいねがたくさんついて好評だったら、技術的な部分、アプリケーションをデプロイするまでの方法、JWT認証部分の作り方、Botの作り方、とかもう少し深掘りした記事を書けたらなと思います。
あとは転職活動中です。腰を据えて長く働ける所を探しています。
よろしくお願いします。
記事に関するコメント等は
🕊:Twitter 📺:Youtube 👨🏻💻:Github 😥:Stackoverflow
でも受け付けています。どこかにはいます。