ISUCON 10の予選に参加しました

id:masawadaid:side_tanaと一緒にはやいTシャツ屋さんで参加しました。初期スコアが最高スコアでした。察してください。

チームでやった主なこと:

  • New Relicの導入
    • Web Transactions
    • SQL
    • Logs
  • デプロイ自動化
    • デプロイするたびにNew Relicのdeploymentsを打つ *1
  • MySQL 8化n
  • estateのlongitude/latitudeをgeometry型にする
  • ☆searchEstates/searchChairsをやっつける
  • もろもろインデックス貼る
  • アプリ複数台構成へ

コミュニケーション

Slackで事務連絡、Scrapboxにメモ、Hangoutで会話という体制にした。Hangoutは繋ぎっぱ。

Scrapboxに予選ページを作ってそこで30分ごとのイテレーションで進捗確認と次のタスクを確認した。

f:id:aereal:20200914112003p:plain

1時間だとおよそ8回しか回せないけれど30分だと16回あるので「これ前回も共有してからハマっているな」って自覚できるタイミングが理論値では30分はやくなる。 実際、「これ前も共有しているからヘルプしますか? まきとりますか?」みたいな会話は何度かできた。

New Relic

ISUCON 9の問題を事前に解く練習をしてその時にNew Relicを試して良かったので今回も使った。

なので我々はalpを使わなかった。pt-query-digestは詳細な分析に使うくらい。複数台構成も睨まれていたので、ログもNew RelicのLogsに集約させた。

alpとかを使うのに比べてレイテンシはまああるし、X-RayGoogle Cloud Traceと比べても遅い。これは集計にかかっているらしい。

それでもタイムアタック競技のISUCONでなぜ選んだかというと、パーマリンクを共有できることと差分の比較が楽という点が魅力だったから。

今回はリモート参加で3人全員別の拠点におり、ぱっと画面を見せたりということが難しいのでパーマリンクができてSlackやScrapboxにスッと貼れるのは便利。

あと時系列で変化を見られると、この変更が効いて速くなったねとか一目でわかる。

特に便利だったのはLogsで、これは仕事でもよくあることだけれど「こういうログが出ていました」「タイムスタンプが見たいな (削らないで!)」「スタックトレース出ていない? (削らないで!)」「どのホスト? (書いて!)」みたいなやりとりはけっこうしたことがあって、複数台で動かすとなるとこれが面倒そうだなと思っていた。 LogsはWebのコンソールに集約されるのでリンクを貼ればメンバーに過不足なく共有できるのが良い。

別途デーモンを立てないといけなくはあるけれど、まあさして大変ではなかったし最後に止めてしまえばパフォーマンス影響はないしでよかろうと思った。

ちなみにファイルに書き出すようにしたのでlogrotateどうしようか一瞬悩んだけれどデプロイごとに : > /var/log/app/isuumo.log して空にすればいいかと思った。妥当な判断だったと思う。

blog.sushi.money

MySQL 8

geometry型にできるとか最新は正義でしょっていうことで選んだ。ただ、最初にコード読んだ時はGISか?!ってびっくりしたけれど、声に出して自分たちはMySQLでいくよねって確認をした。

MySQL 8へのアップグレードやgeometry型への書き換えはid:masawadaがやってくれてめちゃくちゃ頼もしかった。 感想部屋ではbind-addressとかユーザーの接続元情報とかでハマって外から繋げられなかった人もいたっぽいけれど、裏番組で本人に聞いていたら「ハマると思ったので初手でやりましたね」って言っていてかっこよかった。

反省点としては事前の練習時にはMySQLが使われたなら8に上げようって話をしていたので変更点についてちゃんと勉強しておくべきだった。 descending indexとかパラメータとかについて調べておくべきだった。

DB2台構成に至らなかった

のがこのスコアの根本的な原因だねーって話をチームメイトとした。

searchとか改善しても焼け石に水のようなスコア上昇しか見られなかったけれど、後から振り返ると順番が逆だったんだろう。しかし間違えてしまった結果「ここは意味がないのか?」「きっと複数台構成にすれば早くなるはず」と間違った推論をしてしまい深みにはまってしまった。 間違った前提からは間違った帰結しか得られないとはこのこと。

なんというかサービスを改善するっていう意識が低くてISUCONをやるっていう意識に支配されていたなあ。

各ホストのメトリック中止がおろそかになった

New Relic傾倒の余波なのかもしれないけれど、アプリケーションメトリクスはつぶさに見れたと思うが、各ホストのメトリクスはきちんと見れていなかった。

象徴的なのがinnodb_buffer_poolを贅沢にしすぎて終盤にDBがOOMで死んだこと。あとからインフラメトリクスを見ると枯渇気味だったことは一目でわかった。

これは普段ではできていることなので、良くも悪くも道具に使わされたということなんだろう。New Relic, 便利ではあったのでちゃんと手にしたい。

感想

コードを見た時に「短い! 把握できる!」「テーブルが2つしかない!」「外部へのHTTP通信ない! ほんとに?」「なぞって検索、スコアリングに影響しないのにちゃんと作られていてすごい」と今まで見てきた問題のどれよりもコンパクトでありながら歯応えがあって取り組んでいて楽しかった。 それだけにこの結果はすごく悔しい。

特にDB2台構成に舵を切る判断ができなかったのは決断力の甘さと場数の少なさを痛感させられる。

問い合わせの仕組みとかリアルタイムにスコアが増えるベンチマーカーとか予選ポータルのもてなしが充実していたりと、最大級の規模でありながらすばらしい競技体験が得られたのは一重に運営のみなさまの尽力あってのことだと思います。ありがとうございます。

次は予選突破するぞ。

*1:実際にはアプリIDを間違えていて今回は使えていなかった

AWS CDKとは何か、IaCの変遷を踏まえての紹介

概要

これまで業務・プライベートの双方でAWS CDK (Cloud Development Kit)というInfrastructure as a CodeツールをDeveloper Previewのころから着目し利用してきたので、その特徴を競合と比較しながら説明します。

概観を掴んでもらうことを目的とするので詳細は外部の公式・非公式のドキュメントへ適宜リンクするかたちをとります。

想定読者

  • AWS CDKが何かよくわかっていない人
    • CloudFormationとの違いがわかっていない人
  • IaCに興味がある人

前提・背景

AWS CDKの特徴を説明するためにIaCの変遷について簡単に言及したく、そのためにはWeb開発におけるインフラの変遷に触れる必要があります。

結論から言うと 現代においてインフラとアプリケーションの境界は非常に曖昧 で、比較的はっきり別れていた時代に定義・設計された IaCの概念や実装から進化した存在が必要 であるという考えに応えて登場したのがAWS CDKです。

以下に書く話はデプロイ今昔 - Hatena Developer Blogがよくまとまっているのでそちらを読むのが早いかもしれません。 もう少しインフラにフォーカスして書きます。

ペットと家畜

インフラとアプリケーションがはっきり別れていた時代では、インフラのライフサイクルとアプリケーションのライフサイクルが異なっており、インフラリソースの制御がほとんどすべてユーザーの手にありました。

