emahiro/b.log

Drastically Repeat Yourself !!!!

Go で文字数をカウントする

Overview

Go 言語で文字数をカウントする方法について記載します。

そもそも文字数を数えるとは?

LINE Engineering のこのブログが詳しくてわかりやすいです。

engineering.linecorp.com

ある文字列の文字数を計算するときに、コンピューター上で「何文字」として扱われるかは、文字の定義によって異なります。

Go における文字数のカウント方法

utf8.RuneCountInString を使う

マルチバイトの文字(漢字など)を使ったテキストの総文字数を Go でシンプルに計算するときは https://golang.org/pkg/unicode/utf8/#RuneCountInString を使います。

例えば以下のように使用します。

package main

import (
    "fmt"
    "unicode/utf8"
)

func main() {
    s := "こんにちは"
    ss := utf8.RuneCountInString(s)
    fmt.Println(ss)
}

この実装の出力結果は 5 です。

しかしこの文字数の数え方には 絵文字が入った文字列では正確に文字数をカウントできない という漏れがあります。例えば以下のようなケースでは✌️ は Rune Slice の長さ 2 のマルチバイト文字列であることがわかります。

package main

import (
    "fmt"
    "unicode/utf8"
)

func main() {
    s := "✌️"
    ss := utf8.RuneCountInString(s)
    fmt.Println(ss)
}

Grapheme Cluster を使う

ユーザーが認識する文字。文字体系で表現できる文字の最小単位。1 Graphemeは、N code pointで構成されています。

ref: https://engineering.linecorp.com/ja/blog/the-7-ways-of-counting-characters/

Go で Grapheme を使う

OSS として出してくれてるライブラリがあります。

github.com

実装方法は上記の Readme に書いてる通りです。

このライブラリ (というか Grapheme) は Rune Slice が 2 以上あるマルチバイと文字列も「1文字としてカウント」してくれます。

具体的に文字数を数えるときは以下のように実装します。

func main() {
    var sum int
    gr := uniseg.NewGraphemes("✌️!")
    for gr.Next() {
        sum++
    }
    fmt.Printf("%d", sum)
}

この出力は2です。

✌️ + ! の文字列しかないので 2 になります。 utf8. RuneCountInString を使用した場合、上記の文字列は 3 と表示されます。つまりマルチバイトの文字まで数えてしまいます

余談: 同じ絵文字でも文字数が違う場合がある

👉 という絵文字にも以下のような種類が存在します。

f:id:ema_hiro:20210119141227p:plain

この別の肌の色をどう表現してるのかを調べました。

package main

import (
    "fmt"
 
    "github.com/rivo/uniseg"
)

func main() {
    s := "👉"
    gr := uniseg.NewGraphemes(s)
    for gr.Next() {
        fmt.Printf("%x", gr.Runes())
    }

}
// Output: [1f449]

別の肌の色では以下でした。

package main

import (
    "fmt"
 
    "github.com/rivo/uniseg"
)

func main() {
    s := "👉🏻"
    gr := uniseg.NewGraphemes(s)
    for gr.Next() {
        fmt.Printf("%x", gr.Runes())
    }
}
// Output: [1f449 1f3fb]

👉 に色の文字コードが追加された出力されました。なお utf8.RuneCountInString を使用した場合、👉 は1文字ですが、👉🏻 は2文字で計算されます。

同じ絵文字でも文字数が違うことがわかりました。

ちなみに Code Point を調べるとこのことは一発でわかります。

emojipedia.org

👉 U+1F449
🏻 U+1F3FB

と別のデータとして扱われてますね。

まとめ

目で見てる絵文字と、コンピュータ上でどう扱われるかには差があるので「文字数をカウントする」という実装はかなり奥が深い(というかややこしい)ことがわかりました。

Grapheme cluster は覚えておこうと思います。

Firebase deploy via Github Actions

Overview

Firebase 上で動作している Nuxt で書かれたアプリをGithub Actions 経由で Deploy する機構を作った話です。

手順

前提

以下の前提が必要です。

  • デプロイ権限を持っているFirebase アカウントがあること。
  • デプロイするための Firebase Token を取得していること。

Firebase deploy with Github Actions

めちゃくちゃ簡単で以下の公式のサンプルの通りにやったらデプロイまで完了しました。

github.com

  1. firease login:ci を実行して権限を要求する画面が出るので、許可してトークンを発行します。
$ firebase login:ci
 
 Visit this URL on this device to log in:
 https://accounts.google.com/o/oauth2/auth?client_id=xxxxx.....
 
 Waiting for authentication...
 
 ✔  Success! Use this token to login on a CI server:

 
 Example: firebase deploy --token "$FIREBASE_TOKEN"

以下のような認証認可プロセスに入るので権限を許可します。

f:id:ema_hiro:20210113031340p:plain

  1. 取得したトークンを Githubリポジトリの設定から secrets に追加します。今回は FIREBASE_TOKEN という secrets 名で保存します。(この secrets 名を workflow の設定ファイルから参照します)

f:id:ema_hiro:20210113031221p:plain

f:id:ema_hiro:20210113031301p:plain

  1. Github Actions のワークフローの設定をします。

これは一番最初に記載したドキュメントに書いてあるサンプロをそのまま転用しました。

name: Build and Deploy
on:
  push:
    branches:
      - main // master から main に変更したので

jobs:
  build:
    name: Build
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node: [14]

    steps:
      - name: Checkout Repo
        uses: actions/checkout@master

      - name: Setup Node
        uses: actions/setup-node@v2
        with:
          node-version: ${{ matrix.node }}

      - name: Install Dependencies
        run: npm install

      - name: Generate
        run: npm run generate

      - name: Archive Production Artifact
        uses: actions/upload-artifact@master
        with:
          name: dist
          path: dist

  deploy:
    name: Deploy
    needs: build
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Repo
        uses: actions/checkout@master

      - name: Download Artifact
        uses: actions/download-artifact@master
        with:
          name: dist
          path: dist

      - name: Deploy to Firebase
        uses: w9jds/firebase-action@master
        with:
          args: deploy --only hosting
        env:
          FIREBASE_TOKEN: ${{ secrets.FIREBASE_TOKEN }} // ここで secrets に設定した FIREBASE_TOKEN を使う

npm run generate すると dist ディレクトリ配下に成果物が作られます。これを Firebase Hosting でデプロイして参照するようにしているので nuxt のコンパイルは標準のまま使用します。

ここまでやったらあとは main ブランチに push(or merge) するだけでビルドされた成果物が自分の Firebase アカウントの権限で Firebase Hosting にデプロイされます。

めちゃくちゃ簡単でしたね。

コンテナで Lambda を動かす

Overview

12 月の re:invent で Lambda のパッケージングフォーマットにコンテナイメージがサポートされたので、少し遅くなりましたが、デプロイまで試してみました。

今回はランタイムに Go を使用してます。

このエントリは以下のドキュメントを参考にしてます。

aws.amazon.com

コンテナイメージを ECR にアップロードする

別エントリで書きましたのでそちらを参考にしてください。

ema-hiro.hatenablog.com

Go の実装

以下の PR で Go の実装をしました。

github.com

ハマったところ

  • Lambda Handler を実装しないと Runtime.InvalidEntrypoint で落ちます
    • 何を当たり前のことを...w
  • https://hub.docker.com/r/amazon/aws-lambda-go は使えません。
    • このイメージ には実行に必要な Lambda Runtime Interface Entrypoint が入ってませんでした。

f:id:ema_hiro:20210103033351p:plain

結論としては Lambda Runtime Interface が入ったコンテナイメージを用意します。
以下のドキュメントが参考になります。

docs.aws.amazon.com

Go のドキュメントは以下になります。

docs.aws.amazon.com

自分の PullRequest では軽量な alpine ベースでイメージを作成しました。

デプロイ

Lambda を動かすための専用イメージを作成し、ECR にアップロードしたら AWS のコンソールから Lambda を作成するときに、アップロードしたイメージをデプロイするイメージとして選択します。

f:id:ema_hiro:20210103033643p:plain

おまけ

API Gateway との連携

コンテナをベースにしてデプロイした Lambda を API Gateway と連携します。

まず、トリガーに API Gateway を選択する。

f:id:ema_hiro:20210103033756p:plain

すぐ動かしたい時は API タイプを HTTPタイプ に指定します。

f:id:ema_hiro:20210103033759p:plain

デザインとしては以下のようになります。

f:id:ema_hiro:20210103033847p:plain

この状態でテストすると以下のように成功します。

