はじめに
初めましての方は初めまして、土佐犬と申します。
今回は株式会社スタメンの開発部門のインターンシップに参加してきて、私が得られた経験、知識を備忘録としてここに残したいと思います。
なおこの記事は私が能動的に書いたものであり、スタメンの社員さんに指示されたわけでもなければ促されたわけでもないです
アジェンダは以下の通り
- オフィスについて
- 技術的に得られたもの
- 社内雰囲気
1. オフィスについて
まずはオフィスについてです
外観は以下
Google Mapより
正直言ってオフィスがめちゃくちゃオシャレでびっくりしました。
筆者自身そこまで多くの会社に行ったことがあるわけではありませんがダントツでオシャレです。
オフィスの内部も観葉植物や、木を生かしたデザインとなっており、会社!! と思わせることのない環境で働かせていただきました。
また徒歩5分圏内に複合施設があり、お昼ご飯などは社員の方と一緒にご飯を食べに行ったりしました。
2.技術的に得られたもの
次に技術的に得られたものです。
私は今回TUNAGというサービスのフロントエンドリプレイスの案件に関わらせていただきました。
その中で得られた知識としては以下の通りです
- Reactにおける
presentation
の役割 - PRの粒度
- テストの書き方
- DX(developer experience)の上げかた
Reactにおけるpresentation
の役割
まずはReactにおけるpresentationの役割
に関して
正直これが私の中で大きいものだと感じました。
以下が私のインターンに参加する前のコンポーネントの組み方です。
ページに表示するものをコンポーネントに分離し、そのコンポーネントの内部にmodel
を組み込んでいくという感じでした
次が私がインターンに参加した後に目標にしているコンポーネントの組み方です。
以前の組み方に比べ新しくチェックマーク、バツマーク、ロードマーク、スパナが追加されています。
これらのうちマークは正常系、異常系、特殊系(ローディング、空など)、スパナはhooks
を指しています。
では実際に私が以前書いていたコードがこちら(一部抜粋)
======================
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
として出力すべきでしょう
これらを教訓に先ほどのコードを書き直すといかになるでしょうか
=====================
// 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の上げかた
に関しては主に以下です
- 称賛の文化
- 教育とスキル向上
- 自動化
の三つです。
称賛の文化
スタメンにはスタカネ
と呼ばれる独自の称賛の文化があります。
スタカネ
スタメンの大切なカルチャーである相互賞賛を体現する「鐘」が全オフィスに設置されています。
成果を上げ、事業成長に貢献したメンバーが鐘を鳴らし、
拠点を超えて社員全員で讃え合います。
引用:スタカネ
そうなんです、個人が頑張ったこと、成果を会社全体に渡るように大きな鐘の音を鳴らすのです。
私自身インターン中にスタカネ
を鳴らさせていただき、自分の成果を正当に認めてもらうことが形として現れたことがとても嬉しかったです。
また開発プロダクトTUNAG
でもスタカネを鳴らしたこと
が大体的に報告されるため、ほとんどの社員の目につきます。
功績を埋もれさせずに大体的に公表するということは社員一人一人のモチベーションに直結することだと感じました。
教育とスキル向上
次に教育とスキル向上に関してです。
スタメン
では社内Slackにおけるtech
チャンネルや、テックブログなど技術的な情報のやり取りが活発に行われているイメージでした。
私が書く日報にも技術的なアドバイスをいただけることも多々ありました。
また、同じチームのメンターの方々が優しく、開発には直接関係しないこと(社内UIライブラリのバージョン管理方法
や知らないパッケージの説明
など)を快く教えていただけました。
他にも別チームの人とランチに行く機会があったのですが、そこでも自分が知らない知識を教えていただきました。
私が技術オタクな部分も関係しているかもしれませんが、気になることを何でもかんでも聞いたにも関わらず、嫌な顔せずに懇切丁寧に教えていただいた先輩には感謝しかないです。
自動化
最後に自動化に関してです。
私はよくNext.jsを使うのでvercel
にCICDを依存しています。
ですがスタメンでは独自のCICDが組んであり、快適な開発体験を送らせていただきました。
またファイルの自動生成に関してもバリエーションが多く、今後の開発に活かせることが多かったです。
これら3点が私がスタメンで感じたDXのあげかたでした。
3.社内雰囲気
最後に社内雰囲気についてです。
1.オフィスについて
でも取り上げましたが、何よりオフィス内がオシャレで会社という雰囲気がしないことが主観的にはとても良かったです。
またスタメンにはニューメンバーランチ
という,1日でも早く会社に馴染んでもらうための制度であり,経費で部署に関わらず他の社員さんと一緒にランチに行けるというものです。
私もインターン中に2回ほど利用させていただき、1回目はチームメンバーと、2回目は開発部の他チームの人や、ビジネスサイドの方とランチに行き、チームメンバーや他の社員さんとの交流を深めたり、技術トークをしたりなど、得られるものがたくさんありました。
またこの制度に限らず、お昼ご飯をオープンスペースで食べていると、他の社員さんやビジネスサイドのインターン生の方に話しかけていただいたりするなど、コミュニケーションを取る機会が多く見られました。
これをきっかけにビジネスサイドのインターンの方たちとランチに行ったりするといったこともありました。
またCTOの方がとても気さくで親しみやすいというのがとても印象的なイメージでした。
メンターの方や開発サイドのインターン生と飲み会に行く機会があったのですが、slackで募集するとCTOの方に参加していただけることになり、多少緊張はしましたが本当に気さくな方で緊張も最後には無くなっていました。
結論、社内雰囲気はめちゃくちゃ良かったです。
コミュニケーションが活発に行われている環境はおしゃべりな私にとってはありがたいことでした。
最後に
最後にはなりますが、面接でお世話になった方や、チームのメンバー、開発サイドやビジネスサイドのインターン生、ランチにいった方などたくさんの方にお世話になり、楽しいインターンシップを過ごさせていただきました。
この場をお借りして感謝申し上げます。
インターンが終わってしまうと直接的な繋がりは切れてしまいますが、エンジニア界隈は思ったよりも狭いのでどこかでまた再開できることを願ってこの記事の締めにしたいと思います。
スタメンの皆様のご健闘を心よりお祈りしています。