やたらと短いエイリアスは使わない

Gitもシェル (zsh) もよく使うコマンドをエイリアスとして設定して任意の入力で呼び出すことができる。

このエイリアスについていくつか派閥があると感じていて、おおまかには:

  • エイリアスをまったく使わない
  • 入力の手間を減らすため極限まで短いエイリアスを使う
  • よく使うコマンド・オプションの組み合わせに説明的な名前をつける

……という派閥に分類できると思う。

自分は3つめの「よく使うコマンド・オプションの組み合わせに説明的な名前をつける」に属する。

短いエイリアスへの苦手意識

衝突しないようエイリアスを管理するのがだるい

エイリアス名前空間を通常のコマンドと共有しているので、それらと衝突しないよう管理しなければいけない。

短い名前を設定しても思い出せない

よく使うコマンドなら手癖で打てるだろうけれど、ごくたまにしか使わないコマンドのごく短いエイリアスを思い出せる気がしない。

これはエイリアスではない通常のコマンドでも起きるけれど、コマンドは検索なりすれば出てきて辿れるのでリーチできるが、エイリアスの場合は検索しても出てこないので死にエイリアスになったままになりがちという問題があると思う。

これはシェルやGitの設定でコメントを書くとか

誤爆のリスクが高まる

たとえば grgit reset --hard HEADエイリアスしているとして、 grep と打とうしたが誤って gr でエンターを押してしまって誤爆すると悲惨なことになる。

危険な操作にエイリアスをつけるなとか、いろいろ突っ込みどころはあるが、操作者に責任と自由が委ねられているシェルにおいてわざわざ危険な操作を実行する敷居を下げることもないなと感じている。

検索性が低い

シェルのヒストリ検索は文字列の部分一致で行うのが常なので、ごく短い文字列にしていると発見しづらい。

これは技術的に挽回する余地はあると思うけれども、後述するように長いが説明的な名前をつけて、それを検索しやすくするというほうが技術のテコをきかせやすく総合的に生産性が高まると感じている。

説明的な名前をつける

特にGitの操作はやたらと難しいので自分でいろいろエイリアスを作っている。たとえば:

  • git cp: git cherry-pick
    • ファイル操作のメタファーになぞらえて
  • git interactive-rebase, git continue-rebase, git abort-rebase: それぞれ git rebase --interactive, git rebase --continue, git rebase --abort
    • git-rebaseはinteractiveとそれ以外でかなり性質の異なるサブコマンドだが1つに押し込められておりかなりヤバい設計になっている
    • 区別しやすくなるだけではなく、先頭一致で検索する際にサブコマンドの先頭を分けることで誤ったrebaseのオプションを選ぶことを防ぐ意味がある
      • git rebase まででC-pすると git rebase --continuegit rebase --abort も候補に含まれるが、 git cont を入力してC-pすると git abort-rebase が候補に出てこない

など。

そもそもGUIを使えばっていうのはごもっともだと思うけれど、なんだかんだ複雑であまり整然としていないと批判されることの多いGitのCLIも覚えておけばリポジトリの統合とか凝った操作をする時に役立つことがあるので、そこは妥協している。

組織をプロダクトと捉えてみる

  • ※組織の話は詳しくないので自信はないけれど、こう思ったという話を書きます
  • ※現在の所属組織の社内ブログに書いた内容を一部修正して転載しています

プラットフォームのプロダクトマネジメント

メルカリのSREであるdeeeetさん (eは4つ) のこの記事が良かった: 社内PlatformチームのProduct Management | SOTA

直接エンドユーザーに面しないプラットフォームであってもプロダクトマネジメントの基本的な考えや方法論を適用し「ちゃんと」やっていく話と、エンドユーザー向けのプロダクトマネジメントと異なるビジネスとのバランスの話が挙げられている。 参考となる書籍やページがたくさん挙げられているので、おすすめ。

特にどのような優先度がやるかについて紹介されていた方法が自分にとっては初見で有益だったので紹介:

大きなタスクの分類としては左から「User asks」「Platform quality」「Long term key initiatives 」が挙げられる.それぞれの意味は以下.

  • User asks: 開発者の問題を解決するタスク
  • Platform quality: Securityや技術的負債などPlatformの質を担保するタスク
  • Long term key initiative: 長期的な投資のためのタスク

Will Larsonはこれらを40:30:30でバランスをとるのが良いとしている.自分たちは完全にこの比率に沿っているわけではないが分類を含めて参考にしている.

40:30:30の根拠は原文を読んでいないのでよくわかっていない。あとで読む。

他の指標としてBusiness, ROI, SLOが挙げられている。これはまあ普通なかんじ。

組織をプロダクトとして捉えてみる

Developer Experienceがバズワードしていった流れは、作るものが複雑になってきて成果の精度や価値を左右する要素として過程の改善が占める割合が小さくない・投資効率がそれなりに高いと認知されてきたからだと思う。

古くはアジャイル開発を始めとして、最近ではソフトウェアエンジニアリングと組織開発の両方に立脚して意義や効果を説いたという点では広木さんの『エンジニアリング組織論への招待』も挙げられる: エンジニアリング組織論への招待 ~不確実性に向き合う思考と組織のリファクタリング

これらの背景から組織をプロダクトとして捉えると、改善の進め方やうまい機能のさせかたは実は身近なところから応用できるのかもしれないと考えた。

