たごもりすメモ

コードとかその他の話とか。

さくらインターネット入社後3ヶ月のいまの話

さくらに入って3ヶ月経過して、入社直後といま現在ではまた見えている状況も違うし、実際どうなのっていうのを今の段階で書いておくのも悪くないかなと思った。自分より後に入ってきた人達が1ヶ月でのエントリを書いている(link)(link)のに触発されたというのもある。

入社直後のエントリはこちら。 tagomoris.hatenablog.com

思っていたより会社の変化が激しい

入社してから1ヶ月は、社内でこれまでクラウド開発・運用やガバメントクラウドをリードしてきた人達とひたすら話してキャッチアップ、みたいな感じで過ごしていた。で、なんとなく分かってきたかなと思ったのに、翌月から切れ目なく色んな人の入社が続いていて、いま自分がいるクラウド事業本部という組織がすごい勢いで変わってる。特にシニア層というかエンジニアリングマネージャ系の人達の入社が続いていて、これからどうするべきか、あちこちで議論が始まってる(いくつかは自分も始めている)。

これはどちらかというと、組織運営をやりかたに変化がほしい、という動機が先にあって、そのための採用がうまくいった結果がいま表れている、というのが正確な理解だと思う。自分としてもやりたいこと・こうなったらいいと思うことのうち、マネジメント系のことについては頼りになる同僚がどんどん増えているという状況なので嬉しい限り。

思っていたより状況がワイルド

ワイルドってなんだ、というと、なんだろう、スタートアップにありがちな未整備状態があちこちにあるというか、どこもかしこも成長の余地だらけというか……。歴史あるユーザ数の多いサービスをいくつも持っている会社なんだけど、内部を見てみると思ったよりも落ち着いてないなこれ、というか。

これは自分にとっては特にマイナスというわけでもない。特にビジネスが成長していると内部の整備が追い付かないというのは普通だと思う、というか、ビジネス成長速度が速い会社はそうなっていて当然とまで思っているフシが自分にある。前にいたライブドアもトレジャーデータも、特に入った直後はそんなもんと言えばそんなもんだった。そういう状況だとやり甲斐しかないというか、自分のやれることの余地がすごく大きいので割と好きだし、自分以外でもそういう状況が好きな人はまあまあいるんじゃないかと思う。

思っていたより周囲から注目がある

みんな結構ガバメントクラウドとか、あるいは国産クラウドについて思うところがあったんだなー、という感じで、入社後に会った人に応援の言葉をもらうことが多くて嬉しい。行政のデジタル化みたいなものを応援したい、という気持ちの一環みたいなところに国産クラウド応援がくっついているという場合もあって、これは入社前の自分を振り返ってみてもよくわかる気がする。

とはいえ、現状の機能などだと、AWSやGoogle Cloudをあたりまえに使っている各位の期待にすぐには応えられないので、この注目と期待があるうちに状況を前に進めていくぞ、ということで内心ちょっとシリアスな気分になることもある。

クラウドプロバイダの初心者プロダクト担当

プロダクト担当として何をやるべきか、みたいなことを考えながら、全体に影響があって時間がかかるいくつかの話はさっさと始めたりしている。クラウドサービスプロバイダにおけるプロダクト担当となるとエンジニアとしてのバックグラウンドがある方が有利に働くに決まってる、と入社前から考えていたところは見事に的中してると思うんだけど、いっぽうで各種の認証や規制、省庁のガイドライン、各業界の動向など知らないことも多くて内心悲鳴を上げがち。人生常にキャッチアップだな。ただ、自分が不得意なところを得意にしている超優秀な同僚もいて、これは本当に良い。勝手にあれこれ勉強させてもらってます。

入社エントリに「さくらのクラウド全体、あるいは広くさくらインターネットグループの中でやれることは何でもやるつもり」と書いたけど、本当にあれこれやってる。入社動機の大きなひとつ「エンジニアリングとビジネス両面を相手に仕事を」できていて楽しい一方で、コード書くのとは違ってなかなか即座には結果がわからないことばかりなので、本当にこれでいいんだっけ……みたいな不安におそわれたりもする。いやはや。

採用! 採用! 採用!

プロダクトまわりであれもやりたい、これもやりたいということが多く、しかし当然メンバーのほとんどは現行の開発プロジェクトで手いっぱい、という状況を見ると「採用! 採用しないと!!!」みたいな強烈な気分におそわれるので、採用まわりにもけっこう時間を割いてる。自分の知り合いのシニアエンジニアはみんなさくらに来ればいいのに……くらいに言いたい気分。転職考えてもいいかなって気分の人はいますぐ自分に連絡してください。

一度シニアになっちゃうとなかなか開発に集中できない、みたいな話も無くはないと思うんだけど、その点さくらについてはどんどん開発に集中してほしくて、そのためのポジション作りと待遇面の優遇はどちらもなかなか良い状況だと思う。だってクラウドのマネージドサービスを設計して作れる人なんて超貴重だもんね。もちろんプロジェクトをリードできる人だけでもなくて、プロジェクトのデザインと計画をきちんとフォローして開発を進められる人も大募集中です。データセンタの建物や電源みたいな超低レイヤから、完全ソフトウェアで構成された高レイヤのマネージドサービスまで、やることを超絶幅広い中から選べるのが特徴です!

