プログラマーから個人開発者へ。会社では書かなかったコードを、一人で書き始めた
長いことソフトウェア開発の仕事をしてきました。
昔は普通にコードを書いていました。設計して、実装して、テストして、レビューして、リリースする。それを何年も繰り返しているうちに、いつの間にか「コードを書くこと」よりも、「どういうシステムにするか」を考える時間のほうが増えていきました。
要件定義、アーキテクチャ、チーム間の調整、レビュー、会議。気がつくと、エディタを開いている時間より、Excelやドキュメントを開いている時間のほうが長い日もありました。
もちろん、それもソフトウェア開発の大切な仕事です。ただ、そんな日々を過ごしているうちに、少しずつこんなことを考えるようになりました。
「自分でゼロから作ってみたいな」
会社で作るソフトウェアと、個人で作るソフトウェアは別物だった
会社でソフトウェアを作る場合、基本的には役割分担があります。誰かが要件を決め、誰かがデザインを担当し、誰かがインフラを見て、誰かがテストをする。自分はその中の一部分を担当することが多いと思います。
ところが、個人開発になると話が変わります。仕様を決めるのも自分、UIを作るのも自分、サーバーを設定するのも自分、データベースを触るのも自分。料金プランを決めるのも、問い合わせに返信するのも、バグを直すのも全部自分です。さらに、ユーザーが増えなかったときには、「次に何をするべきか」を考えるのも自分になります。
つまり、
Developer
↓
Designer
↓
Product Manager
↓
DevOps
↓
Support
↓
Marketing
↓
またDeveloper
という、かなり不思議な一人チームになります。
始める前は「全部自分で決められるなんて楽しそうだ」と思っていました。実際、自由です。そして、思っていた以上に大変です。
一人でやると、Code Reviewまで自分になる
個人的に面白かったのが、Code Reviewです。
会社にいた頃は、コードを書いたら誰かにレビューしてもらうのが当たり前でした。ところが個人開発では、Pull Requestを作るのも自分、レビューするのも自分です。
const pullRequest = createPullRequest();
review(pullRequest);
if (reviewer === author) {
console.log("LGTM");
}
もちろん、実際のコードがこんなことになっているわけではありません。
ただ、感覚としてはかなり近いものがあります。「この設計、本当にこれでいいのか?」と考えながらコードを書いて、しばらくすると「まあ、動くからいいか」となってしまう。止めてくれる人がいないというのは、個人開発の便利なところでもあり、怖いところでもあります。
会社では誰かが「ちょっと待って、その実装で大丈夫?」と言ってくれます。個人開発では、その役割まで自分で持たなければなりません。
そしてAIに手を出した
そんなことを考えていた頃、AIの進化が一気に加速しました。特に興味を持ったのが、画像生成と動画生成です。
以前なら、ある程度クオリティの高い映像を作るには、カメラや編集ソフトだけではなく、素材の準備、撮影、編集など、多くの工程が必要でした。それが今では、1枚の画像とテキストから動画を生成できるようになっています。
しかも、新しいモデルが登場するたびに、以前なら難しかった表現が少しずつ現実的になっています。技術的にも、プロダクトとしても、個人開発者が挑戦するにはかなり面白い領域だと思いました。
そこで、AIによる画像・動画生成を扱うサービスを作り始めました。
「APIを呼べば終わり」だと思っていた
最初はかなり甘く考えていました。
「AIモデルのAPIを呼ぶだけなら、そんなに難しくないだろう」
実際、APIを呼ぶだけなら難しくありません。
const response = await fetch(API_URL, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
prompt,
imageUrl,
}),
});
ここで終われば楽なのですが、当然そんなに甘くありませんでした。
動画生成は、普通のWeb APIと少し違います。リクエストを送ってすぐ結果が返ってくるとは限らず、まずキューに入り、その後処理され、最終的に完成するまで待つ必要があります。
queued
↓
processing
↓
completed
もちろん、途中で失敗することもあります。タイムアウトすることもあります。APIによってレスポンス形式も異なり、生成にかかる時間も違えば、コストも違います。
さらに難しいのが、ユーザーから見たときの体験です。
ユーザーは単純に「生成」ボタンを押します。そこから数十秒、あるいはそれ以上待つことになります。その間に何も表示されなければ、「壊れたのかな?」と思われても不思議ではありません。
そこで、生成処理を非同期のJobとして扱う必要が出てきました。概念的には、たとえばこんな感じです。
type GenerationStatus =
| "queued"
| "processing"
| "completed"
| "failed";
async function processGeneration(jobId: string) {
const job = await getJob(jobId);
if (!job) {
throw new Error("Job not found");
}
await updateJob(jobId, {
status: "processing",
});
try {
const result = await generateVideo(job);
await updateJob(jobId, {
status: "completed",
resultUrl: result.url,
});
} catch (error) {
await updateJob(jobId, {
status: "failed",
errorMessage: String(error),
});
}
}
コードだけを見ると、そんなに難しそうには見えません。
ところが、実際にはすぐ次の問題が出てきます。
「このJobが二回実行されたらどうなる?」
すると今度は、冪等性について考えることになります。
さらに、
「生成リクエストが二重送信されたら?」
「外部APIは成功したのに、DB更新だけ失敗したら?」
「処理途中でサーバーが落ちたら?」
といった問題まで考える必要があります。
一つ解決すると、その先からまた新しい問題が出てくる。個人開発では、この繰り返しがかなり多いです。
AIサービスで一番怖いのは、バグよりコストだった
普通のWebサービスなら、「この処理が10回実行されても大丈夫かな」と考えることが多いと思います。
AI生成では、少し考え方が変わります。
「これが10回実行されたら、いくらかかるんだ?」
特に動画生成は、モデルによって消費量やコストが大きく違います。そのため、単純に生成処理を動かすだけではなく、アプリケーション側でもユーザーの使用量を管理する必要があります。
たとえば概念的には、こんな処理になります。
async function createGeneration(userId: string, model: Model) {
const cost = getCreditCost(model);
const balance = await getCreditBalance(userId);
if (balance < cost) {
throw new Error("Insufficient credits");
}
await reserveCredits(userId, cost);
return createJob({
userId,
model,
});
}
ここでも終わりではありません。
reserveCredits() が成功したあとにJob作成が失敗したらどうするのか。同じリクエストが二回来たら、クレジットを二重に消費しないか。外部APIの失敗時にクレジットをどう扱うのか。
普通のWebアプリでも難しい部分ですが、AI生成では「一回のミスが、そのままコストになる」という現実があります。
このあたりは、実際に作り始めるまであまり深く考えていませんでした。
AIプロダクトを作るというのは、単純にAIモデルを呼び出すことではありません。AIモデルとユーザーの間に、どんな仕組みを作るのか。その部分が、想像以上に大きな仕事でした。
一番難しかったのは、コードではなかった
技術的な問題は、時間をかけて調べれば、ある程度は解決できます。
本当に難しかったのは、
「何を作ればユーザーが使ってくれるのか」
という問題でした。
エンジニアは、何か問題があるとコードを書きたくなります。バグなら修正する。遅ければ高速化する。UIが悪ければ改善する。
でも、「そもそも誰も必要としていない」という問題だけは、コードでは解決できません。
これは個人開発を始めてから何度も思い知らされました。
完璧にしてから公開する、をやめた
会社で開発していると、「ちゃんと作ってから出したい」と考えがちです。
個人開発を始めた頃の自分も同じでした。
UIを修正して、エラー処理を追加して、またUIを修正する。少し気になるところがあれば直す。もう少し良くできそうなら、また直す。
そして、ある日ふと気づきます。
「これ、いつ公開するんだ?」
そこで、考え方を変えました。
まず公開する。
実際に使ってもらう。
問題があれば直す。
必要なら作り直す。
Idea
↓
Build
↓
Release
↓
User
↓
Feedback
↓
Fix
↓
Repeat
最初から正解を作ろうとするよりも、実際のユーザーからの反応を見ながら次の一手を決めたほうが、個人開発には向いていると感じるようになりました。
もちろん、公開したあとにバグが見つかることもあります。
「公開前に気づけよ」と自分で思うこともあります。
でも、それも含めて個人開発なのかもしれません。
プログラマーから、個人開発者へ
振り返ってみると、僕は突然「個人開発者」になったわけではありません。
これまでやってきたことが、そのまま別の形でつながってきた感じです。
これまで学んできたプログラミング、システム設計、障害対応、ユーザーの要求を理解すること。そして、長い間ソフトウェアに関わってきた経験。
ただ、一つだけ大きく変わりました。
以前は、
「決められた問題を、どうやって解決するか」
を考えることが多かった。
今は、
「そもそも、どの問題を解くべきなのか」
から考えるようになりました。
この違いは、思っていた以上に大きいです。
そして、かなり面白いです。
もちろん、まだまだ失敗します。
たぶん明日も何か壊します。
「ここを直したら、なぜか別の場所が壊れた」ということも普通にあります。
深夜にログを見ながら、
Why is this happening?
と独り言を言っていることもあります。
それでも、以前とは少し違う感覚があります。
今は、自分で問題を見つけて、自分で考えて、自分で作って、それを実際に世の中に出してみることができます。
まだ旅の途中です。
そして、たぶん個人開発には「完成」というものがないのかもしれません。
とりあえず、今日もコードを書きます。
その前に、まずログを確認します。
npm run dev
今日も何も壊れませんように。
今作っているもの
ここまで読んでいただき、ありがとうございます。
今、僕が個人開発で作っているのが VioEvo というAI画像・動画生成サービスです。テキストや画像からAI画像・AI動画を生成したり、複数のAIモデルを試したりできるようにしています。
まだ改善したいところもたくさんありますし、個人開発だからこそ見えていない問題もあると思います。
もしこの記事を読んで、個人開発やAIプロダクト開発について何か気づいたことがあれば、ぜひコメントで教えてください。
「こういう設計のほうがいい」「ここは別の方法がある」「その考え方は違うと思う」など、技術的な指摘も大歓迎です。
まだまだ勉強中なので、いろいろとご指導いただけると嬉しいです。
VioEvoはこちらです。