f:id:ema_hiro:20210103033950p:plain

curl でも叩いてみると以下のように成功します。

$ curl -XGET -i https://$ApiGatewayID.amazonaws.com/default/emahiro-lambda-test

HTTP/2 200
date: Sat, 02 Jan 2021 17:26:59 GMT
content-type: application/json
content-length: 7
apigw-requestid: $RequestID
 
Hello !

ローカルでのテスト方法

手元でコンテナイメージを作成する以上、ローカルでテストしたいですよね。
普通にイメージを作成しただけでは、Local で AWS Lambda を動かそうとするとLambda の設定が未設定なので Local で動作確認することはできません。この辺は結局 Image をアップロードして Lambda を作らないと開発できないってあたりが今までと同じでめんどくさいなと思ったところです。

$ docker run -v --name emahiro-lambda-go-sample:latest -d -p 8080:80 emahiro-lambda-go-sample:latest
2021/01/02 17:38:46 expected AWS Lambda environment variables [_LAMBDA_SERVER_PORT AWS_LAMBDA_RUNTIME_API] are not defined

ただ、いちいちデプロイしないとテストできないのは面倒くさいので、ローカルで Lambda をエミュレートする方法がないか調べたところ公式のドキュメントに To test locally without adding RIE to the image という項目があり、この通りにローカルに Runtime Interface Entrypoint の mock を追加したら動作させることができました。

docs.aws.amazon.com

# RIE(Runtime Interface Entrypoint) のエミュレーターを追加します。
$ mkdir -p ~/.aws-lambda-rie && curl -Lo ~/.aws-lambda-rie/aws-lambda-rie \
https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie \
&& chmod +x ~/.aws-lambda-rie/aws-lambda-rie
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   164  100   164    0     0    398      0 --:--:-- --:--:-- --:--:--   397
100   640  100   640    0     0    888      0 --:--:-- --:--:-- --:--:--   888
100 7964k  100 7964k    0     0  2365k      0  0:00:03  0:00:03 --:--:-- 3919k
 
# /aws-lambda-rie をエントリーポイントとして Lambda のコンテナイメージを指定してコンテナを起動します。
$ docker run -d -v ~/.aws-lambda-rie:/aws-lambda --entrypoint /aws-lambda/aws-lambda-rie -p 9000:8080 emahiro-lambda-go-sample:latest /main
 
$ curl -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{}'
"Hello !"

/2015-03-31 ってなんなんですかね笑

まとめ

イメージ作成とECRのPush周りでつまづきましたが、結構簡単に Lambda をコンテナ上で動作させることができました。

個人的にいいなと思った点は今まで Lambda の動作確認をする場合、開発環境を開発者で共有しながら検証することが必要でした。
普段は Serverless Framework を使うことが多かったので、新しい Handler を yaml に追加したり、別のサービス名でデプロイし直したりしながら、チーム開発において個々人の実装の検証をしていました。
ただ、書いてる通り、結構管理が煩雑になりますし、検証終わった後にゴミ Lambda が残り続けることになるのであまり好きな方法ではありませんでした。

コンテナイメージを使うと、エミュレーターを使って手元で簡単に Lambda の動作を検証できます。
とはいえ、AWS のリソースを使ってる場合は結局デプロイが必要なので、若干片手落ち感ある気がします。。。

AWS さんは AWS のサービスリソースが丸っと入ったエミュレーター作ってくれたりしませんかね笑

今回はざっと動かすところまでやってみたので、実際にサービスで利用するにあたって以下のような観点をもう少し深掘りしてみようと思います。

  • デプロイ自動化。
    • 特にコンテナイメージは毎回 pull ではなく、Lambda の関数を作るときにイメージを指定することしかまだわかってないので、できればデプロイフローで最新のimageを指定してデプロイする、と言ったことをやりたいです。
  • Lambda の設定管理まわり
    • 自己文書化周り -> SAML の記法とか覚えないとですね。
  • Lambda Authorizer を使った安全なリクエストのやり方
    • これは Lambda 側の話かもしれないですが。
  • Serverless framework を使っての実装
    • Lambda on Container でのデプロイが完了したら次にこれをやりたいと思っています。

僕の価値観 ~ 2021 年 ver ~

あけましておめでとうございます。 2021年一発目のブログが記念すべき 250 投稿目です。

自分でもよく続いてるなと思います。

2021年1発目に何を書こうか迷ってたんですが、現時点の自分の価値観をエンジニアリングに関するところと、キャリア全般に関するところでまとめてみました。

あまりまとまっていませんが現時点の思考の dump として置いておこうと思います。

エンジニアリングについて

コードを書けることはスキルの1つであること

コードを書けることは素晴らしいことです。僕もコードを書くことは大好きだし、やって飽きないです。 おそらくソフトウェアエンジニアとして仕事をするにあたって、「コードを書ける」ことは基礎的な素養の1つに分類されると思います。

僕がここで言いたいのは、コードを書くこともハードスキルの1つに過ぎないってことです。

昨今、「コードを書ける」ことがなんというか、注目され過ぎてる(嫌な言い方をすれば、変に神聖化されている)と思っています。コードを書けることそれ自体は「英語が話せる」とか「数字が読める」と変わらないスキルセットの1つに過ぎません。

基礎的な社会人スキルがないのにコードを書けることばかりが持て囃されて、他のことは人並み以下でいいはずがないです。  趣味でコードを書く場合には、好きにしてくださいと思いますが、仕事としてやる以上、コード以外にも人並みのスキルがあってこそコードを書けることに価値が生まれます。

過度に神聖化せずに、スキルセットの1つと捉えることで見えてくることもあります。

コードを書けるだけだと、多分行き詰まる。

ソフトウェアエンジニアとしてコードを書くことができる、というのは大前提ですが、単純にコードを書くことにプライオリティを置きすぎると、おそらくどこかである一定以上の業務を任されなくなり、行き詰まります。 そこから這い上がる覚悟があるなら、コードを書くことにフォーカスしてもいいかもしれませんが、僕はそうではありません。

前述しましたが、コードを書くのは楽しい作業ですが、実際それだけで勝負できるかというと自分よりコードが書けるやつなんてこの業界めちゃくちゃいます。そして若くて優秀な人もたくさん入ってきます。優秀なエンジニアの生産性はそうじゃないエンジニアのX倍なんて言われたりします。

僕の尊敬するエンジニアの皆さんはコードも一流、コード以外も一流、な人が多いです。

そういう人たちと勝負する世界にいる、という危機感は持っておいた方がいいと思います。

プログラミングは面白い

そうは言ってもプログラミングは面白いです。やって飽きないですね。

コードは書くより消す方が難しい

ある程度エンジニアとして仕事をしてると、コードを書くことよりもコードを消す方が難しいことがわかってきます。 僕もこれまでにいくつかコードを削除する変更を出して来ましたが、影響範囲を取り違えてよくプロダクトを壊していました。

結局影響範囲の調査が不十分だった、ってことになるんですが、実装がどこからどう依存してるのか?などはやはり歴史を知らないと全てを把握することは難しいと思います。影響範囲を抜け漏れなく調査し、プロダクトに影響することなく、影響がある場合には段取りをした上でコードを削除する(機能を落とす)作業は実はコードを書く作業よりも、総合力が求められるので難しいんです。

手段が目的化してもいい

技術は課題解決のための手段、という考え方があります。僕も基本的にはその考え方に寄っています。

ただ、一方で「課題は技術を使うためのネタ」という考え方も好きです。要は課題なんかなんでもいいけど、その解決のための手段に興味があってもいいと思います。事業側からしたら何言ってんだ!みたいに思われる内容ですが、エンジニアなんだから好奇心にそって新しいツールや自分の好きな技術スタックでチャレンジしたくなるのはいいことです。

そのネタとして事業のロードマップを利用してやる、くらいの気概を持ってるとスキルも錆つかずに常にモダンな技術アセットをキャッチアップし続けることができるんではないでしょうか?

基礎的なスキルや知識は陳腐化しない

よく言われる話ですね。特に詳しくは述べませんが、HTTPの仕様だったり、SQLだったり、コードを書くための技術であったり、この辺で一度身につけた知識や技術はあんまり陳腐化しないで汎用的に使いまわせるな、と言うのは最近になって実感してます。

それを知らない頃は全然わからなかったんですが、こう言うのはある程度キャリアを重ねるとわかってくるものなんだなぁと思います。 経験がものをいう世界でもあると思います。