あと、プロダクトまわりでもやることがいくらでもあるので、プロダクト担当のチームを立ち上げないといけないかなあとか、マネージドサービスがどんどん出ていくとSDK作らないといけないからDevRel職を置かないといけないんじゃないかとか、そういうことも最近考えてる。

コード書いてないと腹がへるの話

仕事でコードを書かない毎日にようやく慣れてきた。驚くことに入社後マジで1行もコードを書いていない*1。ちょっとした実証コードを書かないとなとは思っていて、それはまだ手が回ってないのが問題なんだけど……。

それはともかく、コード書かずにドキュメント書いてスライド書いて人と話してとやってると、コードを書いているときと較べて腹がへる、という恐るべき事実が自分に起きていることに気付いた。コード書いてるときは飴玉舐めるとかラムネ食べるとかでいかに脳に糖分を届けるかを考えてたのに、なんか最近それだけだと足りなくて、空腹を強烈に自覚するようになったんだよね。なんなんだこれ。エネルギーの消費のしかたが違うのかなあ。

脈絡のないmorisnite vol.5の話

で、脈絡ないですが、そのさくらインターネット東京支社を会場に使えるので、目的もなく集まって飲食するというイベント morisnite vol.5 をやります! 11月22日。

connpass.com

クラウドの話をしたければしてもいいし、したくなければ最近あのコードどうなのよって感じであれこれ楽しく話したいですね。どなたでもご参加ください。

*1:さくらの仕事としては。OSSは普通にやってる。

さくらインターネットに就職しました

TL;DR

  • さくらインターネット株式会社で8/1から働いています
  • さくらのクラウドの一人目のプロダクト担当ということで、エンジニアリングとビジネス両面を相手に仕事をしていきます
  • ソフトウェアエンジニア大募集中です、いっしょに働こうぜ!

経緯

Treasure Dataを辞めたのが2021年7月末*1でしたが、それから丸3年経過しましたね。早いなあ。その間は本を書いたり個人サービスを作ったりしつつ、個人事業主の技術顧問として数社をお手伝いしたりしていましたが、個人的な事情がいくつか落ち着いたりしたこともあって、今年初めくらいからそろそろフルタイムで働くことを考えはじめ、多くの会社さんとのやりとりを経て、さくらインターネット株式会社への入社を決め、今月から入社し働きはじめました。

どういうポジションで何がしたいのかについては当初だいぶふらふらしていたのですが、いくらかの会話を経て「ビジネスを作る/育てる、そのためのチームを作る/育てる」というあたりを目標として動けるポジションで働くことを重視するようになりました。直近2つのキャリアも自分の対外イメージも、ICとしてのソフトウェアエンジニアとしてかなり強いものがあったようで、声をかけていただいてもミスマッチですね、となることも多かったところ、それでも是非といってくださる会社さんもあったのは本当に自分にとっては嬉しかったですね。

他にもチャレンジしているビジネスが本当にすばらしい・有望そうなスタートアップのポジションなどでもだいぶ悩んだのですが、会社のビジネスの状況、オファーされたポジション、待遇、などぜんぶひっくるめて考えて、さくらインターネットで働くことにしました。各社との話の中で何をやりたいかが明確になっていったのは全く否定のしようもなく、声をかけていただいた方々、選考プロセスでお世話になった方々には本当にお世話になりました。この場でになりますが、報告とお礼に替えさせていただければと思います。

さくらインターネット、さくらのクラウド

さくらインターネットという会社は、パブリッククラウドをやるぞという強い意思と、直近でも旺盛な投資意欲があるという意味では国内随一のクラウドサービスプロバイダであると自分は見ています。もちろん現時点でAWS、Google Cloud、Azureといったハイパースケーラ各社には及ぶべくもありませんが、それでも国内では現時点である程度以上のポジションを持っています。その上で、これからガバメントクラウドの正式認定*2を目指す、GPUを大量導入して国内AI基盤としての地位を確立する、といった大きな動きも既に見えています。

このタイミングで一人目のプロダクト担当としてさくらのクラウド全体に関われるというのはタイミング的に非常においしいと思いますし、またクラウドサービスのプロダクトマネジメントという領域がエンジニアとして積んだ経験を存分に活かせそうだという考えもあります。プロダクトマネージャのチームを作るぞという流れになれば、その立ち上げにも関わるでしょう。いやあ、楽しみだなあ。

もうひとつ重要な点として、非常に良い待遇で迎えていただいたのも関係しますが、投資の増大とそれに繋がるソフトウェアエンジニアの採用拡大を強く志向しているのも大きいですね。これから数多くの良いエンジニアの人達と同僚になれる可能性があります。すばらしい。自分が傾向としてスタートアップが好きなのはこの点がかなり大きいのですが、上場企業でも状況によってはこの利点が得られるんだなと思っています。

プロダクト担当

エンジニアリングのみに集中するよりもビジネスとの関わりも求めたい、ということを考えている自分にとってすばらしいポジションですが、一方でさくらインターネットという会社にどういうプロダクトマネジメントが必要なのかは全く明らかではないので、それを模索するところからの始まりになっています。これはもう、本当にわからないので、あらゆる関係者と話しながらやっていくしかないという状況ですね。

