株式会社スタメンのインターンシップに参加してきました！

# はじめに
初めましての方は初めまして、土佐犬と申します。
今回は[株式会社スタメン](https://stmn.co.jp/)の開発部門のインターンシップに参加してきて、私が得られた経験、知識を備忘録としてここに残したいと思います。
なおこの記事は私が能動的に書いたものであり、スタメンの社員さんに指示されたわけでもなければ促されたわけでもないです

アジェンダは以下の通り
1. オフィスについて
2. 技術的に得られたもの
3. 社内雰囲気

# 1. オフィスについて
まずはオフィスについてです
外観は以下
![2022-05-13.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3230849/35f95c30-78a6-9054-dc3b-e940863ab611.jpeg)
`Google Mapより`

正直言ってオフィスがめちゃくちゃオシャレでびっくりしました。
筆者自身そこまで多くの会社に行ったことがあるわけではありませんがダントツでオシャレです。
オフィスの内部も観葉植物や、木を生かしたデザインとなっており、**会社！！** と思わせることのない環境で働かせていただきました。

また徒歩5分圏内に複合施設があり、お昼ご飯などは社員の方と一緒にご飯を食べに行ったりしました。

# 2.技術的に得られたもの

次に技術的に得られたものです。
私は今回[TUNAG](https://biz.tunag.jp/)というサービスの**フロントエンドリプレイス**の案件に関わらせていただきました。
その中で得られた知識としては以下の通りです
- **Reactにおける`presentation`の役割**
- **PRの粒度**
- **テストの書き方**
- **DX(developer experience)の上げかた**

## Reactにおける`presentation`の役割
まずは`Reactにおけるpresentationの役割`に関して
正直これが私の中で大きいものだと感じました。
以下が私のインターンに参加する前のコンポーネントの組み方です。

![スクリーンショット 2023-08-23 22.33.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3230849/d9b342a4-3d0f-c850-1a37-f78bc78ef1fa.png)

ページに表示するものをコンポーネントに分離し、そのコンポーネントの内部に`model`を組み込んでいくという感じでした

次が私がインターンに参加した後に目標にしているコンポーネントの組み方です。
![スクリーンショット 2023-08-23 22.33.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3230849/25fc094e-d70e-ca5a-1eb6-7f275aaa21aa.png)


以前の組み方に比べ新しくチェックマーク、バツマーク、ロードマーク、スパナが追加されています。
これらのうちマークは**正常系**、**異常系**、**特殊系**(ローディング、空など)、スパナは`hooks`を指しています。

では実際に私が以前書いていたコードがこちら(一部抜粋)
```tsx
======================
const CategoryTop = () => {
    const router = useRouter();
    const { category } = router.query;
    if (
        !["game", "hack", "music", "cg2d", "cg3d"].includes(
            typeof category == "undefined"
                ? ""
                : typeof category == "string"
                ? category
                : category[0]
        )
    ) {
        if (router.isReady) {
            router.push("/web/artifact");
        }
    }
    const { works, refetch, isLoading, isContinue } = useGetWorks(
        `${process.env.NEXT_PUBLIC_BACKEND_API}/api/v1/hoge`,
        { tag_names: "hoge" }
    );
    useTopLoading({ isLoading, message: "getting" });
    if (isLoading) {
        return <></>;
    }
    return (
        <Box component="div">
            <Button
                onClick={() => router.back()}
                sx={{
======================
```


こうしてみるととてもひどいですね。
まず第一に異常系の対応がないです。
エラー発生時はどうする予定だったんでしょうか

次に特殊系(ローディング)が2パターンあります。
これはグローバルなコンポーネントとして使用しているloading画面があるためその表示と正常系が空を返しています。

また正常系とcontainerが同一関数内に定義されています。

さらには`hooks`をcontainer内で複数呼び出されています。

先ほどのイラストであげた**チェックマーク**,**バツマーク**,**ロードマーク**,**スパナ×n**が一つの箱にごっちゃごちゃに入ってる状況です。

自分が読む分には読めればいいでしょう、
しかしチームメンバーやレビュアーがこれを見ても解読になかなか時間がかかるでしょう。

これらが積み重なっていくとだんだん無視できない技術負債として現れてきます。

ここで学べることは**関心**、**責務**をしっかり分割するということです。
`Header`を呼び出すのであれば`Header`における`正常系`,`異常系`,`特殊系`,`ロジック`を分割したのち、それらをまとめる**container**を`Header`として出力すべきでしょう
これらを教訓に先ほどのコードを書き直すといかになるでしょうか

```tsx
=====================
// login
const castQuery = (query:string|string[]|undefined):string[]|null => {
    if (!query){
        return null
    }
    return typeof category == "string"? [category] : category
}
======================
// hooks
const useGetWorks= ()=>{
    const router = useRouter();
    const category = castQuery(router.query);
    const isRedirect = category !== null || !["game", "hack", "music", "cg2d", "cg3d"].includes(category[0])
    const { works, refetch, isLoading, isContinue } = useGetWorks(
        `${process.env.NEXT_PUBLIC_BACKEND_API}/api/v1/hoge`,
        { tag_names: "hoge" }
    );
    return {works, refetch, isLoading, isError, isContinue,isRedirect }
}

=========================
// container
const CategoryTop = () => {
    const { works, refetch, isLoading, isError, isContinue,isRedirect } = useGetWorks();
    if (isLoading) {
        return <CategoryTopLoadingPresentation />;
    }
    if (isError){
        return <CategoryTopErrorPresentation />;
    }
    if (isRedirect){
        router.push("/web/artifact");
    }
    return  <CategoryTopPresentation /> ;
======================
```

それぞれ`logic`,`hooks`,`container`に分けられてます。
`castQuery`は色々なところで使いそうなのでグローバルなロジックとして作ってもいいかもしれませんね。


## PRの粒度
次に`PRの粒度`に関してです。
これは前章に通ずるところもあります。
私は今まで基本的には1PRにつき1コンポーネントといった感じでした。
しかし大きなコンポーネントになるとどうでしょう
`正常系`,`異常系`,`特殊系`,`container`,`hooks`,`stories`,`logic`,`test`,`mock`,`...正常系のItem`
最低でも9ファイルくらいは作らなければいけないでしょう。
体感的には1ファイルにつき100行を超えると私は読みにくいと感じることがあるので仮に1ファイル100行あったとして全部で900行のコード、1回のPRにしては大きすぎますし、何よりレビュアーの負担になり、どこかで見落としがあるかもしれません。
これらを避けるためにも他のファイルに依存していない変更ごとにわけてPRをするべきだと学びました。
具体的に分けるとしたら以下になります
- `正常系`の実装(`stories`)
- `異常系`の実装(`stories`)
- `特殊系`の実装(`stories`)
- `mock`の実装
- `hooks`の作成、その`test`の実装
- `logic`の作成、必要であれば`test`の実装
- `container`で繋ぎ込み

こうすることでレビュアーに負担がかかることが少なくなり、より安全なコードの実装ができます。
正直な話として思ったことは、細かすぎても悪いことはないということです。
多くてバグが出る可能性があるよりも、細かすぎてバグが出ない方がサービス実装という面においては重要です。

## テストの書き方
次に`test`の書き方についてです。
私は開発の多くをハッカソンで行うため、あまりテストコードを書くことがなく、フロントエンドのコードを書くのは今回のインターンが初めてでした。
`jest`を使うというのはなんとなく知っていたことなのですが、実際に書いたことはなく、貴重な経験となりました。
単体テストにおいて重要なことは**必要十分を満たすテストを書く**ということでした。

テストにおける必要条件と十分条件に関して
- 必要条件とはその関数が取りうる特定の引数に対して、特定の変数を返すこと
- 十分条件とはその関数が想定しない引数に対して、何かしらの異常系を返すこと

これは他のインターン生とメンターの人の会話を聞いた内容なのですが、
単体テストで書くのは主に**その関数が行う責務を網羅する**ということです。

具体的にはAというコンポーネントにB、Cというコンポーネントが含まれていたときに
Aというコンポーネントで実装すべきテストは
**AがBとCを正しく表示できること**
であり、BとCの内容はテストする必要がないということです。
それを実装すべきなのはBのテスト,Cのテストであり,Aの責務ではありません。



## DX(`developer experience`)の上げかた
最後に`DX(developer experience)の上げかた`に関してです
このインターンを通して感じた`DXの上げかた`に関しては主に以下です
- 称賛の文化
- 教育とスキル向上
- 自動化

の三つです。

### 称賛の文化
スタメンには`スタカネ`と呼ばれる独自の称賛の文化があります。
>スタカネ
スタメンの大切なカルチャーである相互賞賛を体現する「鐘」が全オフィスに設置されています。
成果を上げ、事業成長に貢献したメンバーが鐘を鳴らし、
拠点を超えて社員全員で讃え合います。

引用:[スタカネ](https://stmn.co.jp/about#system:~:text=%E5%87%BA%E3%81%97%E3%81%A6%E3%81%84%E3%81%BE%E3%81%99%E3%80%82-,%E3%82%B9%E3%82%BF%E3%82%AB%E3%83%8D,-%E3%82%B9%E3%82%BF%E3%83%A1%E3%83%B3%E3%81%AE%E5%A4%A7%E5%88%87)

そうなんです、個人が頑張ったこと、成果を会社全体に渡るように大きな鐘の音を鳴らすのです。
私自身インターン中に`スタカネ`を鳴らさせていただき、自分の成果を正当に認めてもらうことが形として現れたことがとても嬉しかったです。
また開発プロダクト`TUNAG`でも`スタカネを鳴らしたこと`が大体的に報告されるため、ほとんどの社員の目につきます。
功績を埋もれさせずに大体的に公表するということは社員一人一人のモチベーションに直結することだと感じました。

### 教育とスキル向上
次に教育とスキル向上に関してです。
`スタメン`では社内Slackにおける`tech`チャンネルや、テックブログなど技術的な情報のやり取りが活発に行われているイメージでした。
私が書く日報にも技術的なアドバイスをいただけることも多々ありました。
また、同じチームのメンターの方々が優しく、開発には直接関係しないこと(`社内UIライブラリのバージョン管理方法`や`知らないパッケージの説明`など)を快く教えていただけました。
他にも別チームの人とランチに行く機会があったのですが、そこでも自分が知らない知識を教えていただきました。
私が技術オタクな部分も関係しているかもしれませんが、気になることを何でもかんでも聞いたにも関わらず、嫌な顔せずに懇切丁寧に教えていただいた先輩には感謝しかないです。

### 自動化
最後に自動化に関してです。
私はよくNext.jsを使うので`vercel`にCICDを依存しています。
ですがスタメンでは独自のCICDが組んであり、快適な開発体験を送らせていただきました。
またファイルの自動生成に関してもバリエーションが多く、今後の開発に活かせることが多かったです。




これら3点が私がスタメンで感じたDXのあげかたでした。


# 3.社内雰囲気 
最後に社内雰囲気についてです。
`1.オフィスについて`でも取り上げましたが、何よりオフィス内がオシャレで**会社**という雰囲気がしないことが主観的にはとても良かったです。

またスタメンには`ニューメンバーランチ`という,1日でも早く会社に馴染んでもらうための制度であり,経費で部署に関わらず他の社員さんと一緒にランチに行けるというものです。
私もインターン中に2回ほど利用させていただき、1回目はチームメンバーと、2回目は開発部の他チームの人や、ビジネスサイドの方とランチに行き、**チームメンバーや他の社員さんとの交流**を深めたり、**技術トーク**をしたりなど、得られるものがたくさんありました。

またこの制度に限らず、お昼ご飯をオープンスペースで食べていると、他の社員さんやビジネスサイドのインターン生の方に話しかけていただいたりするなど、コミュニケーションを取る機会が多く見られました。
これをきっかけにビジネスサイドのインターンの方たちとランチに行ったりするといったこともありました。

また**CTOの方がとても気さくで親しみやすい**というのがとても印象的なイメージでした。
メンターの方や開発サイドのインターン生と飲み会に行く機会があったのですが、slackで募集するとCTOの方に参加していただけることになり、多少緊張はしましたが本当に気さくな方で緊張も最後には無くなっていました。

結論、社内雰囲気はめちゃくちゃ良かったです。
コミュニケーションが活発に行われている環境はおしゃべりな私にとってはありがたいことでした。

# 最後に
最後にはなりますが、面接でお世話になった方や、チームのメンバー、開発サイドやビジネスサイドのインターン生、ランチにいった方などたくさんの方にお世話になり、楽しいインターンシップを過ごさせていただきました。
この場をお借りして感謝申し上げます。
インターンが終わってしまうと直接的な繋がりは切れてしまいますが、エンジニア界隈は思ったよりも狭いのでどこかでまた再開できることを願ってこの記事の締めにしたいと思います。
スタメンの皆様のご健闘を心よりお祈りしています。