先達の小言はわりと聞いてるといいことありますね。老害と言って切り捨ててたりしたら勿体無いことなんで全力でその知見を吸収していきたいですね。

キャリアについて

キャリアに再現性はないが、ロールモデルは複数持っておくといい

現職では「キャリアなんてあってないようなもの」という表現がされており、僕もこれにはかなり同意しています。キャリアにおいてさまざまタイミングで必要とされる役割を100%こなし、期待に応えていくのが大事だと思っています。

ただ、そうは言っても自分が新卒~社会人3年目くらいまでの時に、目標とする人が当時の上司(新卒で入った会社の CTO )しかおらず、当時新人のペーペーだった自分からはあまりに高すぎる目標だったのと、あまりに目の前の仕事に熱中しすぎて、将来どうしたい?みたいなことをさっぱり考えていなかったために、行き当たりばったりの人生をすごしていたな、という反省があります。

別に行き当たりばったりこそ、人生の本質!みたいな価値観で若干生きてるところがあるのでそれ自体は僕としてはいいんですが、無駄なことも結構多く、しかも身近に同じような境遇の人がいれば話を聞いて、知識として持っておくだけで、解決できたかも知れない課題や回避できたかも知れない障害があったので、そういうのを避けるために自分の+5歳くらいの年齢の人が、キャリアにおいてどういう仕事をしてるのか?どういう役割をやっているのか?さらに言えば、そういう仕事の仕方をしてるのか?どういう調べ方をしてるのか?と言った具体的な仕事の内容まで含めたロールモデル的な人を数人見つけて、参考値として持っておくと、実は結構チートでキャリアを進めるんじゃ無いかなと思います。

自分の人生は自分で切り開くものだと思いますが、一方でチートで進めるところはある程度楽して先に進んで、次のもっと大きな課題にさっさと取り組んで言った方が、キャリアのレバレッジは効くと思うので、そういう観点も持ってるといいんじゃ無いかなと思いました。

チャンスの数は平等だが、チャンスを掴み取れるか、には差がある

「やり切ることができる力」を軽視してはいけない

チャンスを掴み取れるか?という一点において僕が伝えたいのはほぼこれなんですが、何か問題がある、課題がある、イケテナイことがある、というのを見つけたら改善するまでやり切ってください。

このやり切る力は単純に技術力があるとか、そういう話に止まりません。コードを書けるてもコミュニケーションが取れなければ、結局を何かを「遂行し、完遂すること」は難しいでしょう。

仕事を進めていくには、実装力に限らない、総合力が求められます。 僕はこの総合力のことこそを「技術力」と呼んでいます。

ビジネスオーナーから何か依頼が来たときに、できない理由を並べることは簡単ですが、それがどうしてできないのか?どうしたらできるのか?実際にやり切るにはどうすればいいのか?までを考えて伝えたり、さっとプロトタイプを実装して PoC できたり、なんというか総合力みたいなものが仕事では実は大事です。このリモートワーク環境下では文章にして伝えることができる力も必要になるかもしれません。

ありとあらゆるスキルが一定以上あって、何かチャンスが来た時にそれら全てを用いて「やり切る」ことができ、そしてその積み重ねが信頼となって、さらに色んなチャンスが舞い込んでくるようになると思います。

この「やり切る力」決して軽視してはいけないと思います。

打席でボールを待たない。ボールを引き出しチャンスを決め切る力が大事。

これは比喩を使って説明します。

よく若い時は打席に立て、という表現を聞いたことがあると思います。これはあながち間違いではないと思いますし、おそらく経験を積むべし!っていう比喩としては妥当なんだと思いますが、そもそも打席に立っててもそもそもボールって投げてもらえるんですかね? 僕は野球も好きなので、この例えが間違ってるとは一切思いませんが、とはいえ実態とはズレが生じて来てる例えなんじゃないかな?と思っています。

このズレを解消する例えでサッカーのオフザボールという概念を持ち出したいと思います。
(僕はサッカーが大好きなので、なんでもサッカーで例えがちなんですが、、、笑)

サッカーのボールがくることをチャンスと捉えると、

  • 走った先にスペース(=チャンス)があるのか?
  • そもそもスペース(=チャンスのあるところ)に向かって走ってるのか?
  • パスが来たときに決めれるだけの決定力(= 技術力やり実行力を合算した総合力)を持っているか?
  • 味方を生かすためにデコイ(囮)になることを厭わないか?

サッカーわからない人にはわからない例えかもしれませんが、チャンスを見つける嗅覚と見つけた時にいち早くアプローチして決め切る決定力があって初めて信頼され、仕事を任せてもらえるということを言いたかったんです笑

また仕事はチームでするものなので、「俺が俺が」じゃなくて時には誰かを生かすために自分が囮(犠牲)になることも大事です。

事業環境は日々変化してるし、自身の役割も日々変化し続けるものだと思うので、その都度最適なポジショニングをしながらチームとして勝ちに行ける哲学を共有するのが大事なんじゃないかな?と思っています。

苦労はしておいた方がいい

じじくさいことを言ってますが、これも生存バイアスの一つだと思ってください。 この苦労についてですが、そもそもの前提として心理的に追い詰められたり、人格を否定されるような苦労は絶対にしては行けませんし、そんな人がいる環境はさっさと逃げた方が良い、ということは先に述べておきます。

あくまでスタンスの話でもあるんですが、仕事、特に業務遂行において現れるさまざまな苦労はしておいた方がいいです。ことあるごとにぐちぐち文句言ってるだけは何も変わらないので、それを改善するためにあれこれ動いてみるといいと思いますし、その過程で発生する苦労を克服したことがそのまま自信に繋がります。

勝ち癖が大事

仕事に勝ち負けの概念を持ち出すこと自体を嫌う人は一定いるかもしれません。また何を持って「勝ち負け」なのか?は人によって解釈は様々なので、何を持って勝ちとするか、から定義する必要があることも理解はしています。

ここでいう「勝ち癖」というのは、「自分の中で成功経験・やり切った経験」と言い換えます。これを僕は「勝ち癖」と表現します。

これを明確に意識したのは去年のことで以下のまとめにあった内容がそのまま僕の考えに一致したのでこの項目を設けることにしました。

togetter.com

勝ち癖をつけるっていうのは自分の中で成功体験を作り続ける(成果を出す)癖をつけるってことです。

割と失敗を賞賛する文化があると思いますが、僕は失敗自体はただの失敗だと思っていて、価値があるのは失敗から学んで成功することだと思っているんですよね。あくまで成功する(成果をあげる)ことが前提で、それを目標としない失敗は、なんの学びもない単なる失敗で自己肯定感が下がるだけのものだと思っています。

常に成果を出していたり、一定のメンタルの元に仕事を進めることができる人っていうのは自分なりの成果を出すパターンをいくつか持ってるし、過去何度も定期的に結果を出してきた人だと思っていて、こうすればXXXできる!っていう自信を元に仕事をしてる気がするんですよね。

そういう人を見てきてるからこそあえて自分なりの「勝ち癖」をつける、っていうのは仕事を進める上でもメンタリティーの面でも大事なことだと思います。

仕事は「チームでするもの」である

これまでに何度か話に出していますが、趣味と仕事の違いは一番はこれだと思っています。

結局個人で好き勝手してるだけでは出会うことなかった課題がチームで仕事をしてると頻出します。それを一つ一つ解決していきながら、課題を解決していくプロセスそのものに面白みを感じることができるか?、仕事とは個人ではなくチームですることだという前提に立つことができるか?というのが、大事です。

キャリアについての余談

キャリアについて語ってる人はそのほとんどがポジショントークで生存者バイアスがかかっている

ほんっと余談なんで軽く聞き流して欲しいのですが、割とキャリアについてあれこれ語ってる人(このエントリについての僕含め)の「hogehogeするべし!」とか「fugafugaしないなんて〜」みたいな言説の出どころは大体個人の経験に依存してるので行ってしまえば N=1 のポジショントークなんですよね。

キャリアはあってないようなものだし、基本的にはその人と同じことをしたとして、同じ道を歩けるわけではなく再現性はないので、生存者バイアスのかかったポジショントークだと思って、全てを取り入れうのでなく、自分にあったところをエッセンスとして一部取り込んだりう参考にするくらいにしておくほうが、他者と比較しないで楽しく過ごしていけると思います。

2020 年の振り返り

振り返りの最後です。

Overview

30代に突入してしまった今年1年の振り返りをします。

過去ログはこの辺

ema-hiro.hatenablog.com

ema-hiro.hatenablog.com

2020 やったこと

転職