一方でプロダクトマネジメントしかやらないぞというつもりも無いので、さくらのクラウド全体、あるいは広くさくらインターネットグループの中でやれることは何でもやるつもりです。どういうことができるでしょうね。ただし、プログラミングやるという機会は確実に少ないと思います。これはもう、そういうものだろうなと最初から思っています*3。

FAQ

技術顧問は辞めたの?

稼動時間数をかなり縮小していますが、まだ複数社と契約は継続しています。これを受け入れるさぶりこキャリアという枠組みがあります。これからも技術顧問業を拡大することはそう無いと思いますが、一方で技術顧問業をやって得られる知識・経験は本業にも大いに役立つので、完全に辞めることはないかなと思っています。

さくらインターネットって誰がいるの?

専門によって誰に目をつけるかは異なるでしょうが、自分にとっては特に、今回誘っていただいた当人でもある@kazeburoさんがいるのは大きいですね。しかも伊勢さんもいるので、ISUCONを始めたときの当事者4人のうち3人が揃いました。すごいぞ。*4

他にも前々からのコミュニティでの知り合いも多いですね。彼らと一緒に働けるのが超楽しみです。

どこで働くの?

完全リモートなので自宅です。続けて東京都内に住んでおります。

入ってみて何がいちばん印象に残った?

社用メールアドレスのドメインが .ad.jp だったことですかねー。すごいぞ。

まとめ

ということで、今後ともtagomorisをよろしくお願いします。あと新卒・経験者問わず絶賛採用中なので、興味のある方はぜひお願いします。採用窓口への応募(すばらしい!)でも、さくらってどうなの? みたいな連絡を自分個人にいただくのでも歓迎します。みんなでパブリッククラウド作ろうぜ!

www.sakura.ad.jp

*1:5月末にエントリを書きましたが、休暇の消化で書類上の在籍は7月末まででした

*2:現時点では2025年末までに技術要件を満たすことを前提とした条件付き認定

*3:それでいいの? と言われると、一方でプライベートでRubyへの巨大なパッチを書いていたりするので、個人としては何も不満はないという状況です

*4:もう一人の@941さんは最近カケハシさんに転職されましたね

RubyKaigi 2024に行ってきた&しゃべってきた

沖縄、那覇で行われたRubyKaigi 2024に参加してきた。いやあ、もう、最高でしたね。

(Photo by @hsbt -san)

しゃべってきた

"Namespace, What and Why" というタイトルで話してきた。

speakerdeck.com

これはいまRubyに提案している"Namespace on read"というものについての紹介で、本当は実装面とかも細々ひたすら語りたかったんだけど、なにしろこれまでのRubyに全くなかったパラダイムと機能なので、デモやユースケースの紹介、これからどうなるか、みたいな解説を中心にせざるをえなかったかな、というのはある。けど、そうしただけあって、聞いてくれた人達にはかなりインパクトを与えられた、んじゃないかなあ、少なくともKaigi中にいろんな人から感想を聞く限りは……。

実装はまだ完成していなくて、自分のトークが15日(水曜)だったんだけど、直前の金曜にようやくデモ用のスクリプトが動くようになった始末。それでもGCの関係で1/10くらいの確率でSEGVする状態なんだけど、直そうとして動いてるビルドを壊したら悲惨だった*1ので、まあこの確率なら本番でSEGV引かないだろ、と思って強行したら見事にSEGV引いて拍手喝采をもらってしまった。マジウケる。リトライしたときはちゃんと動いたので2連発引かなくてよかった。

Namespaceについて、あるいは今後どうなるみたいな話についていろいろ質問ももらったので、それについてはFAQを作ってみた。疑問などがある方はまずこのFAQをどうぞ。

"Namespace on read" for Ruby, FAQ · GitHub

ところでその後のMatzのキーノート

こんな言葉が最後の最後に出てくる? マジで? って、もうなんかよくわからん感じになって一人で感極まってた。もうだめっていう。頑張るぞ!

Matzキーノート後に感極まって動けないところを激写されるの図

(Photo by @emorima -san)

参加してきた

今回はとにかく色んな人にNamespaceどう思った? って感想を聞いて回ってあれこれ話すのが目標で、他の人のトークも聞いて回ったけどとにかく自分のがな、っていうRubyKaigiだった。これはもうしょうがないじゃん?

しかし@tompng(ぺん)さんのキーノートが本当にもう、Self TRICKってなんだよとしか言いようがないことをやりながら他の人のトークのネタを織り込むとかどういうことだよって感じですね。やってることのバリエーションもすごすぎるし、本当にユニークとしか言いようがない、めちゃくちゃに凄い人だよなあって再確認してましたね。

他のトークについては、自分がCRubyの中身に手を入れるようになったから、やっぱり内部の改善について理解の解像度が高まったなと思うところはある。@tenderloveさんの赤黒木の話なんかも、Object Shapeについての理解が進んでたのでつっかえずに聞けてた気がするし。 あとパーサについては、自分はもう最初から宣言的文法定義最高! 派で、かつちょっと今パーサ軍団に加わる余力はない……って感じだったので本当に気になったところだけつまんだけど、何人ものチームでどんどん前に進めててすごいなあ、いいなあって感じ。Namespaceももうちょっと前に進めて、その状態に至りたいもんだと思う。あとLTのときの金子さんの悪そうな顔が本当に最高だった。あのLTの完成度はすごい。

懇親会などなど、あれこれ書きたいことも多いけど、今回は省略。

