かなで技術日誌

プログラミングやエンジニアリング周りについて

主なアウトプットはScrapboxObsidianにまとめてます。

ソフトウェアエンジニアになって約4年半の振り返り

彼女に書けと言われたので書きます。

そんなに推敲していないし無駄に長いので本当に暇な人だけ読んだください。

お前誰よ

インターネットではかなでという名前で細々とフリーランスでエンジニアをしています。

twitter

かなで (@py_kanade0404) / Twitter

GitHub

github.com

エンジニアとして職を得た2018年4月から4年と8ヶ月経ちました。同じぐらいの職歴の方の参考になれば幸いです。

ここに書いてある内容はresumeにもありますが、resumeに記載がない内容がたくさんあります。

github.com

結論

色々触ってできることは増えたのは良かったが、プログラミング技術それ自体や一般的な業務の進め方、相変わらずのコミュニケーションには課題感がある。

これからは実装の速度・質を高めながら、仕事の進め方を改善していき、よりその企業のビジネスに関われるようになっていきたい。

これまでの経歴

2016年~2017年

F欄大卒後、このまま就職してもブラック企業しかなさそうだなという感覚と仕事したくないというお気持ち、当時経済学(マクロの成長理論や都市・地域経済学)に興味があり院進したかったのでフリーターでパチ屋でバイトしながら他の時間で勉強をする。パチンコには興味ない。

F欄というのは2ch学歴板の「早稲田はF欄」みたい意味ではなく、真のF欄です。帝京平成大学とかネタにされるだけマシです。

2017年~2018年

院試受験

フリーターを辞めて試験勉強に集中する。TOEFLがゴミみたいな点数しか取れず、泣く泣くいくつかの研究科を諦めることに。 秋に旧帝大の研究科を3つ受験した。2つは1次試験落ち、K大は1次試験は通過したものの2次試験で落ちる。 ちなみに後日2次試験の成績を申請すれば郵送してくれるんですが、順位が定員+1で本当にギリギリで落ちてた。悔しい。

実はプランBがあったのでそちらの移行することになった。それがエンジニアへの転職だった。

エンジニア転職に向けて

勉強の息抜きに「Rによるやさしい統計学」を読んでRstudioを起動して遊んでいた。Rはもうほとんど忘れた。

www.amazon.co.jp

そこからプログラミング自体にも興味を持ち、数値計算や行列計算、機械学習関連のライブラリが充実しているという情報からPythonを触ることに。 その体験から、もし試験落ちたらエンジニアも面白そうだなと漠然と考えていた。この中にエンジニアが今ホットだとか給料が上がってるとかフリーランスとかは一切考慮になく、ただ面白そうという好奇心が根底にあったのが今に繋がっているんだと思う。

不合格が確定してからはprogateで勉強していた。当時は全部無料だった気がするのでとてもありがたかった。

prog-8.com

試験に落ちてからは就活をするために株式会社UZUZのウズウズカレッジに行くことになった。お金は結構消えてしまったので有料のプログラミングスクールに行くつもりはなかったし、プログラミング自体というよりはとにかく職を得ないといけないのでこういう選択になった。 uzuz-college.jp

最初の面談で初台に行き、カウンセラーの話を聞くことになった。プログラミングコースではJavaをやると聞いたので、帰路の途中で池袋ジュンク堂に行って「スッキリわかるJava入門」を実践編とあわせて購入してすぐ読んだ。

www.amazon.co.jp

www.amazon.co.jp

ここでJavaSQLとざっくりだけどHTML/CSSを学んで自分でWebアプリケーションをapacheで動かすことをやる。自分は2chみたいな掲示板を作ってみた。mavenとかgradleとか一切使ってないし、サーブレットなので今となってはレガシーだとは思うけど、よくわからないが動くからとRails使うよりは良いと思う。

その実装はGitHubにある。色々ツッコミどころはあるけど供養。

github.com

自分が受講した講義は多分これだと思う。多分、というのは当時は全部オンサイトでやっていたので動画を見ていない。動画を見るよりもやっぱりオンサイトの方が楽しいとは思うものの、講師としては拘束時間がきついし動画の方がスケールするので悩ましい。

www.udemy.com

ここまでを1ヶ月半でやって、移行は就活対策をひたすらやる。模擬面接をずっとやってたり志望動機を精査したりとプログラミングはそんなにやってなかったけど、自分はVagrant+VirtualBoxUbuntu環境作ってそこでPythonを入れてDjangoを動かしてHerokuにデプロイするとかをしてた。

就活は自分の場合は結構難航していた。自分は自慢ではないが当時の同期や他のメンバーと比較すると割と実力はあったとは思うが、初心者に技術とか求められないので他の側面で落ちたっぽい。なんとか1月だったと思うけど内定をもらって、移行はウズウズカレッジでチューター兼内部の仕事を手伝うので業務委託で2ヶ月ほど仕事をしていた。

3ヶ月で退職

2018年4月になり晴れてエンジニアとして入社することとなる。