From: DeNA
To: Eureka

退職エントリと入社エントリは以下です。

ema-hiro.hatenablog.com

www.wantedly.com

転職して10ヶ月くらい経ちますが元気に過ごしています。

引っ越し

社会人生活始まって以来6年住んだ三鷹市を離れ、練馬区民になりました。
7年目にしてついに23区内にやってきました。真の都民に昇格です。

家も1DK から2DK にスケールアップしました。家賃も少し上がりましたが、仕事スペースとリビング的な居住スペースを分けることができて快適です。
家とインスタンスサイズはデカいに限るが圧倒的正義です。

原則在宅勤務にもなったので、公私で空間を分けることができたのはラッキーでした。

在宅勤務

Covid-19 に伴い原則在宅勤務になりました。
この業界、いつかそんな日が来るんだろうな、とは思っていましたが、まさかこんなに急に、しかも半ば強制的にやって来るなんて思ってもみませんでした。
始まって1ヶ月くらいは、ON/OFF の切り替えができなかったり、人に会わなさすぎて寂しくなったりして、在宅勤務厳しい、、、やっぱりオフィスがいいよ!とか思っていたんですが、1ヶ月すぎたくらいから全く苦じゃなくなりました。慣れって偉大ですね。
社会人生活始まって以来ずっと苦手な朝に起きて出勤していたのに、ものの1ヶ月くらいで週5日会社行くのは不可能な体になりました。人間は安きに流れやすいですね。 

おかげで夏に緊急事態宣言が明けるまでは、ほとんど自宅で過ごしていました。
7月になって自粛の反動で週3くらい出社するようになりましたが、依然として週末は自宅で過ごすことが多かったです。
ただ夏本番になったら外に出るのがつらくて結局在宅メインになりました笑

涼しくなった頃もまた少し出社するようになりましたが、11月になって寒くなったのですぐに引きこもりました。

結果として。1日の大半を過ごす仕事が在宅になったおかげで、1年の半分以上を自宅で過ごしていました。 

そういえばリモート飲みとかもやってみましたけど、思ったより楽しくなくて数回で飽きました。 
飲みに行くならやっぱりオフラインがいいなと思います。
もちろん状況を考えて、、、にはなるんですが。

仕事忙しい

仕事が忙しかったです。別に今までが暇だったわけではないです。
転職したてて慣れるのに無我夢中だったのに加えて、初めてテックリードというポジションを任されたのもあって8~11月くらいは何かに忙殺されていました。

その振り返りを一昨日書きました。

ema-hiro.hatenablog.com

遠出しなかった

元々自宅は好きなのでさして苦でもなかったのですが、帰省もしなかったのであんまり遠出できなかったことは心残りです。
GoToなんちゃらもほぼ使わず、11月に日帰りで京都に行ったのと、12月に開発合宿で函館に行ったきりでした。

来年はもう少し気軽に出かけられるようになるといいな。

2020年やったこと - 番外編

キャッシュフローが改善した

そもそもオフラインで会うことが著しく制限されることになったので、今まで飲みに行ったり外食に使っていたお金がそのまんま残るようになりました。
同時にクレジットカードを使う回数を減らして、大体をチャージ式のプリペイドで払うようになったので、クレジットカード前提のキャッシュフローの改善にもなりました。

また、在宅メインになったので自宅のインターネット回線をスマホでも使うようになり、パケット消費も劇的に減ったので、MVMO に乗り換えました。
スマホ代が 1/3 くらいになって、インターネット回線全体でも1万円以下で済むようになったので家計にとても優しくなりました。

この他にもいくつかちまちました金遣い(※1) が改善したおかげで、固定費が削れて特に意識せずとも貯金ができるようになりました。
(一部は在宅勤務の設備投資に消えて行きましたが)

※1. 必要なくなったサブスクリプションを全て解約したり、出先でのカフェによったりした時のちょこちょこした出費がなくなったためです。

投資継続した

去年の終わりくらいから楽天証券に口座を開いて貯蓄 + 投資始めてたんですが、今年も継続して運用してたらそこそこ資産が増加してました。
口座開設するなら正直どこでもいいと思いますが、僕は私生活のほとんどを楽天経済圏で完結させてるので楽天使ってます。
定期的に数字見てますが、パフォーマンスは悪くないと思います。
個人的には、メガバンの口座を貯蓄に使ってた時よりも確実に旨味が大きいと思いました。
(金利的にも、銀行そのものの機能的にも)

興味があって始めてみたいと思ってるなら、ネット銀行を使うというのは結構いい選択肢かなと思います。

運動継続した

2019年の夏の終わりにコーチ (@konchan) に強引に筋トレに誘われてから1年ちょっとくらい経ちますが、ジム、時々リングフィットで運動継続できてます。
緊急事態宣言中はジムに行けなかったので、週4,5 のペースで狂ったようにリングフィットをやってました。その甲斐あってか引きこもりだったのに2kgくらい体重落ちた + 足が少し細くなりました。(スッキリ美脚セットをアホみたいに毎日やっていたため)
自粛明け以降、最近はジムに行けるようになったのでリングフィットはちょっとお休みしてます。

自炊継続した

去年できなかったレパートリーが少ない課題を少し改善できました。
これもほとんど外出自粛が原因ではあるんですけど笑

無水調理鍋を購入してみたんですがこれが効果ありました。材料放り込んで蓋して待つだけでそこそこ美味しくできるので自炊捗ります。

自炊してたら美味しいご飯を食べたい欲がふつふつと湧いてきたのでお高い炊飯器の購入を検討中です。

2021年どうする?

仕事面では少し事業的な側面での要求に応え続けてきた1年でもあったので、2021 年は少し技術寄りなコミットをしていきたいなと思っています。
Go もまだまだ深掘りたいし、AWS はだいぶ雰囲気で使ってるので terraform とかその辺りも含めてもう少しちゃんと理解して使いたいなと思っています。

技術や手段そのものにどっぷり浸かった上で使いこなせるようになりたいなと思ってます。
そのためにもとりあえず2020年は全くと言っていいほど本を読む時間を取れなかったので、今めちゃくちゃ溜まってる積読を消化したいです笑 (数万円分が負債として溜まっている...)

今後もおそらく在宅で仕事をすることが前提になると思っているので、今年以上に自宅への投資をしたいなと思います。
とりあえず10年戦士の冷蔵庫と洗濯機を2021年前半で買い換える予定です。

あと、今年やるやる詐欺になっていた英語....笑
これは来年絶対にやりたいです。
今はそんなでもないですが、多分仕事していくうちに現職だと本当に英語使えないとどうしようもない事態になりそうなので、ここはなんとか時間見つけてコミットしたいと思います。
余談ですが、プログラミング言語1つ覚えるより英語ちゃんと使える方が、キャリア全体を考えると多分コスパがいいです(個人の見解です)

あんまり高々と目標を掲げても何もできずに終わるのでこの辺にしておきます。

それではみなさんよいお年をお迎えください。
来年もこのブログをたまによろしくお願いします。

2020年買ったものたち

振り返り記事第3弾です。 もうそろそろネタ切れなので明日は多分お休みします。

Overview

2020年買って良かったと思ったものを振り返ります。 今年はなんといっても Covid-19 の影響で原則在宅勤務になった1年だったので在宅ワークの設備を整えるための物が多いです。

買ってよかったもの

FlexiSpot スタンディングデスク(天板付き)

なんといっても大きな買い物はこれでした。
原則在宅になったこと + 引っ越しをしてそもそもの作業専用の部屋を作ってでかい机を置くスペースができたので買う決断ができました。

中学生の頃から使っていたデスクを入れ替えたので若干名残惜しかったのはありましたが、在宅ワークをする上で絶妙にエルゴヒューマンと大きさがあってなかったのもあって一思いに乗り換えました。

結果としてはめっちゃ満足してます。まず何よりデスクが広いは正義です。
また、電動で昇降してくれるので高さの調整がとても楽です。たまに立って仕事してます。
ちなみに昇降デスクを買って感じたのは「立って仕事をすると座ってる時より疲れる」ということでした。

hb.afl.rakuten.co.jp

このデスクは会社の補助が使えたのでお財布にも優しかったです。現職のブログにも載ってます。

medium.com

ロジクール MX ERGO

ずっと Apple の Magic Trackpad を使っていたのですが、手首が疲れて腱鞘炎になりそうだったので、手首への負担が少ないトラックボールマウスに買い替えました。
慣れるまで時間がかかりましたが今のところいい感じです。

Anker PowerConf S3