このあと

RubyKaigi 2024 事後勉強会というイベントでLTの機会をもらったので、RubyKaigi 2023から2024までの1年間についてざっと振り返る内容を話そうかなと思った。興味のある方はぜひどうぞ。これはもうね、エモいやつです。5月31日。

smarthr.connpass.com

またNamespace on readはRubyアソシエーションの開発助成事業というものの対象プロジェクトに選ばれていたんだけど、その結果報告イベントがあって、そこでプロジェクトの経緯や内容について話します。Namespace on readの設計や実装をどう進めたかなど、RubyKaigiよりもうちょっと詳細に触れられるんじゃないか*2と思っているので、こちらも興味のある方はぜひどうぞ。7月19日。

www.ruby.or.jp

*1:あとRubyKaigi直前は家族旅行にあてていて、スライドもできてなかったから時間もなかった

*2:逆にNamespaceそのものの話はこっち見て、でけっこう済ませちゃうつもり

AWS LambdaのRuby .zipパッケージでgitから取得したgemを使う

AWS LambdaでRubyランタイムを使っててzipアーカイブで関数コードをアップロードしてる人向け。

基本的にはGemfileに依存関係書いてbundle config set --local path 'vendor/bundle'してbundle installすればいい。以下のドキュメントを読もう。

docs.aws.amazon.com

んだけど、Gemfileにrubygems.org以外から取得したgem、特にgitを指定したものを使っている場合にこれだけだとうまくいかないので、原因と対処を書いておく。

そもそもLambdaは何をやってるか

上述ドキュメントを読むとわかるが、zipファイルにはGemfile.lockもGemfileも入れない。それで動作する。これはどういうことかというと、AWS Lambdaのランタイム側ではBundlerを使わず(?)、vendor/bundle 以下にLambdaが想定しているパスで置かれているファイルを直接探索するように$LOAD_PATHが指定されてるんじゃないかと思う。*1

で、通常rubygems.orgから取得したgemについては vendor/bundle/ruby/3.2.0/gems/ 以下にmyclient-1.1.0みたいなディレクトリでコード等が展開される。またvendor/bundle/ruby/3.2.0/specifications/ ディレクトリにgemspecファイルがmyclient-1.1.0.gemspecとして置かれる。試してみていた範囲では、gemspecを検出したらそれに対応するgemのディレクトリを$LOAD_PATHに追加してるんじゃないかと思う。

gitから取得したgemの扱い

gitから取得したgemについてはBundlerはrubygems.orgからのものとは異なり、vendor/bundle/ruby/3.2.0/bundler/gems/以下にmyclient-xxxxxxxのようなディレクトリで展開される。xxxxxの部分はcommit hash。

これはLambdaランタイムが読み込むディレクトリと異なっているため、zipファイルに含まれていても実行時にロードパスが通らず、requireしてもエラーになる。なんということでしょう。

無理矢理パスを通す

しょうがないので、以下のようにしてzipアーカイブを作れば動くようになる。

1. ruby/3.2.0/gems以下にシンボリックリンクを作成する

bundle installしてgitから取得したgemが展開されたら、そのディレクトリに対して以下のようにシンボリックリンクを作る。

$ ln -s vendor/bundle/ruby/3.2.0/bundler/gems/myclient-xxxxxx vendor/bundle/ruby/3.2.0/gems/myclient-1.1.0

こうすれば手元で実行するときにも壊れない。zipアーカイブを作るときは*2シンボリックリンクだった部分はそこにコピーされたようになるはず。つまり、Lambda環境ではruby/3.2.0/gems以下に該当gemのコピーが展開される。

2. specifications以下にgemspecをコピーする

gemspecがないと動かないのでこれもオリジナルの場所からコピーする。コピー時にバージョン番号をつけたファイルにするのに注意。

$ cp vendor/bundle/ruby/3.2.0/bundler/gems/myclient-xxxxx/myclient.gemspec vendor/bundle/ruby/3.2.0/specifications/myclient-1.1.0.gemspec

(余談) pathで指定したgemはどうなる?

Gemfileではgitで指定する他にもpathでローカルパスにあるgemを使うよう指定できる。開発中のgemを直接使うときに便利。

が、この指定だとBundlerは該当パスを直接見に行ってライブラリをロードし、vendor/bundle以下にはコピーを作らない。このため、zipアーカイブにしようとしても入ってくれず、Lambdaランタイム上では使いようがないということになる。

ローカルストレージ上のものをコピーしてzipアーカイブに入れてもいいけど、何が起きるかちょっとわからないのが怖い気もするので、開発中とはいえgitリポジトリ経由で入れるようにしたほうがいいかなと思います。

*1:書きながら思ったけど実行環境にBundlerがロードされているかどうかはちょっとコード書けばするわかるな……やるのがめんどくさい

*2:おそらく大多数の環境では

PathtraqというLifeLogサービスを作った

最近何をやっていたかというと、タイトルの通り、Pathtraqというサービス、iPhoneアプリを作っていた。どんなサービスかと聞かれるとLifeLogというのが一番適切だと思うけど、LifeLogにも種類があって、これは位置情報を記録して検索するサービスになる。

https://pathtraq.tagomor.is/

PathtraqApp

PathtraqApp

  • Satoshi Tagomori
  • Productivity
  • Free
apps.apple.com