アプリケーションの生まれ変わり = デプロイよりインフラの方が大抵長生きするので、インフラが必然と状態を持つ、そんな時代です。 サーバーがペットや家畜として扱われていた時代という比喩もあります。

また、インフラの制御がユーザーの手にほぼあるということは、プロバイダーは何もしてくれずユーザーが管理しなければ壊れたり動かないといった管理コストの裏返しでもあります。 まず仮想マシンの調達と在庫管理からユーザーを開放したのがAWS EC2のようなIaaSサービスです。

マネージド化

長じて、コンテナオーケストレーションサービスやサーバレスサービスが登場します。これらをまとめてクラウドプロバイダーによる進んだ資源管理が行われることをうけてマネージドサービスと称することにします。

これらのサービスはコンテナ技術や進化した仮想化技術を利用しアプリケーションに設計上の制約を与える一方で意欲的なインフラの隠蔽を行うことで、クラウドプロバイダー側が最適化を行う余地を広げました。 ここでいう最適化とは、アプリケーションが利用されていないあいだは計算機資源を開放してコストを抑えることであったり、突発的な利用の増加に複雑な事前準備なしに対応できるよう計算機資源を用意することであったり、あるいはクラウドプロバイダーがより簡素・柔軟なデプロイメントを設計しDX (Developer Experience) を向上させることであったりします。

コンテナオーケストレーションサービスの代表例はAWS ECS (Elastic Container Service) やGKE (Google Kubernetes Engine) などが、サーバレスサービスの代表例にはAWS LambdaGoogle App Engine (GAE)などが挙げられるでしょう。

アプリケーションとインフラの漸近

マネージドサービスがアプリケーションに与えられる制約は様々で、コンテナ技術のようにシステムコールや計算機資源へのアクセスがスーパーバイザに隠蔽・管理されることであったり、AWS Lambdaのように利用できるランタイムに限りがあることであったり、様々です。

インフラがユーザーの管理下にあるケースでは、たとえばCPUやメモリをよりヘビーに使いたければサーバーを増強したり、たとえばアプリケーションのランタイムにPythonを使いたければサーバーにインストールしたりすることで、インフラがアプリケーション側に寄り添わせる柔軟性がありました。 設計の主導権はアプリケーションに多くあったといえそうです。

一方で、インフラ = 計算機資源にはいくつかのハードリミットがあり制約条件たりえました。 用意できるCPUやメモリのスペックに現実的な上限があったり、単位時間あたりに用意できる数が限られていたり、様々です。

また、必要なライブラリを揃えるプロビジョングやアプリケーションを実行可能な配置を行うデプロイメントは、インフラ (OS, ミドルウェア, etc.) による制約や要求が生じますが、アプリケーションと異なるレイヤと規定され分断されがちでもありました。

あるインフラ上で動かすのが非常に手間であったり非効率なアプリケーションを生むケースもありえたでしょう。

サーバレスサービスなどマネージドサービスではアプリケーションに制約をいくつか与える代わりにそれら分断を埋め開発からデプロイメントまで包括的なツールや体験が整備されていることが期待できます。

ここまでのまとめ

  • EC2などIaaS時代までは、インフラとアプリケーションは分断され、ツールもそれぞれの世界で独自に設計されていた
  • マネージドサービスではインフラとアプリケーション双方に制約を与えるかわりにスケーラビリティなど包括的な体験が提供されるようになった

AWS CDKとは

AWS CDKはAWSが開発しているツールセットでCloud Development Kitの略です。 便宜上IaCと呼びますが、インフラに限らない開発キットを志向していることは明らかなので、以下に示す競合との比較も厳密には同じレイヤとはいえず比較として不十分・不適切とも捉えられますが、わかりやすさのためにあえて比較します。

AWS CDKは:

  • L1 layer: CloudFormationのリソース定義に一対一で対応するクラス群
  • L2 layer: CloudFormationのリソース定義を包みユーザーフレンドリーなAPI (intent-based API) を提供するクラス群
  • patterns: 1つのクラスが複数のサービスのリソースを包括的にプロビジョニングするクラス群

……の、3つの層から成り立っています。 refs. Constructs - AWS Cloud Development Kit (AWS CDK)

L1 layer

L1 layerはCloudFormationのリソース定義からコードが自動生成されます。 L2より上位の階層のクラス群はより下位のクラス群を利用しています。言い換えるとCDKが提供するCloudFormationに関わるクラス群は最終的にL1 layerを利用し、CloudFormationの変更適用に還元されます。 基本的にCloudFormationでできることはCDKでも実現できると捉えてよいです。一方でCloudFormationに存在するハードな制約を越えることはできません。

L2 layer

CloudFormationはAWSのリソース設計に強く根ざしリソースのあるべき状態を宣言すれば差分の適用を任せられるサービスですが、宣言的な設計のために生じる様々な要求・制約が絡み、ひとつのリソースをプロビジョニングするのにも慣れが必要な癖の強いサービスでもあります。

たとえばあるIAMユーザーにだけS3バケットへのアップロードを許可したいという目的を達成するために必要なCloudFormationのリソース定義は AWS::IAM::User, AWS::IAM::Policy, AWS::S3::Bucket などに渡るなど人間の思考から離れたリソース設計を見つけ適切な設定を与える必要があります。

また、各リソースの意味論レベルで実現が難しい制約は実行時に検証されることも多いです。 たとえばある設定Aはある設定Bがtrueであることを求めるが、それは実行してみるまでわからない、など。

こうしたCloudFormationの難点に対してまず設計されたのがL2 layerです。 たとえば上記で例にあげた「あるIAMユーザーにだけS3バケットへのアップロードを許可する」ケースでは以下のようなコードで済みます。

import { Stack, App } from '@aws-cdk/core'
import { Bucket } from '@aws-cdk/aws-s3'
import { User } from '@aws-cdk/aws-iam'

const app = new App()
const stack = new Stack(app, 'buckets')
const uploader = new User(stack, 'Uploader')
const bucket = new Bucket(stack, 'FilesBucket')
bucket.grantRead(uploader)

Bucket#grantRead を呼べばいい、非常に人間がやりたいと望んだことにほぼ対応するAPI設計ではないでしょうか。

patterns

最後にpatternsです。これはL1/L2 layerのクラス群を1つ以上包みAWSのサービスでよくあるパターンをカプセル化したクラス群です。

たとえばApplicationLoadBalancedFargateServiceというECS FargateのサービスがALBの後ろにいるというのは典型的なパターンですが、 ApplicationLoadBalancedFargateServiceインスタンス化するだけでALBやターゲットグループ、リスナー、クラスターやタスク定義、サービスなどなどにプロビジョニング・動作に必要十分なデフォルト設定を備えたリソース定義が与えられます。

慣れれば楽ですが、慣れるまでに数え切れないトライアンドエラーを要するCloudFormation. 特に複数サービスにまたがるベストプラクティスは情報ソースを見つけるのにも一苦労だったりします。 上記例だと情報がECSのドキュメントにあったり、ALBのドキュメントにあったり、などなど。