Zoom で会議することがすることが増えたので、聞き手のことを考えて買いました。
会社では専用のマイク買う勢もいて、悩みましたがスピーカーとセットで欲しかったので Anker PowerConf にしました。

割といいマイクを使ってる人の声を聞いてるとPC備え付けのマイクって大したことないんだな(というか専用マイクすごい)と思いました。

これを買うまではずっと Airpods Pro を使っていたのでどうしてもつけっぱなしだと耳が痛くなったり、電池切れ起こしてしまうことが多かったので、耳の健康のことを考えて買ってよかったです。お値段もそんなに高いわけではないのでエントリー的な意味合いでもおすすめです。

ロジクール ウェブカメラ

PowerConf とセットで買いました。PC のカメラでも事足りてましたが、普段 PC の画面はサブディスプレイ的に使っていて、正面においていないので MTG の時とか印象悪いかな?と思って正面がから映すように買いました。HD なので多少画像が PC よりも綺麗かな?とは思ってます。

SHARP 加湿空気清浄機

これも会社から補助が出て買いました。
(どこまで出るんだ...)

作業部屋の空気環境は大事かなと思ったので。
そして今の時期に加湿機能があるとエアコンで乾燥しすぎないので助かってます。

最近秋口にも花粉症が出るようになってしまったのですが、空気清浄機が来て以降、作業部屋では花粉を感じにくくなったので割と効果はあります。

hb.afl.rakuten.co.jp

LG 35型曲面型ウルトラワイドモニター

冬にまた補助が出たのでこれの購入に当てました。
(補助が出ることにもう慣れました)

元々普通のモニター(27インチ)使っていたのですが、会社では局面モニターを使っていて、使いたいなと思っていたもののコスパに見合ういい感じの局面モニタが見つからずにいました。
(ゲーミング用とかで無駄にスペックが高くて高価なやつとかあって決めかねていました)

ただ、どうにも既存のディスプレイの解像度が低くて逆に目が疲れることが多かったので、色々調べてるうちに、これをお勧めされて購入しました。

よかったのは作業効率が上がっただけでなく、ケーブル類がスッキリしたことです。
画面が大きいだけでなく、Type-C給電できるので、カメラやキーボードをディスプレイにつないで、PCとはType-Cのケーブル一本繋げば完結するようになりました。

a.r10.to

ケーブルトレー(メッシュ・汎用タイプ)

ケーブル一本でPCとディスプレイを繋げるようになったとはいえ、ケーブルや蛸足がデスク下にごちゃってる状態だったので、デスクに簡単にかけられるケーブルトレイを買いました。

これのいいところは取り付けが天板に穴を開けるタイプではないので取り付けが楽ってところと、メッシュ生地なのでたまに足が当たっても痛くないってところです。
Amazon楽天でもこれが欲しくて色々探してたんですが、アルミ製だったり、取り付けが面倒なタイプしか売ってなく、たまたま見かけたブログでサンワサプライ本家でいいやつが売ってることを知ったので速攻でポチりました。

机の下もスッキリして満足してます。

direct.sanwa.co.jp

iPad Air + MagicKeyboard + Apple pencil2

プライベートPC で開発することがほぼないので、プライベートPCを処分した後、大体の個人の作業はスマホで事足りてたんですが、DAZNやネトフリ見るのにはやっぱり物足りないので、何かいい感じのものを探してる時にいっそ iPad Air と Trackpad 付きのMagic Keyboardで事足りるんじゃないか?と思って買ってみました。

結果としては、これはもう開発用途以外であれば PC と遜色ない使い勝手かなと思います。
最近手書きすること少なかったんですが、 Apple pencil 使って結構手書きでメモなり思考の dump を取るようになりました。

ちなみに Pro じゃなくて Air にしたのは自分の使う範囲だと Air で十分だったのと指紋があったからです。

www.apple.com

www.apple.com

www.apple.com

iPhone SE2

iPhoneX から機種変で変えました。
マスクをして外出することが当たり前の1年になってしまったので FaceID がほとんど役立たず(若干改善されたらしいけど)になってしまい、どうしようか悩んでいたんですが、中身が 11 で指紋が付いて安くて、小さくて、軽い、という点が自分の欲しかった要件に一番近かったので一思いに機種変しました。

正直カメラに拘らないならアリな選択肢だと思います。一括購入 + MVMO に乗り換えたので携帯代が 1/3 くらいまで圧縮されて家計的にもハッピーでした。

www.apple.com

ただ、12 mini に乗り換えるか悩んではいます...iPad Air についてる指紋が来年 iPhone に搭載されるなら mini で乗り換えも検討します。

フットレスト

エルゴヒューマンアーロンチェアを使っているのですが、絶妙に足が床と噛み合わなくていい椅子を使っているのに姿勢が悪く、疲れがたまることがありました。
そのため試しにフットレスト置いてみたんですが、足おきがあるだけで座ってるのがとても楽になったので、フットレストおすすめです。

これは箱型なので中を収納にも使えます。

番外編

KindleFire TV Stick 4K

元々 FireTV Stick ユーザーだったのですが、4年前に買ったもので流石に動きがカクカクで体験よくないし、DAZN などつけててもたまに意図せず落ちる、みたいなことが頻発していたので、これは流石にもうそろそろ耐えられなくなってきたか?と思って最新の FireTV Stick に乗り換えました。

4年前のものしか知らなかったので、最新はめっちゃサクサクに動いて快適でびっくりでした。FireTV Stick も消耗品なんだなーと実感しました。
今後は定期的に買い換えようと思います。

エルゴトロン モニターアーム

これは買ってないです。
@linlymatsumura にもらいました。

モニターアームもずっと懐疑的でしたが、壁にモニターを近づけられる + デスク広く使える、というのであるとすごく役に立ちました。感謝です。

RAVPower モバイルバッテリー

これは買って失敗したものなんですが、屋外でもPCが充電できるバッテリー欲しいと思って年明けごろに買ったんですが、STAY HOME の風潮になってしまい、カフェで作業するなんてことをしなくなったので、あんまり意味なかったです。

リングフィットアドベンチャー

これを入れ忘れてました!!!
在宅のお供。まじでこれはゲームなのか?っていうくらい運動させられて、在宅の運動不足が解消しました。

5月〜6月は狂ったように結果をツイートしてました。

www.nintendo.co.jp

マウスパッド (横長)

滑り込みで買いました。
手首が痛かったので、キーボードの下にも敷けるように横長を買ってみましたが、結構いいです。

キーボード叩くときに手首あたりが痛くないですし、冷たい天板に直に触れなくていいので寒くないです。

a.r10.to

まとめ

今年はガジェット類でめちゃくちゃ経済回した1年だったなと思いました。

半年間テックリードをしてみての振り返り

年末なので振り返り記事2段目です。

Overview

今回は現職で今年の7月から半年間テックリードをやってみての振り返りをしてみたいと思います。

前提

僕について

  • キャリアの中でリードという立場でチームを作ることになったのは初めて。
  • Covid-19 の影響で初めてリモートを前提として仕事を進めるようになった。
  • 技術的に尖っているわけではない。サーバーは一通り開発できるし、フロントもかじったことはあるけど専業ほど詳しくはない、っていう感じ。 いわんや、モバイルアプリ周りの知見はほぼゼロ。

テックリードの役割について

  • おそらく会社ごとに責務は異なっていると思うのであくまで現職での話がベースであること。

Good & More

Good & More という形で振り返ってみます。
ちなみに形式としてはいわゆる KPT ですが、Problem がどうしても多くなり Keep が出づらいという話もあるので、いいこと及びもっとよくなること、という分類でやってみようと思います。

Good

  • チームで仕事することをより強く意識するようになったこと。
  • 事業ロードマップを意識するようになったこと。
    • ロードマップが決まる時点での情報や会社の動きなど、現場レベルから2レイヤーくらい上の動きに敏感になりました。
    • 自分の意思決定に関わる事象は大なり小なりキャッチアップするようになりました。
  • コミュニケーションを意識するようになったこと。
    • 仕事のほとんどは意図がバッチリ擦りあってるかどうかで決まる、ということに気づいたのでここに自分のリソースの大半を全集中してました。
  • 本格的にプロジェクトマネジメントについて考えるようになったこと。
    • いい方法はいまだに模索中。
    • この半年に限ってはWF(ウォーターフォール)形式を採用して進めましが、いい面の方が多かったです(あくまでツールの選択の話です。)
  • 役割をはじめに明確化したこと。
    • テックリードとしての役割の定義、チームに対して期待することを初期に明文化してました。こういうことをできるようになったんだなと自分で驚きました笑
    • これをやっていたからかはわからないけど、非同期の活動を当たり前のこととして、リモートにうまく対応できました。