会社はSESと受託のハイブリットという感じでかなり小さい。入って早々に実際の開発案件の実装をほぼ自分一人ですることになった。要件自体は難しくないけど1ヶ月後に納品する必要があり、知らないことだらけでまあまあ大変だった。

技術スタックとしてはC#PostgreSQL?とHTML/CSSでMyBatisを初めて驚いた記憶がある。Djangoは触ったことがあるがなんかXMLSQLが書いてあって何が何だかという状態だった。 全然うまく実装できなくて業務委託の中国人の先輩にめっちゃ聞いて1/4か1/3ぐらい実装してもらったかもしれない。色々とひどい実装だった気がするけど、なんとか必要な実装は終えてIIS上で動くようにできた。IISで動かすのはメンターの社員に「これ見てやってみて」とデプロイの説明書をもらったはいいが、日本語のはずなのに何を言っているのか全くわからず、これもほぼ中国人の先輩にやってもらった。

そんなこんなで最初の一ヶ月で体調は悪くなるし契約書周りで(非は向こうにあると言える)トラブルがあり、入社一ヶ月過ぎたところで転職エージェントに声をかけて転職準備をするようになった。

当然、ほぼ未経験なので転職はきついというのは共通認識としてあったが、実際は特に苦労することはなく最初に面接した会社にそのまま合格してそのまま6月を持ってスピード退職することとなった。

この退職はモダンな環境がーとかやっていることのレベルが低いだの自分が生意気だったところもあったので、自分としても反省する点が多々あった。まあそれでも辞めるが。

下積み?時代

中小SIer時代

こうしてSESをやっている企業の中ではかなり大きなIT(人売り?)企業に入ることになる。 最初の現場は登戸まで行かないといけずまあまあしんどかった。ちなみに面談でDocker使ってますか?って聞いたら「Dockerってなんですか?」と聞かれたのでこれが日本の中小SIerの現状なんだと思った。

ここでは製薬企業の試薬検査の管理パッケージの開発をしていた。 最初は帳票出力でVBAを初めて触った。なんだかよくわからなかったけど普通に実装できたので言語としては簡単なのだと思う。PythonとかJavaとかC#を使った後だとあまりにも言語として機能が貧弱すぎるて1ヶ月だけ触ったけどもう2度と触りたくないと思う。

結論から言うとここも3ヶ月で離任することとなった。最終的にはストレスによる腹痛が酷すぎてベッドから起き上がれなかったり、電車には乗れるけど手前の駅でギブアップしてそれ以上先にいけなくなるようになった。理由としては色々あるんですが

  • 要望が一切反映されていない

当時はWebをやりたいと営業には言っており、面談した時もかなり微妙そうな直感があったのでここじゃないとこにしたいですと言ったが、基本的には面談なので自分に決定権というか選択肢が無かった。もしかしたらこの業界だと当たり前なのかもしれないが、それすらも知らなかったし事前に説明は無かった。

離任時の話になるが体調が酷すぎて現場を離れたいしこのままここで仕事することはできないという話をしてもじゃあ体調が良くなったら出勤しましょう(意訳)みたいなことしか言わなかったので営業の人の頭が悪かったと思う。

自分が半分キレてやっと離任の話になり、なんとか離任する運びになった。

  • 開発環境

データベースはOracleでしたが、複数のテーブルのカラム名が特に意味を持たないID名でnullableなvarcharで統一されていた。これは複数の会社の要望に柔軟に合わせるために全てnullableにして、それぞれのカラムの意味はエクセルで管理して都度そのエクセルを参照するというものだった。SIerがよく言うの「お客様に寄り添う(笑)」の結果の賜物であり、ただ要望を横流ししてそのまま実装しているだけでプロダクトマネジメントのかけらもない。 これ以上詳細は書けないですが、今となっても本当に色々終わっていた現場だったなと思います。

ただ良かった点としては実装前にテストケースを自分で考えてレビューしてもらう工程があり、そこでソフトウェアテストについて色々調べたのが今に繋がっている気がする。

日立系列会社時代

そして営業が変わり、次の現場は日立のとある孫会社になった。この時の実質の上司の人はなんだかんだいい人だったなと思う。実際9ヶ月はいたのでエクセルスクショテスト以外はそこまで悪くはないと思う。ゆるゆるやりたい人ならあの現場はいいと思います。製作所の人もいましたが皆さん優秀でした。

ここではJava8とAngularJS1.x、HTML/CSSPostgreSQLで社内Webアプリケーションを開発していた。具体的な年齢は聞いてなかったけど齢70を超えているであろう協力会社のおじいちゃんエンジニアがいて、めっちゃシステムに詳しくてプロパーの偉い人が挨拶に来てた。そのおじいちゃんに言われて画面描画完了まで10秒かかるところを3秒以内に改善したりテストでかなりやばいバグを発見したりと一応進捗はしていた。

ゆるゆるやるならこれでもいいとは思うが、ここにいてもプログラミングはできるだろうが自分でできる範囲が狭かったりPublic Cloudを触ることは難しいと思うので転職をすることとなる。