たとえばプロダクトを開発する部署の顧客は誰だろうと考えると:

  • 経営者:競争力のあるプロダクトを速く正しく作る実行力を提供する
    • 主要な結果の例: 設定した稼動率目標の達成、競争力の獲得
  • プロダクトを開発する部署の各メンバー: 知的好奇心を満たすに足る場や業界で生き残る知識・スキル獲得の機会を提供する
    • 主要な結果の例: 昇給

……など。

同様にプロダクト開発の考え方を適用すると、自分の所属の開発部署をどう変えていくかというロードマップを敷くのは未だできていないことで効果が見込めることかもしれない。

Roadmapを作る意義は大きく2つある.まず開発チームからするとどのような機能がいつ頃利用可能になるのかが明確になる.(略) 次にPlatformチームのメンバーからすると今後何を作るかが分かるため,実装時の意思決定の助けになる.「3カ月後にこういうことをやります」と分かって書くコードと、知らないで書くのとでは全然違う.「次にこうするから今はこういう実装をしておけばOK」といった意思決定ができるになる.

以下の優先度の話にもつながることだがRoadmapを作るといかに「やれることが少ないか」が分かる.(略)

https://deeeet.com/writing/2020/10/07/internal-platform-product-management/ より。

組織とプラットフォーム (基盤) は、提供者と受益者の関係性が二面的であったり曖昧であったりする点に共通点を見出すことができる。

上記例では各メンバーを顧客としたが、同時に開発部署を作るのも各メンバーであって、どちらかのペルソナだけを持つわけではない。 これは社内プラットフォームにも言えて、開発者と利用者が完全に分断されているケースのほうが珍しいだろう。

個人のキャリア形成という文脈では受益者という立場が強く出るだろうし、マネージャーに近い役割を負う人は提供者という立場の割合が大きくなることが想像できる。

ロードマップがあれば、近い内に解決が難しい問題に直面した時に実は3ヶ月後なら変わる芽がありそうだとか、しばらく解決は難しそうだが積み上がっている課題を見れば納得感はあるのでバックログを片付けることに協力しようとう気持ちになるなど、そういったコミュニケーションをとれる人が増えないだろうか。

また人事評価 (MBO) とは独立して、かつ人事評価の単位より長い時点までのロードマップを立てられると、人事評価のようなモノリシックなシステムを変えることなくメンバーにより遠い未来について思いを馳せる機会が与えられるかもしれない。 それは視座の上昇の機会となり、自己組織化を促すきっかけにならないだろうか。 リーダーであるための視野・視座・視点 - Tech Inside Drecom

正しく作る

という話に還元されそうなので未読の以下を読みたいと思います:

正しいものを正しくつくる プロダクトをつくるとはどういうことなのか、あるいはアジャイルのその先についてn

まとめ

  • 冒頭に挙げたdeeeetさんのエントリは良いことが書いてある
  • 組織運営も同様にプロダクトマネジメントの手法やベストプラクティスが応用できないだろうかと考えた
  • その考えに則ると、ロードマップの策定は効果が見込めるのではないか

AWS API Gatewayのdocumentation partに関するメモ

Documentation part

AWS API GatewayREST APIはOpenAPIに準拠したドキュメントの生成もサポートされている *1

API GatewayREST APIはリソースやメソッドなどの部品から成る。特別な定義なしでも、これらAWS上のリソースが持つ各種プロパティからデフォルトでまあまあ良い感じのドキュメントは出力される。
しかしデフォルトではOpenAPI 3が必須とするいくつかのプロパティ *2 が出力されなかったり、実用的なAPIドキュメントに含めたい内容が欠けていたりする。

API GatewayREST APIの各構成部品に対して追加のドキュメンテーション情報を持つことができ、それらはdocumentation partと呼ばれる。
refs. Documenting REST APIs - Amazon API Gateway

location

documentation partのインスタンスは自身がAPI, オーソライザー、リソース、メソッド、などどの部品に対するドキュメントなのかを指す情報としてlocationを持つ。
refs. Location of a documentation part - Representation of API documentation in API Gateway - Amazon API Gateway

locationは各部品ごとに必須・利用可能なフィールドが異なっておりValid location fields of DocumentationPart - Representation of API documentation in API Gateway - Amazon API Gatewayにまとめられている。

properties

documentation partはpropertiesという任意のオブジェクトを持つことができ、これがOpenAPI spec.に現れる。

このpropertiesはOpenAPI spec.に準拠していないといけないとドキュメントでも言及されている。
refs. Content of a documentation part - Representation of API documentation in API Gateway - Amazon API Gateway

即ち仕様で定められたプロパティか、あるいは x- で始まる任意の拡張プロパティのみが使える。

invalidなプロパティはAPI Gatewayによって無視され出力に含まれない。

実体への対応

どうもdocumentation partは対応する部品の実体なしにエクスポート結果には現れないようだった。
これは公式ドキュメントの記述を引いたわけではなく、挙動からの推測になる。

HTTPメソッド定義がなければそれをlocationに持つdocumentation partが現れないというのは想像しやすいけれど、自分はパスパラメータのdocumentation partが出てこなくてしばらくハマった。

現時点ではパスパラメータのdocumentation partはrequestParametersに含めないと現れないようだった。
これは恐らくクエリパラメータやヘッダについてもそう言えると思う。

documentation version

ここまでで定義してきたdocumentation partを反映するには新しいdocumentation versionを発行しなければならない。
APIドキュメントの版を指し、新しい版が最新のdocumentation partを含む。

documentation partだけCloudFormationなどで定義してもdocumention versionを新しく作成しなければ参照できない。

*1:公式ドキュメント中ではエクスポート, exportと表現される

*2:たとえばリソース定義中のパスパラメータ

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プラグインを作って遊んでいきたい。