どういうためのものかというと、普段生活したりどこかに行ったりして、以下のようなことが気になる方向けです。

  • この場所/店/街、最後に来たのいつだっけ?
  • 前に飲みにいってふらっと入ったあの店、どこにあった何ていう店だっけ?
  • 前にあそこからあっちに移動したとき、どのくらい時間かかったっけ?

なんかさあ、この程度のこと、全部記録とってあれば簡単にわかるはずなんだけど、いまいちそうなってない*1。いいかげん解決されてくれよ、2023年だぞ! という気分で作った。

このアプリは基本的にバックグラウンドで動作して、自分がどこにいたのかを、24時間・365日記録する。もちろん記録するだけじゃ役に立たないので、時間を指定していつからいつまでの間にどこからどこまでどのように移動したかを表示できるし、場所(エリア)を指定して、その範囲のログはいつ記録されたのか、つまりある場所に行ったのはいつだったかを検索できる。あれこれチューニング頑張ったので、同様の機能*2に較べて高精度のログが、バッテリー負荷低くとれているはず。

https://pathtraq.tagomor.is/app_view_track.png https://pathtraq.tagomor.is/app_area_search.png

あとはお決まりのチェックイン。人間は行った場所にチェックインしたい。自分もしたい。あと写真もつけたかったのでつけられるようにした。あまり大量につけられると良からぬことに使われるから、いちおういまは4枚までだけど。

https://pathtraq.tagomor.is/app_view_checkin.png

OGP画像生成なんかも頑張ったのでFacebookやXやThreadsにリンクすると格好よく展開される! はず!*3

データはサーバサイドに送って保存してます。写真やチェックイン情報も。いつどこにいたかの情報は、もちろんユーザのもので、必要なときはアーカイブダウンロードできます。やったね。

ということで、興味がある人は使ってみてください! 無料! どうぞ!

データとかどうなるの

いちおうそれなりの知識と経験のあるプログラマだという自負はあって、その自覚と責任のもとで、必要な安全性を担保できる程度にはセキュリティのことを考えて作っています。

またどのBig Techとも関係ない完全に独立したアプリであって、かつ広告なども現状入れておらず、データを個人情報を結び付けることは絶対にしない、というポリシーのもとで作っています。また、データを何らかの形で使用するサービスを作るときには絶対にopt-inとする、というポリシーをもっています。詳しくはFAQをどうぞ。

なぜ作ったの

自分は遠くも近くもあちこち行くのが好きなんだけど、どこに行ったか・いつ行ったか、について、過去のことを正確に思い出せないこともよくある。友人たちと飲みにいったあと、2軒めにふらっと入った店がすごくよかったんだけどしこたま酔っ払ったせいでどこの何という店だったかが正確にはわからない、とか。近所の庭園に散歩に行くのに、前に紅葉がすごくよかったのはいつ頃だったっけ? とか。ひさしぶりの街を歩いてるんだけど色々変わってて、あれー、前に来たのいつだっけ? とか。

もちろん個人でGPSログを記録するだけなら色々方法はあるんだけど、デバイスを持ち歩くには充電に気をつかわないといけなかったり、単にiPhone内に記録するだけだとマシな検索機能を作るのが大変だったり機種変更のときに気を遣うことになったり、あれこれ大変。なので、いつも使ってるスマホ(iPhone)を持ち歩いていれば解決されるようにしたい、というのが動機。

あとはチェックインかな。人間はチェックインしたい。Swarm(旧Foursquare)とかを使ってる人もいるけど、個人的にはゲーミフィケーションがいらないというか、そういうものを見せてほしくないんだよね。チェックインするときは単にチェックインしたい。あと写真を添付したい。Facebookのチェックインが条件にぴったりではあるので使ってたんだけど、Facebookに囲い込まれたまんまなのは嫌だなとちょっと思っていたので、機能をつけた。んでSNS等でシェアしたとき格好がつくようにOGP画像をがんばってみた。

どう作ったの

この記事を読む人は気になっているかも。どうかな。

iPhoneアプリの方は完全にSwiftUIで作った。iOS開発からもだいぶ(12年!)離れてたので、プロトタイプ版(UIKit)のスクラッチからの書きおこしだけ友人に(有償で)お願いして、その上に基本機能を作っていき、目途がついた時点でSwiftUIでのプロダクション版を自分で書きおこした。かなり時間をかけたけど、自分にとって不自然な使い勝手の箇所はだいぶなくなった。

サーバサイドはRuby + AWS Lambdaの完全サーバレス。メンテ作業なんかも全部Lambda経由でやるようにしているので常時稼動してるEC2もコンテナ(ECS/Fargate/EKS)もゼロ。ひとりで作るのにサーバの面倒とか見てらんないし。とはいえ完全Lambdaだとパフォーマンスとか管理とか料金とかが多少心配なところも無くはないので、これはユーザが増えてきたらそのうち考えなおすかもしれない。

Webはあまりないけど、React(create-react-app)でSPA。ただチェックインのWebページにいきなり飛ばれたときにOGP画像出したいとかはあって、そのあたりはちょっと苦労して実装した。だからだいたいはS3ホスティングで済んでるけど、サーバサイドのエンドポイントも多少ある。

これからどうするの