ちなみに個人ではDjango触ったりフロントエンドでAngularやVueを触るなどしていた。この頃はほぼ外に遊びに行くことはなく、ひたすらプログラミングするかちょっとハースストーンするぐらいだった。この頃のようなハングリーさが欲しい。

これが異世界転生ですか?

転職先となる前職は5月ごろにGreenで連絡が来ていた。当時はディレクター is 何だったりWeb制作ってなんぞと言う感じだったが、技術ブログを読んで面白そうだったので何度か面接を受けて6月に内定をもらった。

最初に内部の細々としてことをやって10月から実案件に入ることになる。

実践Webアプリケーション開発

某外コンの案件で新規でWebアプリケーション開発をする。技術スタックはLaravel+MySQL+Vue+Azureといった感じ。Azure以外は普通。

新規実装でCRUDはもちろんのこと、Laravelの認証middlewareを要件に合わせてカスタマイズしたりBackendのリファクタリングテスト駆動開発の導入、FrontendのSP対応とSPデザインをFigmaで作ったりとなんでもやっていました。

ちょっとプロジェクトの雲行きが怪しくなってきたと言うことでアサインされました。結果としてはPoCは通過して自分が離任後に正式にリリースされました。

本件を通じてテスト駆動開発とソフトウェア設計は重要だよねと言うことを再確認できたと思います。 特にFrontendはBackendと違ってFrontend独自の複雑性があり、そこはうまく対応できずかなりお粗末な設計・実装になってしまったなと反省しています。Vuexをどこで使うか、副作用の取り扱い、コンポーネントの責務の切り分けなど今でも難しいなと思っています。一般的なWebアプリケーションのBackendの複雑さの関心は主にビジネスロジックにあると思いますが、Frondendはそれ自体の複雑さが主となっているように思えるのでBackendのようにはいかないのが難しい。

炎上の火消し

2019年初めに上記の案件から一瞬離れて絶賛炎上しているフロントの案件に入ることとなりました。nunjucksを使ってましたがもう一生使うことはなさそうです。

Nunjucks+JavaScriptでFrontendのみリニューアルをすると言うものでした。Frontendだけリニューアルなので、BackendのAPIがHTMLを返すのでそれを描画すると言う謎仕様そのままでした。

ひたすらJavaScriptで検索周りの画面のロジックを実装していました。一応既存実装はあったのですが、チェックボックスの全選択を実行するとチェックボックス全てで都度イベントが発火するので、チェックボックスの数の分だけAPIが叩かれると言うやばい実装になっており、これの作り直しでした。

よく終電近くまでやっていましたが、ある種お祭り感もありそこまでしんどさは無かった気がします。同じことをしたいかで言えばしたくはないですが。。。

みんなで頑張って実装していき、なんとか形になりました。ここでJavaScriptに少し詳しくなりました。

久々の常駐と新規DMP開発

2021年になり、とある美容系広告代理店のエンジニアとして久々に常駐して業務することとなりました。

技術スタックはTypeScript+MySQL+Next+Express+GCPといった感じで既存メンバー二人がFrontendあがりなのでTypeScriptに統一しています。

主に新規DMP開発と先方の新卒社員の育成を行いました。

当初は社内の広告のコスト監視ツールの開発でしたが、ITP対応やiOS14対応と合わせてFacebook Conversion APIを使った広告サーバ件DMPの新規開発を任されることとなりました。現在では主力事業に成長しています。

ビジネス的には開発して離任するまでに10社のお客様に導入していただきました。 導入に関してもコンサルの方と連携して導入サポートをしていきました。

技術的には初めてGCPをフルに使ってのインフラ構築とTerraformでのIaCを実現しました。一部terraform化できなかったリソースがありますが、主要な部分はまあまあ管理できていると思います。

テストコードも合わせれ実装するようにして、テストを書くと言うことを当たり前にしていきました。結果として、顧客影響のある障害は0を達成しました。(唯一、core web vitalsのメトリクス取得でミスってタグ内でエラーで計測に一部失敗していました)

また新卒教育ですが、JavaScriptをベースにWebの基本的なところを抑えてもらって、実際に社内アプリケーションの開発をしてもらって実案件に入ってもらう流れで進めました。 最初はPCの電源の付け方もわからなかったですが、最終的には自力でpuppeteerを使ってFacebookの広告管理画面の2段階認証を突破して業務の自動化をいい感じにやってくれるようになり、離任時はプロジェクトを任せられるほど成長して良かったです。彼ら・彼女らの努力はもちろん、スタンスが素晴らしかったと思います。

前職で最も成長できた期間だったなと思います。

そして退職

その後、会社の方針と自分のキャリア方針、ビジネスとの協調などに課題感を感じて退職しました。

退職はしていますが、筋の良いジュニアのエンジニアがこれからスキルをもっと伸ばしていくという観点でオススメできると思っています。

事実として入社前では無理であろうさまざまな経験を積むことができました。そういった点で、まさにこの転職は異世界転生だったということでした。

技術的には言わずもがなですが、勉強会を個人的に開催したり採用系の業務を行うようになり自律性の向上を感じます。

フリーランス