patternsレイヤはよくあるパターンに名前が付けられパッケージ化されているので、たとえばECSに関するよくあるパターンを手軽に使いたいと考えたらaws-ecs-patternsを見に行けば望むものが見つかるかもしれないというアフォーダンスを与えてくれます。

もちろん複数のリソースに良いかんじのデフォルトを与えてくれるということは、デフォルト設定が自分たちのユースケースにフィットするかきちんと読み解くことが求められます。 手放しにこれを使うべきというより、ベストプラクティスがコード化されていること・それを読んで必要な知識への参照を得られることが本質的に重要でしょう。

モジュール化と再配布

AWS CDKはコアがTypeScriptで書かれNPMパッケージとして配布されています。TypeScriptが第一級のサポート対象言語ですが、PythonJavaへも移植されています。 refs. Translating TypeScript AWS CDK code to other languages - AWS Cloud Development Kit (AWS CDK)

いずれにせよ既にパッケージシステムが確立したエコシステム上で提供されていますし、AWS CDK自体がそうであるように自分たちがCDKを利用して作ったクラスや関数をNPMやその他のパッケージとして配布するのに特別な労力は要りません。 NPMパッケージならTypeScriptで書いて、型定義とトランスパイルされたJavaScriptのコードを成果物に含めるだけです。

きちんと動くパッケージシステムを作るのは存外に難しく、無闇にパッケージシステムを独自開発しても成熟したものに比べてうまく動かず利用者に負担を強いたりdependabotやrenovateのようなSaaSのサポートが受けられず不便であったりします。 過去のバージョンのアーカイブを参照できたり、脆弱なバージョンを非公開にしたり、改竄されていないか整合性を確認する手段を提供したり、などなど、パッケージシステムに求められる要件は多岐に渡ります。

その点でCDKは独自開発せずに既存の成熟したシステムにうまく乗っており、この点でCDK固有のフラストレーションを抱えることはほとんどないでしょう。

実例

たとえば前職ではステージング環境や開発用途のアプリケーションなどを社内の人間のみに公開する用途にG Suiteのメンバー一覧から作られたCognitoのユーザープールとそれを使って認可するリスナールール類を社内モジュールとしてメンテナンス・提供しました。 用途によって社員のみ・アルバイトを含むユーザープールなどを選ぶオプションを提供しました。

これを実現するためには認可されたユーザーが特定のグループに所属しているかG SuiteのAPIに問い合わせる必要があり、このモジュールはそれを実現するLambdaのコードも含みます。

単にCloudFormationのテンプレートを配布するだけではなく付随するリソースも含められる点が、競合と比べてCDKが傑出している点であり、それをうまく活かしている例に挙げられるでしょう。

競合との比較

AWS CloudFormation

基本的にCDKはCloudFormationの上位互換と捉えてよいです。即ちCloudFormationでできることはすべてCDKで実現できると捉えて問題ありません。

CDKはchangesetを作成→実行というCloudFormationのベストプラクティスが cdk deploy というコマンドひとつで実現されているほか、 cdk diff でスタックとテンプレートの差分を表示できるだけではなく、 cdk synth でCloudFormationのテンプレートを出力できるなど、ワークフローにおいてもCloudFormationを包括しより使いやすくしてくれます。

CloudFormationがフィットするケースにおいては、CDKの検討を強く勧めます。

SAM (Serverless Application Model)

ベストプラクティスや典型的なケースをより使いやすくするという思想は似通っています。

SAMはCloudFormationのテンプレート変換として実装されており、SAMに精通しているメンバーがいない場合はCDKを勧めます。 CDKはL2 layerのコードを見たり cdk synth コマンドでどんなCloudFormationテンプレートが適用されようとしているか静的に確認できます。

SAM localなど周辺ツール群はCDKのスコープにないのでそれらは別途利用しても良いでしょう。

Terraform

Terraformはレイヤや設計としてはCloudFormationの直接的な競合ですが、IaCという括りで比較してみます。

抽象度の点ではTerraformよりCDKの方が高いです。一方でTerraformはAWSに限らずGCPなどのクラウドプロバイダーではなくGitHubなどのWebサービスのリソースも管理できます。

特定のクラウドプロバイダーと結びついていないことは柔軟な機能にも繋がっており、コード上で管理していないリソースをインポートしてさも最初からコードで管理されていたかのように扱える terraform import は早いうちから実装されており便利です。 ちなみにCloudFormationでは2019年11月に既存リソースのインポートがリリースされました: 新機能 – CloudFormation スタックへの既存リソースのインポート | Amazon Web Services ブログ

GCPなどAWS以外のリソースも扱う場合はTerraformを使うのがベストです。

一方でCDK for TerraformというバックエンドをTerraformとしたCDKも開発・リリースされています: CDK for Terraform: Enabling Python & TypeScript Support 日本語記事: AWS CDKでプロバイダーとしてTerraformが使える!!CDK for Terraformが発表されました!! #awscdk | Developers.IO

また同様にKubernetesのリソースを管理できるCDK for Kubernetesもリリースされています: Introducing CDK for Kubernetes | Containers

このようにCDKの基本的な概念は他のIaCプラットフォームにも拡張可能ということがDeveloper Previewとして示されており、比較的安定しつつあるCDKでこれらの体験に触れることは今後を見据えればポジティブではないでしょうか。

まとめ

  • CDKは、インフラとアプリの関係性の変遷に応じて生まれた新しいツールキットです
  • 単にインフラリソースを管理するだけではなく、クラウドプロバイダのサービスをあたかも外部モジュールのように扱う設計が特徴です
  • しかもTerraformやKubernetesへの展開も進みつつあり、既存のIaCから拡張された普遍的な概念にもなりえそう

付録: 筆者とAWS CDK

speakerdeck.com

AWS Cloud Development Kit -CDK- Meetupというイベントで新サービス開発にAWS CDKを採用した事例とその特徴について発表しました。上記スライドはその時の資料です。

またaws-cdk · GitHub Topicsにこれまで公開してきたAWS CDK関連のライブラリやリポジトリがあります。

最高のアーキテクトになる

が、今後5年のテーマかなーと思いはじめた。

ここでいうアーキテクトは「ソフトウェアの誕生から運用を通したライフサイクル全体を通して良い感じにするためにあらゆる手を尽して準備する役割」くらいの意味で言っている。
「ひどい目に遭わないよう準備する」がソフトウェアエンジニア一般と色付けを変える・異なるところかなと思う。

近づくために自分が持っているスキルとあるべきスキルを比べて、ソフトウェアを描く力と組織を描く力は両輪であって、前者は既に自分が学んで研鑽するサイクルが確立されているしそれなりの成長曲線を描けていると思うけれど、後者はそうではないのでここが注力すべき分野だなと考えている。