自分で使いたいサービスなので、何があっても当面は運用するつもり。ユーザが(かなり?)増えたら運用や開発体制を何か考える。増えなければ、低コストで運用し続けられるように、特にサーバサイドをシュリンクするとかはあるかもしれない。しばらくは細かい改善と機能追加を続けるつもり。

Androidアプリは直近の計画にはないが、そのうち作れたらいいなー、くらいで考えている。自分でやることにこだわってるわけでもないので、誰かに請負でお願いしてもいいなあ。もし興味のあるAndroidアプリエンジニアの人がいたらご連絡ください。

あとWebページなんかは完全に素人の仕事になっているので、誰かプロのデザイナーさんにいつか頼みたいなーと考えてもいる。これも誰かご興味あればご連絡ください。

まとめ

ゼロ年代の個人ウェブサービス全盛だった頃に自分では何も作らなかったのが、今回真面目に全部自分で作ったので、ちょっとスッキリしたね。フルスタックエンジニアだ!

*1:GoogleMapsのタイムラインは? っていう人がいると思うけど、場所(エリア)から「いつ」を検索することはできない

*2:たとえばGoogle Mapsのタイムライン

*3:はてなブログのOGP画像展開は大きいの使ってくれなくてしょんぼりだね……。

iOS17/iPhone15でPhotosPickerItem#loadTransferable(type: Data.self)が動かなくなっていた

まーいけるだろ、と自作アプリが動いていたiPhone14 Pro w/ iOS16をiPhone15 Pro w/ iOS17に移行したところ、ビルドしなおしてインストールしたら普通に動いてたっぽいから油断してたら、タイトルの通りPhotosPickerを使って写真データを取り出してるところが動かなくなってた。 具体的にはこのエントリで書いてたコード。

tagomoris.hatenablog.com

これの、以下のようなコード。