退職後は次を見据えて業務委託として各社でお手伝いさせていただいています。あまり触ることが無かったAWSに触れたり実業務で使いたかったGolangを使う、これ以上ないほど酷いReactコードをリファクタリングしてTypeScript化をするなど様々なことをしています。

業務をしながら、最終的には「ここしかあり得ない」と言える企業に入るために準備をしています。

次のステップへ向けて

結局何ができて何ができない(改善しないといけない)のかを考えました。

できること

Backend

GolangとTypeScriptとPHPPythonは特に不自由しないと思います。他の言語だとちょっとキャッチアップが必要かもです。

テストコードを当たり前と思って実装します。

設計はちゃんと検討したいと思っており、それなりにはレイヤードアーキテクチャの話が通じます。(ただ最近はレイヤードアーキテクチャ的なアプローチが果たして有効なのかに疑問を持っており、詳しい方に見解を聞きたいです。)

GraphQLもわかります。

Frontend

最近はずっとReactあるいはNextですが、本職のFrontend Engineerよりは劣ると思いますがコンポーネント作成とかstate管理、Storybookやテストはそれなりにはできるつもりです。E2Eもplaywrightで実装した経験あり。

Infrastructure

インフラはGCPでそこそこのWebアプリケーションとデータ基盤の構築はできると思います。

具体的にはCloud RunにLoad Balancer立ててVPC内にCloud SQL用意して、Pub/SubとかDataflowを使ってBigQueryにデータを流し込むパイプラインを作れます。

terraformも1サービスの過半数のサービスを管理していたので、GCPなら不自由しない印象です。

業務一般

組織の課題を吸い上げて自分で解決に向けて実行します。

コミュニケーションは得意ではないですが、前職のマネージャーからは入社時と比べてかなり改善されたと言われたので問題があるという認識もないです。

またいつも同じようなテンションなので、あまり自分の上機嫌・不機嫌が表に出ない

その他

また採用とエンジニア育成に携わってきたので、それらでも貢献できそうです。

多少ですが広告の知識はあります。(用語を言われたら理解はできる)

できないこと

Backend

マルチスレッド処理は得意ではないです。goroutineやchannelは使えますが、シュッと実装できるほどではないです。