コンウェイの法則などを挙げるまでもなく、ソフトウェアの構造とそれを作ったり運用する組織の構造は相互に影響を及ぼす関係にあることは言うまでもなくて、マイクロサービスがどうだ、DDDで分析だなんだというのも、ソフトウェアの世界でだけ考えても組織の世界の都合が追い付かなきゃだめでしょう、だから細かくわける路線もあるで・詳しい人に話を聞けていないなら聞きに行くのが一番やで、という話だと捉えている。
こういう思いはDDD勉強会に参加したり、前職でシニアエンジニアというマネジメント色のあるロールをやって『エンジニアリング組織論への招待』や『エンジニアのためのマネジメントキャリアパス』あたりを読みはじめてなんとなく抱きはじめた。最近『Design It!』を読んで確信に至った。

とはいっても「マネージャー」というポジションはしっくりこないなー、けどなんでマネージャーっぽいロールを引き受けようと思ったんだっけ? と考えているうちにこういうことかな? と一応の決着を見た。

各チームの大小様々なやっていることを共有する会をやったら予想外に盛り上がった話

前職で掲題のような取り組みを提案して始めてみたら予想以上に盛り上がったので書いておく。かなり低コストに始められるので以下に書くような課題感を持つところではおすすめできると思う。

どんな会か

会の名前は「今月のホットトピックス」と銘打った。

前職では毎週木曜日に社内勉強会が開催されており、月始めの枠の一部を借りて30分程度から始めた。
refs. 寿司と勉強会とエンジニア - Hatena Developer Blog

  • 最近チームでやったことの書き出し
  • 聞いてくれ・聞きたいの投票
  • 司会による紹介

……からなる。

まず書き出しは、ナレッジツール (Scrapbox) におもむろに「2020/8 ホットトピックス」というページを作り、各チームやその他有志といった見出しを置いておく。
このライブラリをアップデートしたとか、バグ報告をしたとか、大小いろいろな情報を書いてくれる。

並行して、ページに箇条書きされた各チームの取り組みに「聞いてくれ」(自己申告)「聞きたい」(他薦) を各々投票してもらう。仕組みは素朴が箇条書きの項目に自分のアイコンを置いておくだけ。
Scrapboxを使っているので C-i を押すだけで投票できる。
その他のツールでも単に + を書くとかでもできそう。

最後に司会は当日、画面を共有しながらページを眺めて注目が集まっている項目を中心に書いた人へ話を振ったりして話を掘り下げる。
特に表彰とかはせずに他のチームや人がやったことを知ってもらう体験を持ち帰ってもらうという形式。

そもそもなぜやろうとしたか

当時、社内勉強会はかれこれ5年近く続いていたし、コロナ禍による縮小が起きる前までは外のカンファレンスや勉強会での発表もそこそこ活発であったものの、社内での横の繋がりが薄まり「何をやっているのかわからない」「やってから実は似たような知見が他のチームにあることを知った」という声がちょくちょくあがっていた。
知識共有のドーナツ化現象とでも言うべき状態。

社内勉強会が続いていたのになぜ? というと盛り上がりすぎた結果:

  • 発表が先鋭化し敷居が上がった
  • 盛り上がりすぎて枠の競争率が高まった

……ということが起きていたと思う。こういうステージに移行するくらい盛り上がっていたのは本当にすごいので嬉しい悲鳴ではあった。

予想外だったこと

  • 初回から書き込みが盛り上がったこと
  • 司会はけっこう大変

初回からたくさん集まったのは嬉しいことで、最初は自分のチームとかからとにかく書いてもらわないといけないだろうなーと覚悟していたのでありがたい。
一応、敷居を下げようと思って最初のほうに自ら小さなことばかり書いたりといった工作はしたけれど、それにしたって盛り上がった。

これは、運営からなにか働きかけをした結果というより、メンバーに書きたい話題があった・そもそも共有するのが好き、といったことによるものだと思う。

逆に盛り上がりすぎた結果、司会はたくさんあるトピックから苦渋の思いで選り抜いて話さないといけなくて大変。これについては後述する。
司会役は当初自分ひとりでやっていたけれど、大変になったこととか自分の退職が進んでいたことから他のメンバーも巻き込みはじめた。

予想通りだったこと

ページの更新通知がSlackに流れれば催促しなくて済むだろうということは予想していて、実際にその通りになった。
refs. Slackに更新を通知する - Scrapbox ヘルプ

エンジニア全体で共有するScrapboxのプロジェクトと全員が入るSlackチャンネルがあるので、通知をそこに流した。一応、あまりに集まりが悪かったらチームのリーダーをけしかけますよという断りは入れていたものの、そうなったことは一度もなかった。
共有する会が近付くになれ「おっ 書いてるな」という盛り上がりが出てくるので連られてみんな書き出すということが起きていた。

寂れている場所へ書いてくれってお願いしてまわるのもお互いに気まずいのでこうならなかったのはありがたい。

司会の心掛け

自分は主にWebアプリケーションエンジニアで知識も当然そっちのほうに偏っている。会社の構成比率的にもエンジニアの中でWebアプリケーションエンジニアが多数を占めている。
一方で絶対数は少ないもののスマートフォンアプリエンジニアやデータ活用に強いエンジニアもいて、それぞれの専門性を活かしておもしろいことをやっているのでそういうのもちゃんと掬い上げたい。
単純にせっかくロールや専門性が混ざって集まるカオスな場だからそれを楽しんでほしくて「ここは自分の場じゃないな」って思う人をできるだけ減らしたい。

とはいっても数が少ない職種のやっていることを単純な多数決でピックアップするのは難しい。
なので共有する会で取り上げる話題は「聞きたい」「話したい」という声があるものを優先するが司会の独断で決めるということにさせてもらった。

自分の中での優先順位は「話したいこと」>「『聞きたい』が集まっているが直近で触れられていない分野のこと」>「その他の『聞きたい』が集まっていること」>「『聞きたい』はほとんど集まっていないが個人的に気になること」くらいにしていた。

もうひとつは敷居を上げない圧力をかけること。

そもそものきっかけが「技術勉強会で発表してみたいけれどどれも力の入った資料や調査が伴っているので自分がやるのは大変」「技術勉強会で発表するほど大したことじゃないし」といった声に対するカウンターアクションなので、この会で話したり取り上げてもらう敷居が上がるのは本末転倒である。

司会・主催としては:

  • 話すのは最大5分、下限なしをしつこくアピール
  • 資料なしでOK, むしろ推奨くらいの勢い

……を打ち出した。

そもそも盛り上がって枠内に収まるかどうかという状態でもあったので、あまり気合の入った発表になってしまうと数を増やせないという事情もあった。

結論

特別なツールとかを必要とせずうまく主催が立ち回るところから始められるのでおすすめです。

しかし周りの乗っていきに助けられてうまく滑り出したという側面が強いのでこういったことで躓く、といった情報がもらえるとありがたい。

go-sql-caller-annotation: GoでSQLに呼び出し元の情報をコメントとして埋め込む

github.com

というのを書いた。ご利用ください。

スロークエリを解析する時に「このクエリ、アプリのどこから呼んでいるんだろう?」と調べたいことがよくある。
だいたい遅いクエリって長くて複雑だったりするのでいちいち横に縦にスクロールして全容を把握してgrepするのも地味につらい。

