はじめに
Booost!!! Excite Internship2025に9月から1ヶ月の間参加させていただきました、戸井と申します。ヘルスケア事業部で開発を行ったので、インターンの振り返りをブログ記事としてまとめようと思います。
自己紹介
情報系の大学院で、ソフトウェア工学(特にLLMを用いたコード生成)の研究を行っています。趣味は漫画や本を読むこと、食べること、ゲーム、体を動かすことです。 最近はAIエージェントに興味があり、エージェントをシステムに導入した個人開発をやろうかなと考えてます。
個人開発やチーム開発での開発経験はありますが、完全に実務での開発は初めてです。
やったこと
ヘルスケア事業部でお悩み相談室の開発に参加させていただきました。具体的には、以下のタスクを行いました。
- 新規登録ページにLINEアカウントによる登録ボタンを設置
- 記事一覧ページの移植
技術面での学び
大規模なコード量を持つプロジェクトの開発
大規模なコード量を持つ「お悩み相談室」の開発に携わる中で、コードの流れや設計思想を理解する力が身につきました。
お悩み相談室はクリーンアーキテクチャで設計されていて、開発を始めた際はどういう処理が行われているか、全く理解できませんでした。 そこで、View、Controller、Reposiotryといった各責務の流れ通りにコードを読み進めることから始めました。その結果、処理の流れや、コードで何が行われているかよく理解でき、責務についての理解も深まりました。
大規模コードを持つプロジェクトはコード量が多いので、最初にコードを読んで流れを理解することが重要だと実感しました。
責務の分離への理解
Model、Controller、Viewなどのアーキテクチャ概念は知っていましたが、インターンでより責務分離について理解することができました。
例えば、以下のViewでは、ViewModelで定義しているisLoggined関数を使用して、ログインしている際に特定のバナーを表示しています。しかしこのコード、責務の分離という観点では適切ではありません。
final class FunctionIndexGetController extends Controller
{
public function __construct()
{
}
public function __invoke(Request $request): View | RedirectResponse
{
// ViewModelを取得
$vm = new FunctionIndexViewModel();
return view('pages.function.index', ['vm' => $vm]);
}
}
final readonly class FunctionIndexViewModel
{
public function __construct(
private bool $loggedInStatus,
)
{
}
public function isLoggedIn(): bool
{
return $this->loggedInStatus;
}
}
// View @if ($vm->isLoggedIn() === true) <div class="banner"> ... </div> @endif
なぜなら、「ログイン状態ではバナーを表示する」というロジックを、画面の表示を担当するViewに持たせているからです。 ロジックはController(ViewModel)に持たせるべきであり、改良すると以下のようになります。
final readonly class FunctionIndexViewModel
{
public function __construct(
private bool $loggedInStatus,
)
{
}
public function isLoggedIn(): bool
{
return $this->loggedInStatus;
}
public function shouldShowBanner(): bool
{
return $this->isLoggedIn() === true;
}
}
@if ($vm->shouldShowBanner === true) <div class="banner"> ... </div> @endif
ViewModelでログイン状態によるバナー表示判定を行い、それをControllerを経由してViewに渡します。 Viewでは、isShowBannerがtrueなら、バナーを表示します。些細な変更ですが、このようにすることでViewに表示処理のみを担当させることができます.
依存性の注入と逆転による責務分離
僕が参加したLaravelプロジェクトでは、InterfaceやUseCaseを採用していました。これらのコードを実装することで、依存性の注入と逆転について、深く理解できました。
例として、以下のRepository、UseCaseのコードでは、どちらもInterfaceを参照しています。
interface FunctionRepositoryInterface
{
public function get(): FunctionRepositoryGetOutput;
}
final class FunctionRepository implements FunctionRepositoryInterface
{
public function __construct()
{
}
/**
* MySQLから内容を取得する
*/
public function get(): FunctionRepositoryGetOutput
{
~~~
}
}
final readonly class FunctionGetUseCase implements FunctionGetUseCaseInterface
{
public function __construct(
// 依存性を注入
private FunctionRepositoryInterface $functionListRepository,
) {
}
// 様々な実装
}
RepositoryではInterfaceを参照することで実装を厳密にでき、UseCaseではInterfaceを介することで、外部から依存性(Repository)が注入されています。 また、InterFaceを利用することで、
- UseCase(高レベル)=> Repository(低レベル)
の依存関係から、
- UseCase(高レベル)=> Interface <= Repository(低レベル)
という依存関係に変化します。これにより、高 => 低の依存関係が逆転します。
依存性の注入と逆転を利用することで、オブジェクト間の依存関係が疎になり、UseCaseはRepositoryの実装から完全に独立できます。 例えば、RepositoryでMySQLではなくSQliteからデータを取得するように変更したい場合、Repositoryを修正するだけで済みます。
テストコードの書き方
テストコードの実装は、正直一番難しかったです。 RepositoryやControllerのテストを行いましたが、そもそもどのようなテストを書けばいいのか、失敗テストは書くべきなのかといったように、考慮することが多くありました(メンターさんからも、テストが開発で一番難しいかも、と言われました)。
このような実装面以外にも、プロジェクトの運用的な側面でも意識しなければいけないことがありました。
例えば、Repositoryのテストでは、Repositoryで取得した内容とテストの中で取得した内容が一致するかのテストを行なっていましたが、取得内容が変更される可能性のあるデータの場合、将来的にテストが機能しなくなる可能性がある、という指摘をいただきました。この経験から、運用面についてもエンジニアは考えなければいけないことがあるということを学びました。
テストについては上手くできないことが多かったので、今後の課題と感じています。
実務・チーム開発でのGit操作
チーム開発、特にPull Requestを作成する際に、以下のことに気をつけました。
- 機能単位でコミットを行う
- コミットメッセージにprefixをつけて構造化する
- コミット前に静的解析を行い、コードの保守性などをチェックする
- --amendでのコミットメッセージ変更は、pushした後は行わない
- PRでコンフリクトが発生したら、基本的にPR上で解決する(ローカルで解決しようとして、さらに面倒になった経験から)
コミュニケーション・業務遂行面での学び
相談内容の構造化
相談内容の構造化を行うことで、質問する側・答える側の両方にメリットがあると実感しました。 インターン中にどうしても詰まっている箇所について質問がある際は、以下のようなテンプレートを使用していました。
## やったこと ~~~ ## わからない・詰まっている箇所 ~~~
見出しをつけて内容をわかりやすくすることで、答える側の負担が減るように意識しました。 また、内容を言語化することで自分が今詰まっている箇所について、より理解できるようになったと思います。
早めに頼ることの大切さ
早めに人を頼ることで,早く問題を解決できると実感しました.
インターン開始時の環境構築で、なぜかdockerでプロジェクトが開始できないというエラーが発生してしまいました。 個人開発ではAIに頼りまくっているので、同じようにGemeniを使おうと思いましたが、せっかくのインターンなので、メンターさんなどに聞くことにしました。 相談した結果、プロジェクト内のあるファイルが環境構築コマンドで作成されていないことが判明しました。 このエラーは、自分だけでは絶対に気づけないものだったので、早めに相談して良かったと思いました。 早めに相談するというのは当たり前のことに思われますが、実務で実感できたのはいい経験でした。
考えることの大切さ
開発の際に、しっかり考えることも大事だと学べました.「早めに頼る」ことと矛盾しているようですが,しっかり考えることで物事への理解がより深まるだけでなく,人に頼る時の言語化も上手くなります。 具体的にどう考えていたかというと、例えば上記の質問テンプレートのように、内容を細分化して把握する、原因を細かく遡って追求する、といったことを意識していました。
自分で考えることと人に頼ることのバランスですが、個人的には30 ~ 1時間ほど解決しないようであれば、人に聞いたほうが早いと思います。
若手は人やAIをどんどん頼ろう
僕がインターン生というのもありますが、それに限らず若手は積極的に頼っていくべきだと思います。 人を頼ることで成長スピードが早くなるだけでなく、しっかりとしたコミュニケーションを行うことで、信頼関係を築くこともできます。 AIについては賛否両論ありますが、これも積極的に使ったほうがいいと思います。一見大したことのない、しかし時間のかかりそうなエラーでも、AIを使えば解決できることもあります。
個人的には、AIを使ってもわからなかったら、人を頼るべきだと思います。
まとめ
1ヶ月間というインターン期間は、始めこそ時間がたっぷりあると思っていましたが、終わってみるとあっという間でした。 アーキテクチャに関する理解や、読みやすいコードの書き方など、技術・コミュニケーションともに様々なことを吸収できて、とても楽しかったです。 実際の開発業務に携わった経験は、非常に貴重な経験として、今後の糧とさせていただきます。
また、インターン後半では、メンターさんから就活に役立つような業界情報を教えていただき、今後就活を進めるにあたって非常に参考になりました。
メンターさんを含むヘルスケア事業部の皆さん、そしてエキサイト株式会社の皆さん、インターン生として受け入れていただき、本当にありがとうございました!