アルゴリズムに苦手意識があります。某企業のコーディング面接でダイクストラ法をソラで実装する問題がありほぼ手も足も出なかったという苦い経験があります。(そう指示された訳ではないが問題の解放として一番スマートなのがダイクストラ

DDDとか読み込んでいるわけでもなく、アーキテクト的な業務をやったことがあるわけでもないので要件定義・基本設計のレイヤーはまだ得意じゃなさそうです。

パフォーマンス改善もあまりメトリクスをしっかり測定して何かを改善するという経験が無いです。

Observabilityについても基本新規開発ばかりであまり知見がないです。DataDogとかOpenTelemetryとか知ってるけど使ったことない状態。

Frontend

CSSとかHTMLも全体的に理解が浅いです。マークアップ周りは今後頑張りたいかと言われたら直近ではそうでもないですが。

もうあまりやらないかもですがwebpackの設定とかは全然わからないです。

あとReactのuseMemoとかuseCallbackあたりはあまり使いこなせてないです。

SEOはわからない。

パフォーマンス改善も経験ないです。

Infrastructure

k8sはほぼ触ったことないです。

クラウドネイティブ周りも名前聞いたことあるとかちょっと知ってるけどあまりわかってないものが多いです。

AWSもそんなに詳しくないですが現在は少しずつ業務で触っていて目下勉強中です。Azureもちょっと業務で使ったぐらいです。

業務一般

助けを求めるのが苦手です。

あとは相手が話している言葉の含意を汲み取るとか期待されている言動が何かを理解して動くことが苦手です。(twitterでイーロンマスクに噛み付いた某androidエンジニアとかはこの点で自分に近いものを感じました)

英語は4技能全て苦手です。

これからやるべきこと・やりたいこと

業務

一般

直近で言えば業務知識をつけることです。直近では広告業界にいるのでWeb広告やマーケティングについてもっと勉強する必要があります。 他では気になる領域としては採用・人事評価や決済周りは気になります。

将来的にはマネジメント系をやることがあるかないかは分かりませんが、相互理解のためにも組織マネジメントについて勉強したいですね。

この手の書籍はたくさんありますが、EMについてなら「エンジニアリングマネージャーのしごと」とか

組織論であれば「TEAM OF TEAMS」とかになるのかなと思っています。

Backend

現状メインはGolangですがそこまでこだわりはないです。ただTypeScriptでもKotlinでもいいですが、LL言語はもういいかなと思います。(型によるある程度の動作保証のありがたさはその辺の単体テストよりも重要だと考えているので) 大規模開発の経験をすることが直近の課題です。ここで言う大規模開発とは、あるサービス・プロダクトの利用者数やトラフィックが膨大であることを指します。(何を持って膨大とするかは諸説ありますが。。。)

マイクロサービスについてはそこまでこだわりはないです。マイクロサービスが重要ではなく、正しく理由があって且つ適切なサービス境界で分割できていることが重要なので。

Frontend

こちらはそんなに力を入れているわけではないので特にはないですが、メインはNext.jsを触れるといいのとCDN周りはもう少し勉強したいですね。(最近だとcloudflare、特にD1が熱かったりする)

www.cloudflare.com

Infrastructure

どうせGCPはそこそこ(めっちゃではない)わかるのでAWSをちゃんとやっていきます。GCP好きなんですがやはりマジョリティーAWSなので、AWSも現状のGCPの知識ぐらいあるなら多分クラウドボトルネックになることはないだろうという判断です。

そして前職までの課題であった監視・運用もしっかり携わっていきたいですね。自分で理由づけをして監視戦略・運用戦略を立てられるぐらいを2年のうちに目指したいです。

Others

細かいですがエンジニアの定量的な評価しづらさに課題感があり、four keysを計測したりインフラコストの可視化などは取り組んでいきたいです。

トランクベースの開発もどのぐらい効果があるか試してみたい気持ちがあります。

個人

アルゴリズムを勉強します。LeetCodeで言うとeasyの50%未満の問題もサクッと解けるぐらいをまずは目指したいです。

並行処理・並列処理をもっとこなれるようにしていきます。

アプリケーション設計について「ソフトウェアアーキテクチャの基礎」「ソフトウェアアーキテクチャ・ハードパーツ」を読みます。

個人で密かに開発しているWebアプリケーションをまずはリリースします。できたらFlutterでネイティブアプリを作るのもよいかもしれないです。

英語を勉強し直して、2年後には英語でのコミュニケーションを業務でできるようになります。色々なところで英語の需要が年々高まっていると言うことを聞いているので、流石にそろそろ英語に本腰入れる時期かなと思いました。

あと運動の習慣と早起きを習慣づけます。

終わりに

個人的には4年以上経った割にはあまり大したことができてないなと言う印象を受けました。。。

キャリアが安定したことで昔のようなハングリー精神が削がれてしまっているかもしれません。(一概に悪いとは言えませんが)

先の2・3年を見たらやらないといけないことはたくさんあるので、またやるべきことと優先順位を整理した上で次の振り返りで満足できるように結果を出していきたいですね。

monorepoでのhusky+lint-stagedとlefthookと比較

monorepo環境でのgit hooksの管理でどう設定するのがいいのか、まとまった資料はなさそうなので書き記します。

以降はhuskyとlint-staged、lefthookについては知っている前提です。 知らない場合はざっくり説明すると

husky → git hooksを管理するライブラリ

lint-staged → gitのstagedなファイルに対して任意のコマンドを実行できるライブラリ

lefthook → go製のgit hooks管理ライブラリ

github.com

github.com

github.com

個人的には後述する理由からlefthook推奨です。

前提

バージョンは以下のとおりです。

husky: 8.0.1
lint-staged: 13.0.3
lefthook: 1.1.1

また、今回はgoとnext.jsのアプリケーションが横並びである状態を想定しています。

$ tree -L 1
.
├── go
└── next

目標は、commit時に各アプリケーションのファイルごとに特定のコマンドを実行するようにします。 jsやts、tsxファイルなどに対してはeslintやprettier、goファイルに対してはgo fmtやgolangcil-intを実行します。

husky+lint-stagedでの設定

まずroot directoryで以下コマンドを実行します。

# package.jsonを作成
yarn init
# 依存ライブラリをinstall
yarn add -D husky lint-staged
# package.jsonにhuskyの初期化コマンド追加
npm set-script prepare "husky install"
# huskyの初期化
yarn prepare
# git hooksにpre-commit時のコマンド
yarn husky add .husky/pre-commit "yarn lint-staged"

これでcommit時にlint-stagedが実行されます。

ではアプリケーションごとに実行アプリケーションごとに実行させるには各アプリケーションのディレクトリごとに.lintstagedrcを追加します。

# root/next/.lintstagedrc.js
const path = require('path')

module.exports = {
  '*.{js,ts,tsx}': (absolutePaths) => {
    const cwd = process.cwd()
    const relativePaths = absolutePaths.map((file) => path.relative(`${cwd}/next`, file)).join(' ')
    return [`eslint ${relativePaths}`, `prettier --write ${relativePaths}`]
  },
}
# root/go/.lintstagedrc.js
module.exports = {
    "*.go": "go fmt"
}

これで各アプリケーションごとにcommit時に指定したコマンドが実行されます。 pushとコマンドを分けたい場合は別名でconfigファイルを作成して-cオプションで指定すれば良いです。

lefthookでの設定

lefthookはnpmのpackageはdeprecatedになっているため、macであればhomebrew経由やgoバイナリをdownloadするのが良いです。

www.npmjs.com

lefthookをdownloadしたら以下コマンドを実行してlefthook.ymlを作成します。

lefthook install

そしてpre-commitに実行したいコマンドを追加します。

rootにアプリケーションのディレクトリを指定することで、そのディレクトリからコマンド実行とファイルパスの解決をしてくれます。 {staged_files}でstagedなファイルの全件を半角スペース区切りで渡してくれます。

pre-commit:
  parallel: true
  commands:
    eslint:
      root: 'next/'
      glob: '**/*.{js,ts,jsx,tsx}'
      run: yarn eslint {staged_files}
    gofmt:
      root: 'go/'
      glob: '**/*.go'
      run: go fmt {staged_files}

そしてpre-commit設定を追加します。 lefthook add pre-commit

これでcommit時に各ファイルに対して任意のコマンドを実行してくれます。

どちらが良いか

lefthookのwikiにも記載がありますが、以下の理由から自分はlefthookを推奨します。

  • huskyはNodejsランタイムが必要だがlefthookはシングルバイナリのみで動く
  • huskyは並列実行できないがlefthookは並列実行可能
  • lefthookは一つの設定に集約可能
  • lefthookはgit hooks管理ライブラリではなくタスクランナーの側面もあるので適用範囲が広い

github.com

4番目は私見ですが、commitを契機とするタスク実行をしてくれるライブラリなのでhusky+lint-stagedよりも適用範囲は広いと思います。 任意のscriptを実行でき、dockerにも対応しているのでそもそもできることの幅がhusky+lint-stagedとはだいぶ違うという認識です。

終わりに

どちらでも基本的なmonorepoでのpre-commitの設定はできることがわかりました。

husky+lint-stagedがデファクトスタンダードですが、まだ使ったことが無い方はlefthookの導入も検討してみてください。

新規プロジェクトでとりあえず入れるライブラリ・サービス

自分が新たにリポジトリを作るときにある程度デフォルトでいれているライブラリやサービスを書き残します。

Docker

説明は不要。ライブラリ開発なら要らないかも。

www.docker.com

GitHub Actions

基本GitHubを使っているので、特段の理由が無ければGitHub Actionsを使用。

今日のソフトウェア開発においてCI/CDは当たり前であり、プロジェクト初期から入れる方がレバレッジが効く。GitHub以外なら他のサービスをよしなに使います。(CircleCI、Gitlab CI、etc...)

github.co.jp

lefthook

git hookを管理するツールです。 github.com

一般的にはhuskyが有名だと思いますが、

The fastest polyglot Git hooks manager out there とあるように、最速を謳っています。

他にもhuskyとの差別化について書いてます。

github.com

個人的な見解としては速度もそうですが

  • huskyはdockerとの相性が悪い
  • nodejsとhuskyを入れる場合はlint-stagedと原則セットになるのが煩雑

secretlint

secretlintはcommitしてはいけない秘匿情報を含んだファイルのチェックを行うライブラリです。 github.com

JavaScript Primerのauthorだったりtextlintの開発者であるazuさんが開発しています。ご本人による解説はこちら。 efcl.info

秘密鍵やPublic Cloudのsecret keyなど、誤commitされるケースは少なく無く、そのままpushされて最悪は悪用されることがよくあります。悪用されれば金銭的な被害のみならず、事後対応やレピュテーションリスクに晒されることになる可能性があります。

secretlintは割と他に代用できるツールが無いのにあまり広まってない感覚があるので、特にアプリケーション開発ではlefthookとセットで入れるのをおすすめします。

actionlint

GitHub前提になってしまいますが、GitHub Actionsのyamlを静的解析してくれるライブラリです。

github.com

ご本人の解説はこちら rhysd.hatenablog.com

GitHub Actionsの構文解析をpushして怒られてからではなく、手元で確認できます。 説明にも書いてあるように本体側でのチェックが緩いこともあり、かなり細かく見てくれるのでありがたいです。

これで後はCircleCIみたいにローカルで確認できれば...

renovate

renovateは自動でライブラリや他依存関係のアップデートの修正チケットを作成してくれるライブラリ及びWebサービスです。

github.com

GitHubであればGitHub Marketplace経由で無料で有効化でき、以降は自動でPRを作成してくれます。

またself hostingもできるのでオンプレ運用できたり、ライブラリとしても提供されているので他の定期実行できるツールと合わせて利用することができます。

ライブラリのアップデートは全て手動で行うのはそれなりの時間を取られてしまうので、renovateでCIがpassしたら自動mergeするようにすることで運用コストを大幅に削減できる可能性があります。

設定も非常に細かく適用できますが、まずはデフォルトの設定から入れるのが良いかと思います。

3/24

本来は有給のはずなのに仕事している。どうして...

今日はずっとHTMLとCSS(SCSS)の修正で一日が終わった。恥ずかしながらdialog要素を初めて知りました。マークアップは初心者なので。

プライベートはpuppeteer辛みを感じるのでchromedpを試している。

github.com

3/18

PlawrightでゴリゴリE2Eテスト実装してCSSPhotoshopでできたUIデザインにキレてたら一日終わった。

タスク4つほど並行ある上に某クライアントがMTG多すぎてあの会社いやすぎるってなっている。

 

タスクは減るものの来週もこんな調子になりそうでうーん。

 

 

最近やってること

あまりにも更新しなさすぎなので、最近やってることについて書いていく。 今後もちゃんと日誌にして雑多なことを書いていって、まとまった技術的な知見はzennに書いていく感じにしていきたい。

業務

去年はずっとTypeScriptとGCPとTerraformをやっていた。

TypeScriptはType Challengeは全くやっていないが一通り業務で自分で実装もするしレビューもしたのでそれなりに実装できていると思う。 ただ信頼できない外部からの入力に対しては脆く、実践導入はできなかったがio-tsとか使えばもっと楽になったかもしれない。

github.com

Terraformはぶっつけ本番で実戦投入して、良かったこともあるし知識不足でうまくいかなかったこともあり、全部含めて学びになった。(次はもっとうまくやるぞ)

ここ数ヶ月は大したことはやっていないが、直近はplaywrightでJavaのWebアプリケーションのE2Eテストを実装している。E2Eテスト、慣れてないのもあるかもしれないが壊れやすくて難しい。

TypeScript

プライベートでpuppeteerでスクレイピングしたりそのpuppeteerをkickするAPIをExpressとかNestJSで実装するなど。

Reactは1月に社内の課題でviteとかesbuildとか使ってフォームをいい感じにvalidationするのをTDDでやってた。

とはいえ型周りの仕様に疎い(inferとか)のでType Challengeを4月はやりたい。(時間あるのか?)

github.com

Go

現職の社内プロジェクトでGoでAPIを隙間時間で開発したりTerraformの環境構築したりしている。 如何せん本業の隙間なので時間があまりない。

Goのhot reloadに何がいいかを同僚と検討していて、reflexがいいんじゃないかという話になり採用している。

github.com

現状ではGoでしか使ってないが、他の使い道がありそうで良さげ。

Database Clientはいまいち何が主流なのかあまりわかってない。SQL firstなsqlcも良さそうだしGraphQLと相性が良いentも良さそうな感はあるが、そんなに使えてないので不明。

github.com

https://entgo.io/

Rust

個人で細々とAPIを開発している。actix-webがベタだけど、axumも良いかもしれないと思い始めている。

blog-dry.com

blog.varwww.com

その他趣味

ちまちまとやっていた雀魂で雀聖になった。このぐらいはいけると思っていたので今も粛々とやっている。基本に忠実に打って、若干引き気味ぐらいがちょうど良いという感じがある。一向聴からは思っているよりも押せないというのを科学する麻雀で確認できたのとNAGAと対戦して序盤の切り順を矯正できたのが良かった。天鳳もやっていきたいが時間がない。

同僚に布教したら自分よりもどハマりしており毎日やっている。

React+Contentful+Netlify+Google Domainで個人ブログを作る

久しぶりの投稿です。TypeScript+Reactの素振り用に前から気になっていたHeadless CMSのContentfulを使ってブログをNetlifyにホスティングしてみました。今ならCloudflare Pagesとかになるんでしょうか。

Netlifyから提供されるドメインではなく独自ドメインを設定する方法も合わせて追記しています。今回はGoogle Domainを使いました。お名前.comは使いたく無い

NextでもVueでも同じようにできると思うので置き換えてください。

参考になるか分かりませんが、記事の例で使用している自分の実装はこちらです。あくまで参考程度ですが、特にeslintとかprettierとかの設定は参考にしないでください。

話すこと

  • Contentfulでの設定方法
  • Contentfulのmodelの型定義
  • SDKの使い方
  • Netlifyでのデプロイ手順
  • GitHub Actionsの設定
  • Google Domainでのドメイン取得方法

話さないこと

  • TypeScript自体
  • Reactの環境構築
  • 全体の実装

お品書き

  1. Contentful設定
  2. SDKの使用方法
  3. modelの型定義
  4. Netlify設定&デプロイ
  5. ドメイン取得

Contentful設定

Contentful自体の日本語での説明はこちら(次世代Headless CMS「contentful」事始め)をみてください。

何はともあれログインしてspaceを作成します。基本Freeで大丈夫です。

f:id:kana_kanade:20210405224154p:plain

spaceを作成したらmodelを作成します。これが実際のデータ構造になります。

Contentfulは多くのfieldを用意しています。ReferenceはRDBの外部キーのような使い方ができます。今回はカテゴリとタグを紐づけるのに使用しました。 今回は記事のカテゴリとタグ、記事自体のmodelを用意します。 f:id:kana_kanade:20210406082733p:plain

まずカテゴリのmodelです。 f:id:kana_kanade:20210406082530p:plain 次にtagのmodelです。 f:id:kana_kanade:20210406082546p:plain 最後に記事のmodelにカテゴリとタグを紐付けます。 f:id:kana_kanade:20210406082551p:plain 記事本文は今回はMarkdownを指定してください。描画時にMarkdownをhtmlに変換します。 f:id:kana_kanade:20210406083042p:plain カテゴリとタグの紐付けはReferencesを選択するとOne ReferencesとMany Referencesが選択できます。カテゴリは記事に対して一対一、タグは一対多で一つの記事に複数紐づくようにしました。 f:id:kana_kanade:20210406083712p:plain

最終的に以下のようになります。 f:id:kana_kanade:20210406082524p:plain

最後に記事を取得するためのAPI keyを生成します。ヘッダーのSettings→API KeysからAdd API Keyを選択して生成します。 後でContentfulのSDKを使いますが、その際にSpace IDとContent Delivery API - access tokenが必要になります。 f:id:kana_kanade:20210406084536p:plain

これでContentfulは終わりです。

SDKの使用方法

Reactの一通りの環境構築が終わっている前提で、SDKのinstallと使い方の説明をします。

SDKをinstallします。

npm install --save contentful

記事一覧を取得する実装です。 createClientにSpace IDとaccess tokenを渡してContentfulClientApiインスタンスを生成します。 その後getEntriesで全体を取得できます。 ここでは実装していませんが、検索条件を付与することも可能です。詳しくはドキュメントを読んでください。

型定義については後で説明します。

import {ContentfulClientApi, createClient, Entry} from 'contentful';
import {Article as ContentfulArticle} from '../domain/contentful/article';

const getClient = (): ContentfulClientApi => {
  return createClient({
    space: process.env.REACT_APP_CONTENTFUL_SPACE_ID as string,
    accessToken: process.env.REACT_APP_CONTENTFUL_DELIVERY_API as string,
    resolveLinks: true,
  });
};
const getContent = async (
  contentType: string
): Promise<Entry<ContentfulArticle>[]> => {
  const client = getClient();
  const response = await client.getEntries<ContentfulArticle>({
    content_type: contentType,
  });
  return response.items;
};

modelの型定義

Contentfulの記事はここをanyにしてごまかしている実装がよく検索でヒットします。JavaScriptではなくTypeScriptを使っているのでanyに逃げずにちゃんと型定義をします。

先ほどのgetEntriesの戻り値の型はPromise<EntryCollection<T>>>です。 その戻り値のitemプロパティの型はEntry<T>でこれがmodelの実体になります。このTに自分で設定したmodelの型定義をあててあげれば型をつけて取得できます。

Entry型は以下のようなデータ構造になっています。

export interface Entry<T> {
    sys: Sys;
    fields: T;
    toPlainObject(): object;
    update(): Promise<Entry<T>>;
}

sysはuniqueなidや作成日時、更新日時などのメタデータが定義されています。 fieldsがモデルの定義になります。

よって今回の型定義は以下のようになります。

import {Sys} from 'contentful';
export type Category = {
  fields: {
    name: string;
  };
  sys: Sys;
};
export type Tag = {
  fields: {
    name: string;
    slug: string;
  };
  sys: Sys;
};
export type Article = {
  category: Category;
  content: string;
  tag: Tag[];
  title: string;
};

Article型はEntry型のfieldsプロパティの型を定義してsysプロパティの型定義は不要です。

Netlify設定&デプロイ

NetlifyはGitHub連携でpush時に自動でデプロイするのとSDKでのGitHub Actionsでのデプロイがあります。 starter planではビルドが月300分までなので、頻繁にデプロイしたいならGitHub Actionsでビルドしてデプロイするとお得です。 以下のような.github/workflows/deploy.yamlを用意してください。

masterブランチへのpush時に以下のworkflowが実行されます。

name: Deploy

on:
  push:
    branches:
      - master

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout source code
        uses: actions/checkout@v2

      - name: Cache node_modules
        uses: actions/cache@v1
        with:
          path: node_modules
          key: ${{ runner.OS }}-build-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
            ${{ runner.OS }}-build-
            ${{ runner.OS }}
      - name: Setup Node
        uses: actions/setup-node@v1
        with:
          node-version: 14.x

      - name: npm install and build
        run: |
          yarn install
          yarn build
      - name: Deploy to netlify
        run: npx netlify-cli deploy --dir=./build
        env:
          NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
          NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}