More

  • 事業について
    • 実行の速度が落ちることがあったな、と思っています。
      • 現場レベルでの実行力・施策遂行の練度についてはまだまだ改善の余地があるなーと。いくつか Todo にも落とし込んでることがあるので実施していきたいと思ってます。
      • 「全員が王騎になる (= 単騎で戦局を変えてしまう)」くらいを目指したいなーとか妄想してます (※1)
  • コミュニケーションについて
    • ある程度フォーマットを決めていましたが、どうしてもすり合わせに時間がかかりました。
      • 個人の自由度を縛らない程度にコミュニケーション方法や報告内容もフォーマット化したほうが余計なノイズが減りそうな気がするので来年トライしてみたいと思っています。
      • コミュニケーションについては「答えじゃなくて観点を共有する」ということを前職時代の上司が言っていたのを思い出したので、これを実践していきたいなーと思っています。
  • 自身について
    • 発言大丈夫だったのか問題
      • 元々口がきつい、あんまり人の話を聞かずにサッと片付けてしまう方なので、ちゃんと話を聞くこと、そして意図した通りに伝わっているか?ということに結構神経を使いました。まだ不十分だなーと思ってるので来年も継続したいです。そして気を遣える程度の余裕を常に持っていたいです。余裕がなくなると気を遣えなくなるので。
    • もっと技術的にチャレンジできたのではないか?
      • Advent Calendar にも書きました(※2)が、ある程度できたと思う一方で、個人のスキルアップ的な意味でも少し技術的なチャレンジ要素をもっと入れていきたいなと思っています。
    • 事業のことを考えていたか?
      • 100% というと嘘になりそうです。どうしても目先の事業より技術的な改善なり、回ってはいるけどちょっと厳しい運用の改善なりにフォーカスしたくなってしまったなーと思っています(職業柄)この辺の折り合いをどうつけるかについてはまだ模索中です。

※1. コードギアス好きな人向けに説明するなら「戦術が戦略を凌駕する」ってやつですね!

※2. 以下のエントリです。

medium.com

やってみての所感

いくつか感じたことはあるんですが、大きなものとしては「視座の変化」と「実行へのコミットメント」というところが自分の中でも大きく変わりました。

まず一つ目について。 こういうことをいうと恥ずかしいのですが、僕は Good に書いてあるようなことの中でも、特に事業に関連することなどは実は今まで全くと言っていいほど意識したことがなく、業務の中で自分の担当するタスクを大なり小なり自分で順序立てて仕事を進めているような人間でした。 事業に対して俯瞰的な視野を持つようなことを明確に求められているわけでもなかったので、そもそも意識することが必要だったのか?と言われると正直そんなこともなかったとは思ってますが、まぁ目の前の技術的な課題をひとつひとつ解いていくことを主眼に仕事をしていて、事業全体を把握しながら業務にコミットするような動き方をしていたか、と言われるとそうじゃないな、というのは事実としてありました。

ただ、自分がチームを作っていくという立場になって初めて、上の方も見てるとちらほら出てくる「めんどくさいあれこれ」的なものがいい感じに隠蔽されてたことを知って、歴代の上司の皆さんには頭が下がる思いでいます。
考えたくもないこと(プロダクト開発の邪魔になりそうなこと)で脳みそのCPUが食われたり、MTGに呼ばれ続けて気づいたら夕方みたいな生活を送っていると「自分が今までどれくらい守られてきたのか?」ということを痛感しました。

次に二つ目について。 この半年は常に「実行」について考えていた半年でした。施策を前に進めるには?ということを考えることが多くて、前に進まないブロッカーはなんなのか?取りこぼしてるボールはないのか?といった障害になりそうなものを早めにキャッチして適切にボールを回すと言ったことにコミットしてました。ともすると思考のノイズにもなるので、結構疲れましたが、どこでボールが溢れそうになるのか、溢れないためにはどうすればいいのか?、そもそもボールどこにある?と言ったことに目を向けるようになって、ロードマップの推進を上手い感じに俯瞰してみれたり構造的に捉えることができるようになったのは良かったです。

あとやってて気づいたんですが、「前に進んでる実感」がないと人間は確実にダレます。僕がそうだから余計にわかります。
これに割と早い段階で気づけたことは良かったです。僕が元来飽きっぽい性格だからですかね。
途中で、あかんこれは飽きる!みたいな謎の危機感が湧いてきたので、施策を進める中でエンジニアリング領域の改善作業を入れ込むモチベーションにもなりました。

来年に向けて

初めてのテックリードということもあり、僕は自分が手慣れていたウォーターフォールを採用してプロジェクトの推進を行っていました。

手法自体はなんでも良かったのですが、役割自体が初挑戦で不確実なことが多く、その不確実性に対して割く自分の時間がわからなかったので、極力意識そのものをテックリードとしての役割に傾けたかったというの理由です。

外部サービスとの連携だったり、ステークホルダーが多い中で仕事をすることが今年は多かったのでこの進め方が比較的ハマりましたが、来年バリバリ改善してく系も入ってくるので、完全にウォーターフォールによることなく、案件によっては少しアジャイル的なエッセンスも入れていきたいなと思っています。

まとめ

Good & More という形で振り返りをしてみましたが、自分としてはポジティブな半年間になったと思います。 振り返ってみて気づきましたが思ったより Good の方が多くて、自分のことを少し褒めてあげたくなりました。

「立場が人を作る」とは使い古された表現にはなりますが、まさか自分がそんな風になるなんて思いもみませんでした。
このこと自体はこうしたチャンスをもらえない限りずっと視座が低いままだったので、負荷はかかるけど視座を上げてもらえるような仕事をさせてもらえたことは感謝です。

またリモートワークが前提となったことは最初は厳しかったけど自分自身の常識のアンラーニングには非常に寄与してくれました。

リモートワークだからこそ、

  • よりチームで仕事をすることを意識する。
  • 物事を前に進め続けていかないと、チームの雰囲気が停滞してしまう。

と早い段階で気づくことができたことはよかったです。

僕自身は初めてのリードという立場でもあったので、チームで活動するためのいい感じの潤滑油になれればいいなくらいに最初は思ってたんですが、今年最後の振り返りでチームメンバーからポジティブなフィードバックをもらうことができたので、この点についてはある程度はできたのかなと思って自信になりました。
振り返りでの Good に起票されたこれは素直に嬉しかったです。

f:id:ema_hiro:20201228041430p:plain

総じていいチャレンジができた半年でした。
来年は潤滑油にとどまらず、ガソリンにもなっていきたいなという謎な目標を明言してこのエントリを締めたいと思います。

テストを書くことについて個人的な考え

年末というのもあって色々振り返り記事を書いていきます。
一応他にもエントリを用意してますが、これで終わるかもしれません笑
振り返りも兼ねて思考を吐き出してるので、若干文章が拙いかもしれないのはご容赦ください。

Overview

とりあえず個人的振り返り記事第1弾はテストについてです。
自分の中で考えてた「テストを書く」ということについての考えをまとめておきます。
ちなみに自分は特にテストに詳しいわけでもなく、いつも実装をする中で普通に単体テスト(いけそうなら E2E テストも)を書いてる程度のエンジニアです。

ちなみに来年の今頃には全く違う考えを持ってるかもしれません。

TL;DR

ざっと僕がテストについて考えてることは テストはやっぱり書いた方がいいと思う ということです。

そう考えてる理由は以下です。

  • テストを書いた方が結果的に 色々と 速いと考えてること。
  • テストの 書き方 がわかること。書き方がわかることで見えてくるものがあること。

テストを書いた方が結果的に「色々と」速い

こう考えるのは以下のようなことがあるからかなーと思っています。

  • 思い切ったりリファクタリングができたり、実装ミスにいち早く気づける。
  • 単体テストでデバックを半自動化できる。
  • レビューで突っ込まれた時に「テストを書かない理由」を説明する方が時間がかかる。

思い切ったりリファクタリングができたり、実装ミスにいち早く気づける

まず1つ目ですが、まぁこれは言わなくてもわかることかなと思います。
振る舞いを変えずに実装の詳細に変更を加えた時に、その変更が他の箇所に影響を与えないかをすぐに検証できます。

単体テストでデバックを半自動化できる