fileprivate extension PhotosPickerItem {
    func pictureData() async -> Data? {
        var transferable: Data? = nil
        do {
            transferable = try await self.loadTransferable(type: Data.self)
        }
        catch {
            // log the error
            return nil
        }

これを実行するとcatch節で以下のようなエラーが報告された。iOS16 / iPhone14では起きなかったもの*1。

CoreTransferable.TransferableSupportError error 0

で、これ、iPhone15Proのシミュレーターでも起きないんだよね。今のところ実機でしか再現できてない。なんなんでしょう。PhotosPickerの使いかたみたいなのをググると同じようなコードで直接Dataに変換している例が大量に見付かるんだけど、みなさんどうなってるんですかね?

解法?: Transferableな変換先を自分で実装する

よく(Appleのドキュメントでも)見るloadTransferableの変換先といえばImageなんだけど、これだとデータ本体を取り出すことができないので却下。困ったんだけど、自分でTransferableな変換先を実装するしかないかな、ということになった。Appleの以下のドキュメントにある通り。

developer.apple.com

自分の場合は対応フォーマットとしてJPEG/PNGのデータが取り出せればよかったので、それぞれJPEGの場合とPNGの場合用のTransferableなstructを作って変換を試みたところ、これでiPhone15の実機でも正常に動くようになった。コードは下に載せておく。

直接Dataを取り出すことがなぜ失敗するのかがマジで意味不明だが*2、まあ解決した上にコードも綺麗になったような気がするので良しとする。しっかし何故なんだろう、他の人のは動いてるのかな……。

import PhotosUI

fileprivate enum TransferError: Error {
    case importJpeg
    case importPng
    case emptyJpeg
    case emptyPng
}

struct PickedJpegImage: Transferable {
    let data: Data

    static var transferRepresentation: some TransferRepresentation {
        DataRepresentation(importedContentType: .image) { data in
            guard let uiImage = UIImage(data: data) else {
                throw TransferError.importJpeg
            }
            guard let data = uiImage.jpegData(compressionQuality: 1.0) else {
                throw TransferError.emptyPng
            }
            return PickedJpegImage(data: data)
        }
    }
}

struct PickedPngImage: Transferable {
    let data: Data

    static var transferRepresentation: some TransferRepresentation {
        DataRepresentation(importedContentType: .image) { data in
            guard let uiImage = UIImage(data: data) else {
                throw TransferError.importPng
            }
            guard let data = uiImage.pngData() else {
                throw TransferError.emptyPng
            }
            return PickedPngImage(data: data)
        }
    }
}


fileprivate extension PhotosPickerItem {
    func pictureData() async -> Data? {
        do {
            if self.supportedContentTypes.contains(UTType.jpeg) {
                guard let p = try await self.loadTransferable(type: PickedJpegImage.self) else {
                    // error log
                    return nil
                }
                return p.data
            } else if self.supportedContentTypes.contains(UTType.png) {
                guard let p = try await self.loadTransferable(type: PickedPngImage.self) else {
                    // error log
                    return nil
                }
                return p.data
            } else {
                // unknown type: error log
                return nil
            }
        } catch {
            // error log
        }
        return nil
    }
}

*1:iPhone14でちゃんとiOS17に上げておくべきだった……。いやちょっと他の優先順位が、ゴニョゴニョ

*2:だって独自TransferableだってDataを経由して変換してるじゃん

源泉徴収票シリアストーク: 情報の不均衡とうまくつきあう

TL;DR

  • 自分の給与額が業界内で高いのか低いのか、知るのは難しい
  • 似たような条件の人どうしでうまく匿名化して共有しあえばいいのでは?
  • という欲求を満たす源泉徴収票シリアストークという試みを紹介する
    • 実際の実行にはレギュレーションが重要です

給与に関する情報の不均衡

給与・所得というやつがあります。生きるのに必要なのはもちろん、たとえば同じ会社や同じ職種・同じ業界の中でどのくらい高い給与・所得を得ているかがある種のバロメーターになって、高い人がエラい、みたいなトンチンカンなことを言い出す人も出てきたりする。もちろん給与が高いからエラいなんてことは絶対にないんだけど、それはそれとして給与は高いほうが嬉しい。

だがしかし、給与額というやつは個人の能力だけではもちろん決まらなくて、儲かっている会社にいるかどうか、所属している会社がどのくらいの給与を払うポリシーなのか、会社内でどのように評価されているか、などに大きく依存する。このため、よっぽど良い知人同士であっても直接に給与額どうしを比較するのはめちゃくちゃな危険を伴い、後々まで有害な影響を残す*1ことが知られている。

とはいえ、そこに情報の不均衡ができる。給与を出す側の人達は、色々な人を見比べて、誰にどの程度の給与を出しているかを知っている。また外部の人材エージェントやヘッドハンターなども知っていることが多い。しかし雇われる側はそう多くの情報を持っておらず、そもそも自分の給与は満足すべきものなのか、それとももっと多い額を望めるものなのか、正確に知ることはまず不可能と言ってもいいだろう。

求める情報を整理して限定する

さて情報の不均衡はあるが、そこで情報を求めるにしても、誰かれ構わず給与額を聞いたって仕方がない。自分と較べるんだから、自分と同じくらいの評価を受けているのではないか、という人の情報を聞いてこそ意味がある。職種・職位でともに似たようなポジションで、転職の際に、まさにその人の替わりとなって入る、くらいだと理想的だ。あるいは、単に自分と似たような技術的知識と経験をもっている、と思われるくらいでも構わないだろう。

また、雑に「年収」と言ったときに人が想像するものが意外に異なっていることもある。多くは税引き前の給与総額を指すだろうが、実際の暮らしぶりは税引き後の金額を見ないとわからない、とする人もいるだろう。しかし税金は家族構成や居住地などに影響を受ける一方、出す側が考慮することは少ない*2だろう。福利厚生などまで考えだすとどんどん話が膨れてしまい、とても公平には考慮できない。

自分はソフトウェアエンジニアのためその事情も考えると、特にシニアになってくると給与以外の収入があることもある。技術書や雑誌原稿の執筆による印税および原稿料、副業としての勤務や顧問業などをもっている人もいる。これらも「その人の収入」としては含まれるだろう。

しかし、考えてみれば、いま求めているのは「自分の給与額を評価するための指標」であって、これは他人が原稿料を稼いでいようが副業をやっていようが、何ひとつ関係がない。福利厚生だって転職先を決めるには必要な情報だろうが、今の給与額の評価には関係がない。

求める情報は源泉徴収票にある

必要な情報とは、つまり、源泉徴収票の「支払い金額」*3である。

自分と同じようなポジションの、あるいは自分と同じような技術・知識を持った人の源泉徴収票において、「支払い金額」にどういう金額が書かれているかが知りたい。個人名として「誰の」は実際には重要ではなく、自分と比較できる人だと分かっていればそれでいい。

源泉徴収票シリアストーク

ここまで書かれているようなことを自分が考えたのは、もう8年以上も前のことだ。そのとき話が合った人達とやったのが「源泉徴収票シリアストーク」である*4。ある夜にこっそりと居酒屋の一室にあつまり、強固なレギュレーションのもと、それぞれの年収を安全に共有した*5。このくらいの人達はだいたいどのくらいの給与を得ているのか、自分はその中で高低どこに位置するのか。

結論としては、実に有意義な試みだった。ものすごく生々しく確実な形で、自分の給与額をどう評価すればよいかの情報を得た。参加者の一人からは、後に、あの情報がその後のキャリア形成に実に役立ったということも聞いた。ということで、他の人も条件が整えられるのであればやるといいのではないかと思う。

このエントリは以後、源泉徴収票シリアストークをどのように開催すればよいかを紹介する。ざっくり言うと、数名で行われる飲み会として実行する。

人選が何よりも重要

とにかく、給与額という比較的センシティブな情報を、匿名化した状況とはいえ、共有してよいという人だけを集める。よい友人関係があることが前提になるだろう。また個人名と給与額の結び付きを推測できる条件はできるだけ無くしたいため、同じ会社の同僚などは避けたほうがよいだろう。

自分と比較できる人たちである、ということも重要な条件である。明らかに自分よりもシニアな人、明らかにジュニアな人と給与額を比較する意味はまったくない。「同じくらいもらっていて不思議ではない人」、業界的あるいは知識・経験的に自分と同格程度であろう、という人たちを集めて行うべきである*6。

あまり少ない人数だと、誰がいくらもらったかの推測が容易になってしまうため、ある程度の人数がいたほうがいい。おそらく6人以上が望ましいだろう。一方、あまり多い人数になると同格で信用できる人を集めるのも難しいし、全員が同じものを同時に見るのも厳しくなる*7。6〜8人程度が最適なのではないだろうか。

レギュレーションを決める

ここに当時使用したレギュレーション実物がある。これはすべて非常に重要なので、表現を変えつつ理由なども含めて解説する。

レギュレーションの主目的は、どの金額が誰のものなのかを分からなくすることにある。情報は安全に共有できなくては意味がなく、どの金額が誰のものかを推測できそうな手がかりは可能な限り潰しておこう。また金額を見る瞬間を全員で同じにすること、見たときのリアクションを全員で統一することも重要で、これによっていつ出た金額が誰のものかを推測する手がかりも潰しておく。

準備

準備は非常に重要である。守れなさそうな人は参加者に入れてはいけない。

  • 源泉徴収票 20xxå¹´ のものから「支払い金額」のみを転記する
    • 「円」などの表記を入れず、数字のみを書くこと
    • ひと目でわかるよう、3桁ごとにカンマを入れること (例: 8,000,000)
  • 白い紙にプリンタで、黒で印字すること
    • 全員がひと目で見られるよう、数字をできるだけ大きくなるよう印刷すること
    • 数字以外の情報は一切書かないこと

共有すべき情報をシンプルかつ確実に定義する。また「ひと目でわかるよう」は重要で、これで全員が同時に金額を理解できるようにする*8。筆跡などの情報を残さないようプリンタでの印刷は当然だ。

  • 給与所得のもののみを記載すること
    • 原稿料、印税など、主たる給与所得以外のものは含まない
    • 20xx年途中での転職などがある場合は合計金額を記載すること
    • ただし転職前後で著しく給与が異なる場合や不労期間が含まれる場合などは20xx年末の給与所得基準で善意のもと12ヶ月分の給与になるよう補正をかけてよい

これも共有される情報の定義である。単に守ればよい。

  • 白、長形4号の二重封筒に入れること
    • ローソンで販売されているものが望ましい
    • 紙を入れたら密封すること
    • 封筒の外側には一切何も記載しないこと
    • 封筒を折り曲げたりしないこと

これは当日、全員分を出したときに区別がつかないようにするため*9。全員が白い封筒なのに一人だけ茶封筒だったりしたら台無しである。こういう細部をきちんと守れてこそ、安全に重要な情報が共有できると言える。

当日の行動

ここからは当日の行動。実行したときは、できるだけ参加者の知人がいなさそうな街の、できるだけ奥まった場所にある、確実にドアつき個室がとれる店を選んで予約した*10。今にして思えば、ドアつき個室でさえあれば店の外で誰と遭遇しても大して問題にはならなかったんじゃないかという気もするが、しかし秘密の会合めいたイベントを開催するにあたっての雰囲気づくりとしては実に良かった。楽しかった。

さて、当日の行動においても、情報を安全に共有するために守るべき点はいくつもある。

  • 飲み会終了15分前になったら行動を開始する
  • 全員の封筒を取り出し、全員でシャッフルする

これらの事項は単純だが、重要でもある。状況の共有は飲み会の終了直前がよいと思う。見た内容についてお互いに話し合うようなことは何もないし、一人で考える時間をとる方が健全だろう。封筒を全員でシャッフルするのは、もちろん匿名性のためだ。

  • 全員の目の届くところでひとつずつ開封する
    • このとき、参加者は数字を見たら必ず「ふーむ」と言うこと
    • それ以外は口にしないこと

開封時のリアクションを、これらの項目で強制している。強制的に「ふーむ」と言わせることで自分の金額が出てきたときもリアクションのブレをおさえられる*11。これも安全な情報の共有のためには非常に重要。

  • 封筒全部を開封したらハサミで細断する
    • 店を出たら全員分をコンビニのゴミ箱などで確実に投棄する
    • 以降、当日見たことについては口外しないこと

当然である。

給与額がすべてではない、しかしお金は大事

このようにして、安全に、自分の給与額を評価するための指標を手に入れることができる。いい仲間を集められる人は試してみてもよいだろう。

もちろん、給与額がすべてではない。給与額より重要なことはいくらでもある。しかし同時に、被雇用者として働く以上は、あるいはいい人生を送るためにはある程度はお金について考えることは欠かせない。そのための情報を得るひとつの手段として、こういうのもあるよ、という話でした。

*1:人によります。

*2:つまり雇用主からの評価は税引き前の金額に反映されているはず

*3:給与として勤務先から支払われた、一年分の税引き前の総額

*4:当時、ソフトウェア関連の勉強会で 〜 Casual Talk という名前をつけることが多く、それが名前の由来になっている

*5:この安全性のために重要なのがレギュレーションである

*6:自分の場合はソフトウェアエンジニアのコミュニティ内での友人たちのうち、これはという人達で実行した

*7:これは当日の行動として重要な条件である。後述。

*8:例えば数字が小さかったり読みにくかったりすると、よく見ようとする人と見なくても分かってしまった本人の間でリアクションに差が生まれる可能性がある

*9:なぜローソンのものが望ましいという条件をつけたかはもう覚えていない。なんだっけ……たまたま近くにあったのかな。どのコンビニ等でもよいと思うが、どこかのもので統一できれば理想的だろう。

*10:ある冬の日の実行で、あのブリしゃぶは非常においしかった

*11:どうでもいいが、このとき参加者がみんなで「ふーむ」ってtweetしまくってたら、参加者以外がなにやら不穏な空気を感じとったり真似たりしていてちょっと面白かった