ISUCONに向けてほしいねーって話になったので書いた。

PerlDBIを使っていた時にはDBIx::Tracerを使ったりして実現できていたので、Goでもやりたいと思って書いた。

やっていることは、database/sql/driverのラッパ実装を作っている。仕事は実際のドライバ (go-sql-driver/mysqlとかpqとか) に任せていて再実装ではないからDBへのアクセス部分にまったく不安はないと言っていいと思う。

この実装方法はaws-xray-sdk-goから借用した。Apache License 2.0を継承したりコメントやNOTICE.txtにもきちんと書いたつもりだけれども、もしライセンス上の不備があれば教えてください:

aws-xray-sdk-go/sql_context.go at master · aws/aws-xray-sdk-go · GitHub

どうぞご利用ください。

退職する時に同僚からフィードバックをもらう

退職するにあたって、人事評価とかそういった枠組みをすべて取り払って去る自分に対して言い残したことがあれば伝えてほしいなと思い、自分がどう見えていたかをGoogle Formによるアンケートというかたちで同僚に乞い、ありがたいことにたくさん送ってもらえた。

設問は:

  1. 業務上なんらかの形でaerealと関わったことがありますか?
  2. あなたは aereal と働くことをどのくらい友人や知人に薦めると思いますか?(仮に自分に似た価値観の友人や知人がいたとしてお答えください)
  3. 上記スコアを付けた理由を教えてください。またそのスコアはどうすればもっと改善すると思いますか。
  4. ソフトウェアエンジニアとして見た時、aerealの良い点・望ましくない点・その他感じたことがあれば教えてください
  5. 戴いた回答の一部または全部を引用し、ブログなどで外部に公開することを許可しますか?(引用する際には内容から個人を特定できないようにします)
  6. その他、言いたいことがあれば何でも書いてください(例: Xの引き継ぎ頼む、Yをやり残している etc.)
  7. よければあなたの名前を教えてください

の7つ。2つ目の設問以外はすべて自由記述式なので回答者の負担にならないよう、すべての項目を省略可とした。
「あなたは aereal と働くことをどのくらい友人や知人に薦めると思いますか?(仮に自分に似た価値観の友人や知人がいたとしてお答えください)」という項目は10段階評価。

「薦めると思いますか」という評価とそれぞれのコメントを突き合わせると「『これがあればもうちょっと』の幅がこういうかんじなんだなあ」とわかって思っていた以上にフィードバックとして高い質になっているなあと感じた。

おもしろかったのはパーソナリティについて:

  • 何を考えているかわからない
  • 怖い、とっつきにくい
  • 穏やか、優しい

と、かなりまばらなフィードバックを受けたこと。

企画の方やあるいは仕事上のやりとりが少ない・薄いメンバーは「穏やか、優しい」、エンジニアからは概ね「怖い、とっつきにくい」、マネージャーからは「何を考えているかわからない」「怖い、とっつきにくい」といった評価が多かった。

これは自分でも得心がいっていて、オフに近い時だったりロールや職能の軸にある考え方が違うと認識している相手にはオトナの顔ができているのでそういった相手には穏やかとか受け取られるのだろうし、自分は仕事をする時に機械になりたいしウェットな面は別に見せる必要がないと思うタイプなのでマネージャーからは不気味に見えるのだろう。

そして、同じエンジニア相手にはアウトプットや振る舞いに甘さや妥協が見えると「もっとこうすれば良くなるよ」という上昇志向が溢れすぎて怖いと思われるんだろう。
言い方がめちゃくちゃ意識高いというか美化しているようにも見えるけれど、要するに相手からすると正論で殴られているに近いんだろうと思うので、そりゃプレッシャーを感じるだろう。

いちおうマイルドにしようという意識はあって努力はしているものの、ソフトランディングさせるために婉曲な物言いをするのも違うと思うのでとる手段が「一度にあれこれ言わない」くらいで、それも相手によっては「なんかいかにも物言いたげだけど黙って呆れたか我慢しているんだろう」というのが透けて見えたりして、対処になっていなかったりするんだろう。
まあ、なにもかも変えるべきとは思わないけれども、そんなに変なことは言っていないはずなので、輪郭の問題で咀嚼されにくいことがあるなら呑み込みやすい工夫はしたい。

ちなみにこれはQuipperでのオフボーディング実践入門を見て真似したいと思ったのでそれにインスパイアされた:
quipper.hatenablog.com

以下、個人的に印象だったコメントで公開することを許可されたものをいくつかご紹介:

なんか、きっと、「まだ中堅としてうまく立ち回るより、もっと前線を張りたい」というイメージがあるのかなぁと思っており、その面では転職というのは正解だったと思っています。(社内だともう中堅ムーブをどこでも求められると思う)

まったくそのとおりです。もちろん中堅としておもしろい立場を振ってもらえることも多く嬉しい一面もあったのですが、良くも悪くもそういった印象・評価はコントロールしようがないのでハードリセットをかけたかった、というかんじですね。
良く見てくれているなあと思い嬉しかったです。

aerealさんは、はてなに入社する前からブログなどで見かけて「この人、はてな好きそうだなぁ」という印象があったため、入社したのを知ったときに謎の納得感があったのを覚えています。そんな「ザ・はてなのエンジニア」な人の退職というのは、ちょっとした一時代が終わった感があり、非常に感慨深いです。(略)残された者としては、この衝撃で崩壊してしまわないように気を引き締めていこうと思います。

こんな風に高く買ってもらえるなんて光栄です。インターネットヒーローになりたかったので、社内とはいえこんな評価をしてもらえるなんて報われたのでしょうか。

一方で、ものごとの粒度が荒く、それに対しての説明ロジックが十分成立していない場合でも、(本人は意識していないのかも知れないけれど)ロジカルさや正確さを求められて、会話の温度感のなんとなく溝が埋まらない、という場面はあった気がする。歩み寄ってくれる感がちょっと弱いというか。
そういう意味では、技術に疎かったり勢いで押すプロデューサーなんかだと「扱いに困る」ところがあるのでは、と思ったりはします。

これもまったくその通りですね。メタな期待設定というのを今思い付いたけれど、これはどこにどう着地させるのが良いのか。
安易にやると単に人を値踏みすることになりそうなので慎重になりたいけれども。しばらくの課題・テーマとします。

最後に、フィードバックいただいた同僚のみなさまありがとうございます *1

*1:しれっとまだフォームは開けているよ

心配ゴム用 (^ω^)ニニニニニつ ビヨーン

gyazo.com

const buildBiyon = (max) => {
  let incr = 1;
  let count = 0;
  return () => {
    if (count === 1) {
      incr = 1;
    }
    if (count >= max) {
      incr = -1;
    }
    count = count + incr;
    return "ニ".repeat(count);
  };
};

(() => {
  const biyon = buildBiyon(5);
  setInterval(() => {
    history.replaceState({}, '', location.pathname+location.search+`#心配ゴム用 (^ω^)${biyon()}つ`)
  }, 100);
})()