僕は普段 Go で開発してることが多いので、 実装が意図した振る舞いになってるのかを go test ... でシュッとテストしてます。
(似たようなテストの機構がついてる言語が他にもあると思うので、これ自体は Go に限った話ではないですが。)

実装内容を単体でシュッとテストできる方法を身につけるといちいち local でサービスを立ち上げたりしなくていいので、デバックを効率化できます。(※)
というのもモノリスなアプリケーションだったりすると、local で動かすだけでも時間がかかったり、メモリをバカ喰い(主に鯨くんが)して他の作業に影響が出たりします。 (高いスペックのマシンを使わせてもらうべし、だとは思っていますがw)

※ これと関連して curl コマンドにも習熟してると API の検証がやりやすいです。

実装の中でクラウドベンダーのリソースにアクセスしてるケースもあってテストそのものができない(テスト回すたびに課金されたりとか嫌ですよね?)ものもあるかもしれませんが、最近はクラウドベンターもMockツールやエミュレーターを用意してくれているので、クラウドのリソースを使っている実装でも実はテストが簡単になりつつあります。 AWS だと公式のドキュメントで以下のようなものが用意されています。

aws.amazon.com

レビューで突っ込まれた時に「テストを書かない理由」を説明する方が時間がかかる

あと最後に大体テストがないとコードレビューで突っ込まれます(経験談
テストを書かない理由はまぁいくつかありますが(一つ後述します)、まぁ大体サボったりめんどかったりみたいなことがあって、書かなかったり、書き方がわからなくて書かない、みたいなことがあって、まぁ言っちゃうと客観的で妥当な理由がないんですよね。。。

僕は最初の方こそ「書かない理由」を探してたんですが、そのうち気づいたんですよ、書かない理由を説明するよりテスト書いたほうがレビューが approve されるのが早い(=ユーザーへの機能提供のスループットが上がる)、ということに...笑

これがわかって以来、僕はテストを書いてます。

「テストの書き方がわかる」

禅問答みたいな理由ですが、もうひとつの理由です。
昔の自分がそうだったんですが、テストを書かない理由の一つが「テストの書き方」がわからないってあると思うです。

テストに限らずなんでもそうですが、「やらないとやれるようにならない」のでテストを書く意味を先に考えるんじゃなくて、まず書いてみて、書き方を先に覚えてからなんでテスト書くのいいんだっけ?ってところを考えるのがいいと思います。テストを書いてるうちに、そもそもの「テストしやすい設計」というやつが頭に入ってくるようになると思います(テスタビリティが高いってやつですね。)

この辺は自分の経験談なので、N=1 ではあるんですが。

以上が僕がテストを書いた方がいいと考えてる理由ですが、とはいえ、例えば実装と単体テストが密に結合してしまうとアジリティが落ちるケースはあると思います。とはいえ、それは多分ある程度までは回避できると思います。というか普通に実装変更したらテストで落ちて欲しいですね。そのほうが思い切ってリファクタリングできるし、コードを書いてて怖くないかなと思います。

そしてテストの話でよく話されるのはスピードとのトレードオフの話ですが、それについては以下のスライドあたりで今年1年 たくさん語られるようになったんじゃないかなと思います。 (何度も改訂されてるのに毎回読んでしまいます。本当にすごいスライドだなと。)

speakerdeck.com

ちなみにテストと開発効率の話については所属してる企業のアドベントカレンダーにも書いてます (ちなみに僕は筆者の daisuzu さんにテストについてめっちゃ教わりました笑)

medium.com

脳死でテストを書く、という立場の是非について

僕は今のところ実装時にはほぼ脳死単体テストくらいはセットで書く事にしてます。

ソフトウェアエンジニアとして「脳死で」 XXX することへの違和感、嫌悪感は確かにあります。これはテストも例外ではないと思っています。
ただ、「実装においては最低でも単体テストは書いておく」ということ前提にして実装する癖をつけること、チームにおいては制約を設けることだけでも、後になって恩恵を受けることは多いと思うので、とりあえず書いておこうって思います

テストもプロダクションコードと同じでそれ自体が「プロダクトの資産であり歴史」になると思います(※) テストを書いたことでテストしやすい設計やコードの書き方について思いを馳せるようになったり、運用フェーズに入ってプロダクトでテストが書けずにデバックが捗らないなどのツラミにぶつかった時に「ここをこうすればいい」みたいなリファクタの指針みたいなものを手に入れることもできます。

※ ちなみに資産と捉える以上、それが負債になる日もある。(なぜなら資産は正負を問わないから。いわゆる技術的な負債ってやつが言葉としてはそれ)

まとめ

2020年終わりの自分のテストに対する考えをまとめてみました。
冒頭にも書いた通り、今時点の考えなので、半年後、1年後に全く違うことを言ってるかもしれません。

余談: 事業が優先されるケースでテストを書いてる暇なんかないんじゃないか?論争について

これもまぁある話かなと思っています。
1ヶ月後に残ってるかもわからないプロダクトのコードにテストを追加する意味あるのか?問題です。
僕もそんな状況に追い込まれたら流石にテストは書かないんじゃないかなと思いますし、というかそんなもの描いてる余裕は多分ないと思います笑
あとはなぜか数日後が締め切りのマーケ対応とかもそれですね。
運用フェーズに入ってるプロダクトでもこういういった類の話はあることかなとは思います。

こんなこと言ったら怒られそうなんですが、この問題について自分の現時点での考えをまとめておくと、そういう環境で求められる実装力はおそらく相当高いので、テストで気づけるような実装ミスを頻発するようなスキルではそういったスタートアップ的な環境では活躍できないんじゃないか、ということです(厳しい見方過ぎますかね..w。僕はとてもじゃないけどそんな環境に身を置けませんw)

数年前ならいざ知らず、市場が成熟し、ユーザーの目も肥えて、各種規制や制限が加わったことで事業運営自体の難易度も上がってる今は、スタートアップと呼ばれるフェーズの会社でも一定程度のプロダクトの品質は担保できて当然のように思われているので、そもそも品質への投資の優先度も上がっているはずです。

バグが起きても直せばいいっしょー、それよりユーザーに価値を提供する方が大事っしょ!っていう考えももちろん大事ですが、それはそのバグ(とそれに伴う影響と復旧までの時間を加味した全体影響)の深刻度によるという話だと思っていて、ミスっても早急にロールバックできるなら別にいいと思います。
ただ、そうじゃないならテスト書いておく、それも加味してスケジュール組む方が現状だと安全だし、そういった小さな品質保持への投資がプロダクトとユーザーのためになるんじゃないかなと思っています。

割と書かない理由の最たるもので上がってくる話かなと思ったので余談ですが書いておきました。

Elastic Container Registory に Image をアップロードする

Overview

Lambda on Container を試す際に Elastic Container Registory (以下 ECR) にコンテナイメージをアップロードする必要がありました。 このエントリではその ECR に Docker イメージをアップロードする方法について記載します。

以下に書かれてる内容と大体同じです。

aws.amazon.com

前提

以下が前提になります。

  1. AWS ECR を操作できる権限を持った Role でログインしていること。
  2. ログインした Role でクレデンシャルを取得し、aws コマンドを叩く権限を手に入れていること。

2 については権限を持ってるユーザーであれば local から aws コマンドを叩いても可能ですが、権限を持った Role でログインして CloudShell を使うのがいいかもしれません。

ちなみに僕は AWS に慣れていないのでクレデンシャルの取得方法で悩みました。

dev.classmethod.jp

イメージをアップロードする

# アップロードしたいイメージのディレクトリを掘る
$ aws ecr create-repository --repository-name $DirName/$imageName --image-scanning-configuration scanOnPush=true

# 手元のイメージを ECR 用のイメージとして作成する(タグ切る)
$ docker tag $localImageName:$Tag $ECR_URL:$Tag

# ecr のクレデンシャルを取得して docker にログインする
$ aws ecr get-login-password | docker login --username AWS --password-stdin $ECR_URL

# Image を push する。
$ docker push $ECR_URL:$Tag

$ECR_URL$AccountID.dkr.ecr.$Region.amazonaws.com になります。

ちなみに $AccountID は以下から確認できます。

f:id:ema_hiro:20201223155815p:plain

Go で unused なコードを検出する

Overview

staticcheck を使って unused なコードを一括で検査します。

Install

$ go get honnef.co/go/tools/cmd/staticcheck

ref: https://staticcheck.io/docs

検出する

U1000 がどこからも参照されていないコードの警告コードになるので、検査結果を grep で引っ掛ければ一覧を取得できます。

$ staticcheck ./... | grep "U1000"

その他のコードの一覧は https://staticcheck.io/docs/checks に記載されてます。

おまけ

golangci-lint を使うと「よろしくない」コードを一括で検査して警告してくれます。Reviewdog と組み合わせて CI で利用するケースは多いかと思いますが、普通に手元でコードを検査できるのも便利です。

$ GO111MODULE=on go get github.com/golangci/golangci-lint/cmd/golangci-lint@master
golangci-lint run --config ./.github/.golangci-lint --tests=false ./...

各オプションの詳細については golangci-lint run -h で調べると良いです。

ref: https://golangci-lint.run

AWS Lambda 向けに trace 付き Logger を作った

Overview

業務で AWS Lambda を使用してとある実装をしたのでその紹介です。

github.com

なぜ作ったのか?

Lambda 上でログを吐くとデフォルトで Cloud Watch Logs(以下 CW Logs) にログが飛ぶのですがこの CW Logs の生ログ検索が非常に使いづらく、また Lambda は非同期でバンバン実行されるので、どのログがいつ実行されたLambdaに紐づいているのかわからず、ログは追えるけどどういう流れでそのログが出力されたのがわからない、という問題を抱えてました。 そこで Stackdriver logging でやっていたようなログの構造化ができないのか調査したところ 公式ドキュメントにある https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/logs/CWL_AnalyzeLogData-discoverable-fields.html によると、デフォルトのCW Logs のフィールドに加えて、JSON文字列を出力した場合にその key がそのままCW Logs のフィールドとして使えることがわかったので trace をつけた json 文字列をログとして出力して、CW Logs Insight で trace でフィルタして引っ掛けると実行ごとのログの一覧できる Logger を作りました。

どう作ったか?

以前似たような Loggerを作ったことがあったので、それと同じようなロガーを実装しました。
具体的には Opencensus の分散トレーシングのライブラリ(https://pkg.go.dev/go.opencensus.io/trace) を使用して Lambda が起動するごとに一意な traceID と spanID を発行し、それを一連の処理の context として引き継いでいくことで Lambda の起動ごとにログをまとめられるようにしています。

How to Use

Lambda のエントリーポイント( Go の場合は main.go) で lambda.Start を Call する前に SpanContext をセットし、その Context を log の出力の時に使用します。 詳細は Readme を参照してください。

結果

このロガーを使用するようになったことにより、CW Logs Insight で以下のようなクエリを叩くと、特定の trace に紐づく一連のログを全て出力できようになりました。

fields @timestamp, @message
 | filter trace="service/$ServiceName/trace/xxxxxxxxxxxxxxx..."
 | sort @timestamp desc

Lambda の起動ごとの一連の流れをログで確認することができるようになったことに加えて CWLogs Insight は非常に高速にフィルタした結果を取り出してくれるので、サービスの中で何が起きているのかをすぐにわかるようになりました。

その他

実はこのロガーは Lambda の起動ごとに一つの処理しか行わない想定で、起動につき一つのライフサイクルのログのしか表示されないものかと思っていましたが、どうやら実際のログを追ってみると別のライフサイクルの Lambda のログも表示されることがわかりました。
Lambda の一度の起動につき、1つの処理しか行われない(Lambda が立ち上がっているインスタンスの使い回しは発生しない)と思っていました、実際にはインスタンスを使いまわしてるケースが存在することがわかりました。
ログはグルーピングされているので運用上特に困ることはないんですが、Lambda の振る舞いを知る機会にもなりました。

Firestore が絡んだテストで事前にデータを生成し終わったら削除する

Overview

テストを書くときにテストの実行前にデータを作成し、終わったら削除したくなる時がありますね?僕はあります。
Firestore が絡んだテストを書くときに Firestore Emulator を起動した状態で実際にデータを作成して、終わったら削除する場合の実装方法について記載します。

Sample

事前に Firestore Emulator を起動した状態で、愚直に Add してテストが完了したら defer で Truncatate します。

$gcloud beta emulators firestore start --host-port=localhost:$PORT

実装のサンプルは以下のようになると思います。かなり長ったらしいですが、他のテストのケースに影響を与えないように愚直にやるとこんな感じになるかなと思います。

tx := context.Background()
fs, err := firestore.NewClient(ctx, "$ProjectName")
coll := fs.Collection("$CollectionName")

tests := []struct{
    name string
    // テストデータ
    data interface{}
}{
    name: "case_1"
    data: inteface{}{}
}

for _, tt := range tests {
    tt := tt
    t.Run(tt.name, func(t *testing.T){
        // setup data
        if _, _, err := coll.Add(ctx, tt.data); err != nil {
            t.Fatal(err)
        }
        // data の削除
        defer func(){
            refs, err := coll.DocumentRefs(ctx).GetAll()
            if err != nil {
                t.Fatal(err)
            }
            for _, ref := range refs {
                batch := fs.Batch().Delete(ref)
                if _, err := batch.Commit(ctx); err != nil {
                    t.Fatal(err)
                }    
            }    
        }
    }
}

Refs

See Also

Firestore の js SDK には clearPersisitence というメソッドを教えてもらいまいたが、Go の Firestore SDK には実装されていなかったので使えませんでした...

Eureka Advent Calendar 2020 に投稿しました。

所属してる企業のアドベントカレンダーに記事を投稿しました。

Lambda にガッツリ触れた際の振り返りについて書いています。

興味がありましたら是非ご一読ください。

medium.com

MOKUMOKU ONSEN #3 に行ってきました

f:id:ema_hiro:20201214205434j:plain

Overview

定期的に行っている開発合宿に行ってきたログです。

行き先

今回は函館です。
GoTo キャンペーンを使ったので、普段なら考えられないリゾートホテルに宿泊できました。

www.hotespa.net

進捗

ずっっっっっと停滞していたアウトプットをやってました。
計5エントリくらい(会社のアドベントカレンダー含)書き貯めたので年内少しずつ吐き出していきます。

その後副業で関わってるサービスの仕事を少し進めました。

ほんとは自分のサイトの Nuxt のバージョンも上げる、くらいまでできるかなと思ったんですが、流石にそこまではいけなかったので、それは冬休みの宿題に持っていきます。

振り返り

Good

仕事を言い訳にブログが書けずに溜まっていた系の進捗が出たので2020年をスッキリ終われそうです。
(ほんとよかった...)

こんな時期なので、旅行なんて...とも少し思いましたが、少人数 && 感染防止対策を徹底していたので比較的安全に旅行できたのではないかと思います。
この時期の函館は寒すぎて、市内に降り立った瞬間に外を出歩こうなんて気持ちは折れました(この時期に最高気温 -4 度叩き出す北海道は流石にレベルが違いました...)

あとご飯は美味すぎました。
価格は同じくらいなのに、味の質と量が東京で食べるそれとは段違いでした。
なぜか毎回食と酒へのモチベーションも高い「もくもく温泉」ですが、その中でも最も満足度の高い合宿になりました笑

More

今回残念だったのはホテルの Wi-fi がそんなに強くなく、まぁまぁな頻度でインターネットが切れてしまったことですね。今までのところは比較的回線が安定していたので差を感じてしまいました。
外寒いし吹雪だし、ホテルに引きこもって Youtube でも見てたんですかね。。。

あと桃鉄持っていったのに、進捗に出すのに夢中で結局できませんでした(いいことです)

See Also

僕らの知見は以下のツイートの主のエントリにまとまってるので一読してみてください。

Github Actions のビルドマトリックスを使う

Overview

Github Actions のビルドマトリックスを使って複数の Go のバージョンでテストを行う方法について記載します。

ビルドマトリックスとは?

Github Actions のドキュメントに書いてあります。

docs.github.com

strategy.matrix を指定すると matrix で指定された値(配列なら要素分) を ${{ matrix.value }}value に入れて Actins でビルドしてくれる機能です。
これにより、例えば今まで複数の言語のバージョンをサポートしたいケース(Goなら直近2バージョン)で2つ step を記載していたのをstep を1つにまとめることが可能になります。

Samples

以下のような感じで設定します。

https://github.com/emahiro/glc/blob/master/.github/workflows/gotest.yml#L6-L9

バージョンの指定方法は Github Actions の言語ごとの設定を参考にしました。

ex.

ビルドマトリックスを使えばOSSを作ってるケースで複数バージョン対応をシュッと書けるだけでなく、OS のディストリビューションGithub Actions に用意されてるものについてはシュッと書けるのでおすすめです。
コマンドラインツールとか作ってる方向けにも便利な機能ですね。