まずAUTH_TOKENとSITE_IDですが、アプリケーション設定画面に遷移するとNew access tokenとあるので押下するとAUTH_TOKENが取得できます。 f:id:kana_kanade:20210510234707p:plain

SITE_IDはhttps://app.netlify.com/sites/[自分のサイト名]/settings/generalに遷移するとAPI IDとあるのでこれがSITE_IDです。

f:id:kana_kanade:20210510234754j:plain

これらをGitHub Actionsで使うために環境変数として登録します。 GitHubリポジトリのSettingタブからSecretsを選択すると環境変数が登録できます。 f:id:kana_kanade:20210516182707p:plain

Google Domainsでカスタムドメイン設定

Google DomainsGoogleが提供するドメイン管理サービスです。 日本だとお名前.comがありますが、個人的には使いたくないので今回はGoogle Domainsを使います。

これ読んででいいですか?ダメ?

大切なのは、Google DomainsのカスタムリソースレコードにnetlifyのIPアドレスとwwwつきドメインを追加してください。 f:id:kana_kanade:20210531221952p:plain

netlifyのドメイン設定もここにあるんですが、

NetlifyのDomain ManagementからAdd Custom Domainで以下のような設定されていればおkです。 f:id:kana_kanade:20210531233456p:plain

最後にbaseURLを設定したカスタムドメインにすれば終わりです!

まとめ

覚えてしまえばそんなに難しく無いかなと思います

今度はNext.jsでcloudflare pagesにデプロイしてみたいです。