See also blog.sushi.money

Kiritoru: 見ているページをScrapboxでブクマをAndroidの共有メニューから簡単にできるように

scrapit.vercel.app

……というのを作った。

ソースコード: GitHub - aereal/scrapit

blog.sushi.money

PCのブラウザではこういうブックマークレットでブクマしている一方、Androidで見ている時にぱっとやる方法がなくて、ブラウザのブックマークに入れておき後でPCを開いて丁寧に移すということをしていたけれど面倒だった。

きっかけは忘れたけれどWeb Share Target APIという存在を知って、Webアプリでできるならと思って試してそつなく動いている。半日で完成したので満足。

Web Share Target APIはmanifest.jsonに共有されたら開かれるURLを書いておくと、Androidの共有メニューで選択されたらそのURLが開かれるのでその先はアプリで好きにできる、という仕様。
現時点でのステータスはunofficial draftなのでそういうつもりで接します。

Next.jsでPWA

NetlifyもFirebase Hostingも使ったことあるしVercelは触ったことないので試してみよう、Next.jsと良く統合されているっぽいのでcreate-react-appでも良いけどNext.jsにすることにした。

next-pwaを使うとWorkboxを使って良い感じにService Workerに登録するスクリプトをホストしてくれる。

イニシャルで特定される都道府県

anond.hatelabo.jp

ルール:

使ったコードは以下:

#!/usr/bin/env ruby

require 'abbrev'

prefs = DATA.each_line.map {|l| l.strip.split('-').first.downcase }
abbreviates = Abbrev.abbrev(prefs)
unique_prefs = abbreviates.keys.select {|k| k.size == 1 }.map {|k| abbreviates[k] }
puts unique_prefs

__END__
Hokkai-do
Aomori-ken
Iwate-ken
Miyagi-ken
Yamagata-ken
Fukushima-ken
Ibaraki-ken
Tochigi-ken
Gunma-ken
Saitama-ken
Chiba-ken
Tokyo-to
Kanagawa-ken
Niigata-ken
Toyama-ken
Ishikawa-ken
Fukui-ken
Yamanashi-ken
Nagano-ken
Gifu-ken
Shizuoka-ken
Aichi-ken
Mie-ken
Shiga-ken
Kyoto-fu
Osaka-fu
Hyogo-ken
Nara-ken
Wakayama-ken
Tottori-ken
Shimane-ken
Okayama-ken
Hiroshima-ken
Yamaguchi-ken
Tokushima-ken
Kagawa-ken
Ehime-ken
Kochi-ken
Fukuoka-ken
Saga-ken
Nagasaki-ken
Kumamoto-ken
Oita-ken
Miyazaki-ken
Kagoshima-ken
Okinawa-ken

実質5行で求められて便利。abbrevはたまにこういうことするのに便利で好き。

続きを読む

AWS CDKアプリを作る時のテンプレートリポジトリを作った

AWS CDKの昨今

最近は仕事でもプライベートでもAWSのリソース配備にはAWS CDKを使っている。
ちなみに最近はCDK for Terraformが出るなど、AWSに限らないツールキットを指向している雰囲気があるので「仕事は {ここにAWS以外のクラウドプロバイダが入る} だからな〜」「AWS以外も含めたハイブリッドクラウド環境だからな〜」という人も一度触ってみるとおもしろいのではないでしょうか。

ちなみにCDK for Terraformのようなものを作る布石はaws/constructsという前は@aws-cdk/core以下にあった階層や依存関係を表現する基礎的なクラス群が独立したライブラリとして提供されはじめたころからあったと思われる。

CDKはCloud Development Kitという名の通り従来のIaCというところから一歩進んだ「クラウド開発キット」であり、マネージドサービスを使っていくとインフラかアプリケーションかという区別が曖昧になっていくなら、構築支援するライブラリ・ツールキットもそれらを無理に分け隔てる必要はないのでは? という着想が垣間見える。実際使っていると隔たりが限りなく取り払われていく感触がある。
たとえばAWS CDKには手元のファイルをS3にアップロードしたり、手元のディレクトリでdocker buildしてECRにイメージをpushするライブラリなどがあったりする。Lambdaのコードを手元からアップロードするのはとても便利。

アプリケーションを上から下までプログラムで構築していくという考え方が徹底している。そういった話は1年前くらいの発表で触れていて、変わりない。

speakerdeck.com

CDKのベターデフォルト

cdk initで雛形はできるけれど、いくつかの点で不満がある。

  • ESLintが同梱されていない
    • ここに空白あったほうがいいですね、とか議論していい時代はもう終わった
  • renovateが設定されていない
    • CDKは頻繁に更新されるのでこれ無しではすぐに滅ぶ
    • アップデートはさほど苦しくないので自動化を徹底することが大事
  • GitHub Actionsの設定がない
  • TypeScriptのコンパイルが必須

などなど。

ESLintとかはともかく、TypeScriptのコンパイルを必須とするのは個人的にはCDKのような、Vanilla JSへ変換することが必須だったりアセットの整合性を追跡したいような要件がない場合には冗長だと感じる。
なので--noEmitとts-nodeで良いじゃん派。cdk deployとかして古いな〜 → コンパイルしていなかった、というようなできごとで失う時間のほうが多いと考える。

ので、良いかんじのtsconfigなどを込みにしたリポジトリを作った:

github.com

含めてあるものは上記の通りで、

  • ESLintと良いかんじの設定
  • renvoateの設定
  • 良いかんじのtsconfig

など。

テストなどはcdk initで出力されるものを基本的に踏襲している。

公開されたシンボルにJSDoc/TSDocを書くことをESLintでルール化したい

公開しているライブラリほどじゃないにせよ、チームで開発しているTypeScriptで書いたコードにちらほらドキュメンテーションしていきたい。

それESLintで

とりあえず今までろくに書いていなかった部分はいまさっき書き足すPull Requestを作ったけれど、今後手を入れる時にもちゃんと書いていってほしい。
で、こういうルールの徹底を人間がレビューで「ドキュメントおねがいします」って言うのは不毛。2020年代にやっていいことではない。

TypeScript/JavaScriptでやろうとなったらESLintでどうにかしたい。eslint-plugin-jsdocがJSDocに関連したルールを提供している。
JSDocはTSDocと基本的なフォーマットは同じで細かいタグのセマンティクスは差異がありそう。
そしてなんとeslint-plugin-jsdocはTypeScriptもサポートしている *1

ちなみにTypeScriptでドキュメント書く時はけっきょくどのフォーマットがええんや (なんかesdocっていうのもあったような) と思っていたらちょうど良いところに:
blog.pokutuna.com

最高。

ほどほどに始めたい

とはいえ社内でのみ開発されるアプリケーションのドキュメントをがちがちに書けというのも大仰なルールだし、そもそもTypeScriptで担えている型情報とかは書かなくて良いということにしたい。

前者は公開されたシンボルにだけドキュメントが書いてあれば良いということでよさそう。公開というのはexportされている、もしくは可視性がpublicであるフィールド・メソッドという定義にする。

eslint-plugin-jsdocにはそのものずばりpublicOnlyというオプションがrequire-jsdocルールにある。

そんなこんなで始めたESLintのルールが以下:

  "eslintConfig": {
    "extends": [
      "eslint:recommended",
      "plugin:@typescript-eslint/eslint-recommended",
      "plugin:@typescript-eslint/recommended",
      "plugin:@typescript-eslint/recommended-requiring-type-checking",
      "plugin:jsdoc/recommended"
    ],
    "plugins": [
      "@typescript-eslint",
      "jsdoc"
    ],
    "parser": "@typescript-eslint/parser",
    "env": {
      "node": true,
      "es6": true
    },
    "parserOptions": {
      "ecmaVersion": 2018,
      "sourceType": "module",
      "createDefaultProgram": true,
      "project": "./tsconfig.json"
    },
    "rules": {
      "jsdoc/require-jsdoc": [
        "error",
        {
          "publicOnly": true,
          "require": {
            "ArrowFunctionExpression": true,
            "ClassDeclaration": true,
            "MethodDefinition": true
          },
          "checkConstructors": false
        }
      ],
      "jsdoc/require-param-type": 0,
      "jsdoc/require-returns": 0
    }

むすび

*1:大半のルールにおいては。一部のルールはまだサポートされていない

react-type-safe-render: 第二引数にnullを許容しない安全なReactDOM.renderの型定義を書いた

@types/react-domで `ReactDOM.render` の第2引数 (container) はnullを許容しており、`document.getElementById` の返り値の型に合わせているそう。
しかしランタイムがnullを許容しているわけではなくnullを渡すと実行時例外になる。

TypeScriptを使っているのにこういうことが起きるのはなかなか悲しいので、nullを許容しないより安全なrender代替をexportするライブラリと、ReactDOM.renderを禁止するeslint pluginを作った。

GitHub - aereal/react-type-safe-render: Provides `safeRender` function that is more type-safe version of `ReactDOM.render` with no additional implementations

npm i -S @aereal/react-type-safe-render
yarn add @aereal/react-type-safe-render
npm i -D @aereal/eslint-plugin-react-type-safe-render
yarn add -D @aereal/eslint-plugin-react-type-safe-render

実装はとても素朴でRendererの定義から丁寧にnullを取り除いただけ。
関数の実体はReactDOM.renderそのものなので、実行時にペナルティは無い。

またESLintのプラグインを初めて作った。

@types/eslintにはプラグイン自体の型定義がなくてドキュメントを見ながら作っていたら、疲れていたのかしょっちゅうミスしてだいぶ時間がかかってしまった……。

最初はno-restricted-importsのruleを定義を持つconfigを配れば良いじゃんと思っていたのだけれども、利用者でno-restricted-importsを使いたい時に競合して上書きしあってしまうので実用的ではなく、似たような実装のruleを別に作ることにした。

けっこう楽しかったのでおもしろESLintプラグインを作って遊んでいきたい。

株式会社はてなを退職

2020年8月14日付けで退職する運びとなった。
入社が2012年なので勤続丸8年を迎え社内でも古株の方になってきつつある。Web業界にしてはわりと長くいたほうだと思う。
自分自身でもこんなに長く籍を置くとは思っていなかったので驚いている。

はてなに入社してから3039日

退職を決めた理由は主に2つ。

金沢移住

1つめは、現在住んでいる京都を離れて金沢で暮らしたいと考えたから。

数年前に観光で訪れた金沢を歩いてから一目惚れしてしまい、自分がここで生活する想像をするうちに単なる夢想から具体的に実現することを考えはじめた。
これを書いている時点で、株式会社はてなの事業拠点は東京と京都のみであり、在宅勤務は育児や介護、その他会社が認めるに足る理由があるケースのみ認められている。
平時は週数日程度スポットでの在宅勤務はマネージャーと合意した上では認められている。またコロナ禍においては在宅勤務推奨となっている。ただし、継続的にフルリモート勤務というのは上記の通り認められる事例は限定的になっている。
このまま在職しつづけても実現が難しそうと判断したのが、転職を決意した最大の動機といえる。

制度設計や判断について特に言うべきことはない。単に自分のニーズに合わなかったというだけのことだと思う。

幸か不幸かコロナ禍でリモートワークを半ば余儀なくされ当事者は拡大しているので、議論が進む機会となることを願っている。

一応、制度の拡大について提案してみたが諦めた。
世の事例を調べるに、ローカルワーク *1 に慣れたそれなりに成熟している企業がリモートワークに切り替える際には、

  • トップダウン
  • 合理性や生産性の観点で評価するというより、働きやすさ・自己実現の促進という観点を優先させた

事例がほとんどだった。

つまり、トップがある物差しを差し置いて別の物差しを守ろうとする強い判断を起こしたというヒロイックな事例しか見つからなかった。
「働きやすさを守る経営者」というと美しく見えるが一長一短であると思うし、どちらが良いという話でもない。

ここで重視したのは、生産性がローカルとリモートでどれくらい変化しうるとか、それをどう埋め合わせるための仮説や検証をこうしたとか、経営判断に繋がる十分な強度を持った提案の根拠として十分な仮説や先行事例を当時は見つけることができなかったということ。

ただしこれらは転職活動を始めた当時の調べによるものであって、現在は状況が変わっているのでこれまで在宅勤務の導入に踏み切っていなかった企業がうまくバランスさせながら導入を試みる事例や方法論がより多く世に出ていくことと思う。

そして、当社は企業全体の生産性と働きやすさをバランスさせる制度を前提としており、なかなか骨が折れそうなので諦めることにした。
実際、全体最適を考えてリモートワークを拡大しようとすると慎重にならざるをえないと自分も思う。しかし、今自分が選びたいのは組織全体にとって善となることより、個人の願望を十分なスピードで叶えることだった。

また、リモートワークが前提となった世界でではどううまくやっていくか? という問題に取り組みたいと考えた。
過去に失敗した事例はいくらでもあるし容易な道ではないことは承知しているが、それでもフロンティアに立ってどう解決するかに自分の時間と情熱を捧げたい。

後述するように、他の動機もあって留まることにこだわることはないと考えた。

ちなみに、在宅勤務ではなく通勤手当の拡大という線でも提案したがこちらも諦めた。在宅勤務の拡大より合理性の面でより望み薄 *2 だと考えていたので、これも仕方がないと思う。

スーパーリセット

2つめは、大きな変化を欲したから。

端的に言えば老害なりかけの自分を一旦捨てたかったから。

blog.kentarok.org

8年勤めて、様々なことに慣れた。仕事の進め方や手続きで不明なこともない。
中心に据えられている事業分野で求められるであろう技術・スキルはそれなりに備えていると思うし、業界が発展してもキャッチアップしつづける自信もある。
それなりに信頼も得ている。自分の発言はそれなりに尊重してもらえる。

でも、ふとした時に自分ってそんなに大した人間なのか? って思う瞬間が増えた。これって、単に時間を重ねただけの結果なんじゃないの?
ここに至るまでにもっと短い時間で済ませられたり、同じ時間をかけたらもっと遠いところに至れる可能性はなかったの?

ちゃんと自分は成長するべくして成長したのか。エンジンはへたっていないか、惰性に任せきりではなかったか、そういった自問自答に自信をもって答えられる根拠は少ない。
単に周りに恵まれただけで、自力は大しことがないのではないか。

自分は仕事以外にも時間を使いたい趣味はいろいろあるし、仕事一筋でも大好きというわけでもない。働かずに暮らせるなら今すぐそうしたいと思う。
しかしそれも難しいので、ならば生活の中で少なくない時間を使う仕事はとてもおもしろいものであってほしい。おもしろい仕事は難しい。難しい仕事を果たすためには技量と信頼がいる。
そういえば面接で当時社長だったjkondoさんに「僕は仕事がしたくないです。仕事を仕事だと思ってするつまらないことは絶対にしたくないです」って言ったおぼえがある。

株式会社はてなに留まってもそういう仕事ができる可能性は十分にあると思う。しかし自分がへたっていないか、ちゃんとおもしろい仕事を楽しみつづけるだけの体力が今もあるのか、それを維持できる体なのか、ということを試したい。
そのために、自分の身ひとつ以外は無いところに身を移してみたいと考えた。

8年ちょっとでやったこと

Resume - aereal

転職活動を始める際にさっと職務経歴書にまとめた。ここに書くことに苦労しないくらい色々とおもしろいことがやれたのは本当に良かった。
複数の相手にURLだけ伝えれば良い・見るであろうぎりぎりまで手直しできる、などのメリットがあるのでPDFとかを都度送るのは非合理だなと思った。

新サービス立ち上げから独自ドメインHTTPS配信、オンプレミスからクラウドへの移行などなど。Webのソフトウェアエンジニアとして一通りここは通りたいと思っていた分野を一巡りできたのではないかと思う。

特に独自ドメインHTTPS配信システムを作るのは良い経験になった。RFCで定められたプロトコルに従って実装を書いたり、そもそもTLSまで降りてプロトコルを学んだり、それまでのWebアプリケーションエンジニアから少し踏み出したエンジニアリングができた。

これから

そんなこんなで転職活動を始め活動を終了した。8月から新しい会社で働く。
金沢で暮らしたいという意思を伝えて理解いただいたうえでオファーをいただいたので確実に移住への大きな一歩を踏み出した。

次もWeb業界でソフトウェアエンジニアをやる。試用期間が終わるまでは所属は明らかにはしないと思う。クビになったらダサくて恥ずかしいので。

転職活動はかなり消耗したのでしばらくやりたくない。まず初めての転職だということもあるし、おまけにCOVID-19の拡大と重なってしまい現職の仕事も転職活動もだいぶ大変になった。
内定が出たGWごろは、楽しみだった旅行もライブもすべて無くなってしまい気分が完全に落ちていたので手放しで喜べなかったどころか、途端に不安になってしまったりかなり参っていた。

最後に。大学を中退したどこの馬の骨ともわからない人間が、10年近く経って贅沢に自己実現やキャリアのことを考えられているのも伸び伸びと仕事ができる環境があってのことだったと思う。
また、お世話になった方は数知れずいるものの、id:hitode909さんは自分が入社する以前からよく気にかけてくださり、大学もアルバイトも辞めて実家に帰るか野垂れ死ぬか考えていた時に「(はてなに) アルバイト応募してみませんか」と声をかけてもらえなければこうしていることはなかったでしょう。
入社してからもソフトウェアの話、仕事の話、おもしろ技術の話、様々に刺激を受けお世話になりました。数年来hitodeさんが務めたチームのリーダーを引き継ぐと決めた時にやっと同じ土俵に立てたように思い、とても感慨深くまたやっと自立できたと思える瞬間でした。
こういう話はしたことがなかった気がするけれど、ずっと意識していたのです。

在職中は他にも挙げきれないほどの方々にたいへんお世話になりました。本当にありがとうございます。引き続きインターネットでお会いしましょう。

*1:同一拠点で勤務すること。オフラインと表現することもあるが、リモートとより対称がとれていそうなのでこの表現を選ぶ。

*2:近距離通勤手当を福利厚生として支給している

JWEは毎回ランダムなInitialization Vectorを使用するので得られるトークンは毎回異なる

タイトルですべて言い切ってしまった。

JWE (= JSON Web Encryption) にはInitialization Vectorというフィールドが含まれており、これは暗号化処理ごとにランダムな値が使われる。
なので、同じペイロード・同じ秘密鍵を使って暗号化しても暗号化したトークンは異なる値になる。

もともとJWS (= JSON Web Signing) で署名したトークンを使っていて完全一致でテストしていたコードが暗号化するようになって必ず落ちるようになりハマって調べた。

複合して元のメッセージが得られれば良いので暗号化した文字列が毎回異なっていても良い、というのは暗号化において一般的なのかもしれないと後から思い至ったけれど、同じ入力・秘密鍵を与えて変わると思っておらず、テストの書き方がおかしいのだと思い込んで時間をつぶしてしまった……。

scrapbox.io

Scrapboxにもまとめた。

cloud.google.com/goのリクエストがX-Rayでトレースできないので土日を潰して調べて完全に理解した

cloud.google.com/goはGo向けのGCPAPIライブラリ。

一部を除き単にHTTPリクエストを送っているのでX-Rayでトレースする *1 のは造作もないだろうと思ったら思いがけず難航し、だらだら調査していたら2日かかってしまった。

詳しいメモと結論はScrapboxにまとめた。

scrapbox.io

読んだのは

の3つ。

VSCodeでdelveを使ってデバッグする体験がだいぶ良くて捗った。というかこれがなかったらつらくて諦めていたと思う。

土日を潰したけれど結果的によく使う大きなライブラリのコードリーディングをする良い機会になった。特にgolang.org/x/oauth2は雰囲気だけ知っていたけれど、まとまった時間をとって読んだりメモしたことはなかったので。

あと複数のライブラリを行き来しながらメモを書くのにScrapboxがかなり役に立った。
最初は調査用メモのページに実行パスをあれこれ書いていたけれど、階層が深くなっていくし同じメソッドを複数回呼ばれている時に表現しようがなくて困ったけれど、各関数ごとにページをわけたらすっきりする。
ページのリネームが簡単にできるので、書く時は `Cred()` とかにしておいて、後からパッケージ名を含めた完全修飾名にして検索しやすくするのも苦ではなかった。しかも後から補完が効いて便利だし。

早くにoauth2のissueを見つけて、てっきりこれがすべての原因だと思っていたけれどデバッガを使っているうちにトークン取得リクエストだけではなくAPI呼び出しキャプチャするaws-xray-sdk-goの実装が呼ばれないのは変だということに気がつくまでに時間がかかった。
これに気がついてからはあたりをつけて調べは早くに済んだけれど。

*1:github.com/aws/aws-xray-sdk-goを使って