ぽよメモ

レガシーシステム考古学専攻

actions/cache@v4ではヒットしなかったときcache-hit=='false'にならない

cache-hitとは

GitHub Actionsのキャッシュ用actionであるactions/cacheは、指定したキーに完全一致するキャッシュがヒットしたかどうかのパラメータをそのstepのoutputとして保持している。 つまり以下の様にすることで、キャッシュがヒットしたかどうかを判定し、何かアクションするということが可能である。

jobs:
  run:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/cache@v4
        id: cache
        with:
          path: ./cache-dir
          key: your-cache-key
      - if: steps.cache.outputs.cache-hit != 'true'
        run: |
          # something to do

また、actions/cache(およびactions/cache/restore)のREADMEには以下の様に記載されている。

https://github.com/actions/cache?tab=readme-ov-file#outputs
https://github.com/actions/cache/blob/main/restore/README.md

Note cache-hit will only be set to true when a cache hit occurs for the exact key match. For a partial key match via restore-keys or a cache miss, it will be set to false.

restore-keysで前方一致するキャッシュにヒットしたときや、何もヒットしなかったときにはfalseがセットされると書かれている。 実際にv3まではこのような挙動であった。

v4における挙動変更(?)

自分は以前紹介したactions/cache/saveとactions/cache/restore*1を使って、デフォルトブランチでのみキャッシュをsaveし、トピックブランチではrestoreのみを行うというワークフローを組んでいた。具体的には以下の様に記載していた。

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

jobs:
  build:
    on: ubuntu-22.04
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-go@v4
        with:
          go-version-file: go.mod
          # 自分でキャッシュをハンドルするので無効にしておく
          cache: false
      - uses: actions/cache/restore@v3
        id: restore-go-cache
        with:
          path: |
            ~/go/mod
            ~/.cache/go-build
          key: go-cache-${{ runner.os }}-${{ runner.arch }}-${{ github.sha }}
          restore-keys: |
            go-cache-${{ runner.os }}-${{ runner.arch }}-
            go-cache-${{ runner.os }}-
      - run: |
           # something to do
      - uses: actions/cache/save@v3
        # mainブランチ上での実行時かつ、キャッシュがヒットしなかった場合のみ
        if: github.ref_name == 'main' && steps.restore-go-cache.outputs.cache-hit == 'false'
        with:
          path: |
            ~/go/mod
            ~/.cache/go-build
          key: ${{ steps.restore-go-cache.cache-primary-key }}

実際これは上手く動作しており、v3の間は特に問題なかった。actions/cacheをv4に変更しても、特にエラーなどは生じていなかった。ワークフローが走りきったので、自分はそこでOKと判断ししばらく忘れていた。

しかしあるときから、なぜかCI中のビルド時間が伸びていることに気付いた。よく見ると、キャッシュがヒットしていない。mainブランチのワークフローが失敗せず走りきっているのに、キャッシュがヒットしないとはどういうことだ???

cache-hitがfalseを返さない

actionsの実験用に持っているリポジトリでこれを検証することにした。簡単のためgoは使わず以下の様なワークフローを組んで cachemiss ブランチとしてpushしてみた。

name: cache

on: [push]

jobs:
  save:
    runs-on: ubuntu-22.04
    steps:
      - uses: actions/checkout@v4
      - uses: actions/cache/restore@v4
        id: restore
        with:
          path: .cache
          key: test-${{ runner.os }}-${{ runner.arch }}-${{ github.sha }}
          restore-keys: |
            test-${{ runner.os }}-${{ runner.arch }}-
      - run: |
          echo "steps.restore.outputs.cache-hit=${{ steps.restore.outputs.cache-hit }}"
          echo "github.ref_name=${{ github.ref_name }}"
          mkdir -p .cache
          echo ${{ github.sha }} > .cache/sha
      - uses: actions/cache/save@v4
        if: github.ref_name == 'cachemiss' && steps.restore.outputs.cache-hit == 'false'
        with:
          path: .cache
          key: test-${{ runner.os }}-${{ runner.arch }}-${{ github.sha }}

すると、どうやらv4になって以降、primary keyにヒットしなかったとき cache-hit というoutputが空文字を返すようになってしまっていることが分かった。

cache-hitが空文字になりcache/saveがスキップされる様子

これによりmainブランチで実行すると github.ref_name == 'main' && steps.restore.outputs.cache-hit == 'false' という条件では

  • github.ref_name == 'main''master' == 'master' → true
  • steps.restore.outputs.cache-hit == 'false''' == 'false' → false

となり、このステップが単にスキップされてしまったようだ。

ワークアラウンド

ワークアラウンドというか……実は actions/cache のREADMEやGitHub本体のドキュメントを読んでいると outputs.cache-hit == 'false' はどうやら使われていないようだ。

自分が以前調べた時は == 'false' だった気がするのに……と思ったところ、どうもドキュメントの方が修正されたらしい。

github.com

いやだったら false を返すって記述は消した方が良かったし、v4のbreaking changeに書いておいて欲しかったよ……?? 何も書いてないじゃん……

Release v4.0.0 · actions/cache · GitHub

というわけで、なんかこう納得いかないですが皆さん cache-hit != 'true' を使いましょう。

気になった方は是非↓のissueにupvoteしてもらえると、もしかしたら(ドキュメントの方か実装の方かわからないけど)修正されるかもしれないのでよろしくお願いします。

github.com


coreutilsのlnでのシンボリックリンクの更新はアトミックになっている気がする

シンボリックリンクの更新におけるatomicity

シンボリックリンクをアトミックに更新するには、単にln -snf ではダメだという記事を昔読んでずっとそうなんだと思っていた。

qiita.com

この記事ではstraceでlnの挙動を追いかけ、シンボリックリンクの更新には unlink してから symlink しているのでアトミックではないと書かれていた。

StackOverflowなどでもこのような回答は多く見られた。

unix - Can you change what a symlink points to after it is created? - Stack Overflow

linux - How does one atomically change a symlink to a directory in busybox? - Unix & Linux Stack Exchange

web deployment - atomic way of deploying website updates? - Stack Overflow

やってみた

Ubuntu 22.04 LTS

$ uname -r
5.15.0-91-generic
$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 22.04.3 LTS
Release:    22.04
Codename:   jammy

ふとしたときにlnのstraceを取る機会があり、見ていたところ思っていたのと違う挙動になっていた。 適当なディレクトリを作成し、その中で以下の様なコマンドを実行してみた。

echo "hello old" > hello.txt
strace ln -sf hello.txt hello.sym

straceの結果を見ると、このときは単に symlinkat が呼ばれている。

... 省略 ...
symlinkat("hello.txt", AT_FDCWD, "hello.sym") = 0
... 省略 ...

次に、新しいファイルでこれを上書きしてみる。

echo "hello new" > hello.new
strace ln -sf hello.new hello.sym

hello.sym には既に既存のリンクがあるため、symlinkat をしようとして EEXISTS で失敗し、最終的に乱数名のsymlinkを作った後renameしているようだ。

... 省略 ...
symlinkat("hello.new", AT_FDCWD, "hello.sym") = -1 EEXIST (File exists)
openat(AT_FDCWD, "hello.sym", O_RDONLY|O_PATH|O_DIRECTORY) = -1 ENOTDIR (Not a directory)
newfstatat(AT_FDCWD, "hello.sym", {st_mode=S_IFLNK|0777, st_size=9, ...}, AT_SYMLINK_NOFOLLOW) = 0
newfstatat(AT_FDCWD, "hello.new", {st_mode=S_IFREG|0664, st_size=10, ...}, 0) = 0
openat(AT_FDCWD, "/dev/urandom", O_RDONLY) = 3
read(3, "\37f\22\377\241\351", 6)       = 6
close(3)                                = 0
getpid()                                = 4322
getppid()                               = 4319
getuid()                                = 1000
getgid()                                = 1000
symlinkat("hello.new", AT_FDCWD, "CurXXqPD") = 0
renameat(AT_FDCWD, "CurXXqPD", AT_FDCWD, "hello.sym") = 0
... 省略 ...

この挙動は、適当な名前のsymlinkを作ってからmvする方法と全く同じ事をやっているように見える。 実際に先のページに書いてあるようなテストコードを実行してみてもエラーになることは無かった。

$ cat << EOF > ln.sh
#!/bin/sh
echo test1 > src1
echo test2 > src2
while :
do
    ln -snf src1 dst
    ln -snf src2 dst
done
EOF
$ chmod +x ./ln.sh
$ cat << EOF > cat.sh
#!/bin/sh
while :
do
    cat dst
done
EOF
$ chmod +x ./cat.sh
$ ./ln.sh

↓別のシェルで実行

$ ./cat.sh

macOS 14

macOSには strace がなく、代わりに dtruss というコマンドが使えるらしい*1。なお、 ln コマンドのシステムコールをトレースするためにはSIPによる保護を無効化しなければならなかった。

SIPを無効化せず、codesign コマンドで署名を削除することで dtruss が使えるようになるというハックを見つけた*2が、少なくとも自分の環境(M1 Pro macOS 14.1)ではエラーになった。

$ cp /bin/ln ./
$ sudo codesign --remove-signature ./ln
$ sudo dtruss -deflo ./ln -sf hello.txt hello.sym
dtrace: system integrity protection is on, some features will not be available

dtrace: failed to execute ./ln: Could not create symbolicator for task

一時的にSIPを無効にしてやってみた結果:

$ echo "hello old" > hello.old
$ sudo dtruss -deflo ln -sf hello.old hello.sym
... 省略 ...
 1883/0x385d:      1317       3      2 lstat64("hello.sym\0", 0x16D91A370, 0x0)      = -1 Err#2
 1883/0x385d:      1318       0      0 stat64("hello.sym\0", 0x16D91A370, 0x0)       = -1 Err#2
 1883/0x385d:      1319       1      0 lstat64("hello.sym\0", 0x16D91A370, 0x0)      = -1 Err#2
 1883/0x385d:      1460     140    140 symlink("hello.old\0", "hello.sym\0")         = 0 0
$ echo "hello new" > hello.new
$ sudo dtruss -deflo ln -sf hello.new hello.sym
... 省略 ...
 2177/0x41df:      1200      10      9 lstat64("hello.sym\0", 0x16D686370, 0x0)      = 0 0
 2177/0x41df:      1206       5      5 stat64("hello.sym\0", 0x16D686370, 0x0)       = 0 0
 2177/0x41df:      1208       2      1 lstat64("hello.sym\0", 0x16D686370, 0x0)      = 0 0
 2177/0x41df:      1276      68     68 unlink("hello.sym\0", 0x0, 0x0)       = 0 0
 2177/0x41df:      1305      63     28 symlink("hello.new\0", "hello.sym\0")         = 0 0

よってmacOSの組み込みlnコマンドではアトミックになっていない。実際にあの単純なテストコマンドを実行すると以下の様にエラーになるケースが確認できた。

$ ./cat.sh
... 省略 ...
test1
test2
test1
test1
cat: dst: No such file or directory
test2
test1
test2
cat: dst: No such file or directory
cat: dst: No such file or directory
^C

macOSではHomebrew等を使ってGNU coreutilsをインストールして使うことができる。

$ brew install coreutils

なおこのとき ln ではなく gln のように g というプレフィクスを付けて呼ぶ*3
こちらは当然symlinkatした後にrenameatしており問題は発生しない。

$ sudo dtruss -deflo gln -sf hello.new hello.sym
... 省略 ...
21550/0x11e09:      1837      15     15 symlinkat("hello.txt\0", 0xFFFFFFFFFFFFFFFE, "hello.sym\0")      = -1 Err#17
21550/0x11e09:      1844       7      6 openat(0xFFFFFFFFFFFFFFFE, "hello.sym\0", 0x40100000, 0x0)       = -1 Err#20
21550/0x11e09:      1849       3      3 fstatat64(0xFFFFFFFFFFFFFFFE, 0x16DD8BA6A, 0x16DD8B3F8)      = 0 0
21550/0x11e09:      1851       1      1 stat64("hello.txt\0", 0x16DD8B488, 0x0)      = 0 0
21550/0x11e09:      1862      10     10 open("/dev/urandom\0", 0x1000004, 0x0)       = 3 0
21550/0x11e09:      1863       1      1 read(0x3, "?\275L\205Gm\321\225@\004\0", 0x8)        = 8 0
21550/0x11e09:      1953      90     89 symlinkat("hello.txt\0", 0xFFFFFFFFFFFFFFFE, "CuXBf0rC\0")       = 0 0
21550/0x11e09:      2032      79     78 renameat(0xFFFFFFFFFFFFFFFE, "CuXBf0rC\0", 0xFFFFFFFFFFFFFFFE, "hello.sym\0")        = 0 0
... 省略 ...

いつ変わった?

2017年のこのあたりのコミットで挙動が変わったのだろうか……?

github.com

このコミットはv8.27から含まれていそう。

github.com

If the file B already exists, commands like 'ln -f A B' and 'cp -fl A B' no longer remove B before creating the new link. That is, there is no longer a brief moment when B does not exist.

このあたりが該当する……?少なくともここ最近のcoreutilsが入っている環境なら問題無さそうだ。

まとめ

何か落とし穴あったら教えてください。


*1:yohei-a.hatenablog.jp

*2:www.deepanseeralan.com

*3:プレフィクス無しで呼びたい場合は $(brew --prefix coreutils)/libexec/gnubin へパスを通す。

ソフトウェアエンジニア4年目

はじめに

これはあくあたん工房 Advent Calendar 2023 10日目の記事です。
あくあたん工房という団体を設立してから6回目のAdvent Calendarらしいです。なんだかんだ長く続いていて正直ただただ驚いています。後輩たちの努力が凄い。

もうすぐ社会人5年目というところで、自分の仕事とキャリアに関する所感を書いておこうかなと思っています。それなりにちゃんと仕事をし、それなりに大きなタスクを任せられるようになり、社内でのプレゼンスも徐々に上がってきたのでやっていることの方向性は大きく間違っていないんじゃないかなと思っています。 ソフトウェアエンジニアとしてキャリアを歩んでいくということはおそらく多くの学生にとって未知であり、何かの参考になると嬉しいです。

TL; DR

  • 知り合いは多い方が良いよ
  • 意見や質問は積極的にしようね
  • チームを大事にしようね

お仕事

探せば普通に所属とかわかるけどなんとなく濁しています。

  • Web系企業(新卒入社)
  • SREとかインフラエンジニアとかそういう感じ
    • めちゃくちゃコードは書いている

クラウドを一切触らないわけではないですが主にオンプレが主戦場です。
バックエンド・ミドルウェアあたりから下、物理より上くらいの基本的にお客様から直接的には見えないレイヤーを取り扱っています。

この職自体は1年目から続けていて、2年目直前あたりから比較的大きめのプロジェクトに関わり始めました。いわゆるインフラ基盤の刷新というやつです。 日々レガシーなコード、知らない仕様、どんどんやってくる障害と戦っています。

今のポジション

おおよそ中の上くらいのミドルエンジニアと言って良いでしょう*1。そろそろシニア名乗って良いでしょと思っているので、そういう感じでマネージャに掛け合っているところです。

チームはまだ小さく、スタッフレベル*2のエンジニア(以降チームリーダー)の下で良く言えばナンバー2として働いています。元々はほぼ2人体制でしたが、ありがたいことにここ2年で倍以上になりチーム感が出てきたところです。

部署はもう少し大きく数十人規模で、それらのメンバーがまとまって現在のサービスの運用を担っています。

お仕事内容もう少し詳しく

  • 既存のサービスおよび基盤の運用・メンテ
    • 深夜対応などもする。複数チームによる週単位のローテーション制。
  • 既存のサービスの基盤移行に伴う検討・設計・実装・運用・メンテ
    • 対象は全てではない。複数のチームが同時に動いていて、色々なサービスの移行が並行して行われている。

理想的なDevOpsにはまだ遠く、自分が書いたわけではないアプリケーションの運用をしていて、問題が起きたから見てくれとエスカされるみたいなこともメインの業務の一つです。逆にこういうことが起きているがわからん助けてくれと開発しているチームにエスカするなどもしています。
現在の基盤もまだまだ使っているため、その改善なども行います。デプロイを早くしたり、パフォーマンス問題を解決したり、レジリエンスを向上させたりなどです。

既存のサービスの基盤移行にはお客様から直接見えるアプリケーションの開発チーム始め様々なチームが関与します。それらのチームが顔を突き合わせて議論する場に出席し、主にインフラ側からの意見・SREとしての意見を出しています。ここで方針を決め、各チームが実装し、実際に移行のオペレーションを行います。
移行は単純に載せ替えればよいという物ではなく、歴史的経緯により複雑に絡み合った毛玉からセーターを作るようなもので、日々どうにか着られるセーターになるようにみんなで糸を引っ張ったり切ったり結んだりしています。

その過程で流用が難しいものをフルスクラッチで書き直したり、既存のコードに手を入れたりします。ここで主にコードを書いていて、チームメンバー(リーダー含む)にコードをレビューされたり逆にレビューしたりしています。

だいぶぼかして書きましたが、要するに非常に不確実性の高いタスクとずっと向き合っています。正解はなく、先は長く、しかし長引かせ続けて苦しくなるのは自分なので手短に行わなければいけません。

今後のキャリアに関する所感

何も分からん。

何も分からんけど、とりあえずしばらくは今の延長戦で十分戦えるなという感じです。問題はそこから頭一つ抜けるために何が必要かです。 あまりにも何も分からんので、今年は何かの参考になれば良いなと思ってスタッフエンジニア本を読んだりしました*3

bookplus.nikkei.com

各社のエンジニアリング組織の文化、その中のポジション、必要とされる仕事には様々なものがあり、あまりスタッフエンジニアはこういうことをするといった画一的な定義があるわけではないことが分かったのは収穫でした。
自分が社内でのプレゼンスを増していったとき、そこで必要だと考え認められた仕事が結果として何らかのポジションを得るんじゃないかなという気持ちになってきています。

キャリアのために意識してやっていること

マネージャになるにせよ、スタッフエンジニアを目指していくにせよ、IC*4としてめきめきやっていくにせよ、共通して重要なのはソフトスキルです。主にそこに対して自分がやっていることを書きます。

技術面では普通にやっているだけなので特筆することが思い浮かびませんでした。特にあくあたん工房に居るようなメンバーならあんまり心配することは無いと思います。

どこからが意識的にやっていて、どこからが無意識的にやっているかと問われると難しいところですが、意図を述べることができる場合は意識的にやっているということにします。

他の人の手助けをする

例えばKubernetesを使っていてこういうことがしたいがエラーが出る、やり方が分からない、これってできるのかな?みたいな投稿を見かけたら意見を述べるようにしています。これは同じチームに限らず、他チーム・他部署に関しても同様です。特にアプリケーションを開発しているメンバーが下回りで困っているときは積極的に関与しています。
他にも最近ジョインしたメンバーが分からないと言っていたらドキュメントやコードへの参照を教えたり、他チームから問い合わせが来たら早めの回答を心がけたりしています。

これは単純に会社全体としてその方が効率が良いというだけではなく、自分の信頼感を上げるための施策の一つと考えています。会社というのはそれなりの人数が居るので、埋もれると一生認知してもらえません。自分の名前を売ること、その名前を良い意味で覚えてもらうことを意識しています。

意見を述べる・質問をする

ずっと横柄な態度取ってるとただのヤバイやつですが、何も言わずにただただ従ってるだけというのはキャリアがどんどん閉ざされると思っています。ある程度の厚かましさは必要だと思っていて、これってどうなん知らんけどとか、なぜそうなってるんですかとかを例え他チーム・他部署のエンジニアにでもぽいぽい投げるようにしています。
多くの人は出来れば他の人が決めてほしいと思っているし、意見を出す最初の一人になりたくないと思っている節があります。そのため、意見を求められたときに場が沈黙して終わりになる場面を度々見かけます。ただ意見を出すだけ質問するだけで議論が活発になる面があり、声を上げる上げないの間には大きな差があります。

これも同様に自分の認知度を上げるための施策の一つであり、同時に自分のやりたい仕事に方向を近づけるための努力でもあります。どうせ同じような仕事するなら自分のやりたい仕事したいですよね。

仕事を任せられる人を増やす

自分一人で仕事をするには限界があります。自分がある程度整理した仕事を処理してくれる人、なんなら不確実性の高いタスクを投げたときに自分の代わりに整理して実行までやってくれる人が居ないと、どこかで処理出来るタスク量が頭打ちします。
当初から人数の少ないチームだったので、人を入れなければいずれ詰むという危機感がありました。工数的にはかなり厳しかったのですが、新人の体験的な受け入れ・学生との面談・インターンシップの開催等は必須であると考え積極的に手を上げるようにしていました。最終的に人を採用できチームに迎え入れられたのはマネージャや他の社員のおかげでもあり、人が足りない・受け入れる用意はあると訴えてきた自分の努力のおかげでもあったと考えています。

自チームに引き込む以外に、他のチームに依頼できる関係を構築しておくのも重要だと思っています。普通に仕事をしていると、自チームの外とは会議の場くらいでしか会わず、気軽にメンションしたりはなかなかハードルが高くなりがちです*5。社内の勉強会、雑談、イベントに顔を出してある程度緩和を試みています。まだここは試行錯誤できる余地が大きいなと思っています。

多様な情報にアンテナを張る

これは元々趣味なので、それほど昔から傾向は変わっていないかなと思っています。技術的なトークが出来る人が増えたり、専門ほどではないにせよ概念を理解していることで応用が利いたり、よいことがたくさん有ります。
自分は主にFeedlyで色んなRSSを購読し、Twitterで軽く話題になっているものに目を通して、Notionに印象に残った物をクリップしてストックしています。

Notionにストックされている様子

ただ、年々これが厳しくなってきているように感じており、今後の情報収集にはあまり期待できないかもなぁと思っています。以下は直近一年での自分の情報収集の変遷です。

  • QiitaのトレンドのRSS購読をやめた
  • はてブのトレンドを追うのをやめた
  • 特定の個人や企業のブログのRSSを個別登録するようになった
  • Techfeed登録してみたけどむしろノイズが増えたので見るのを辞めた
  • Twitterが使い物にならなくなってきてBlueskyに移った
    • まだ時折見ているがおすすめタブが精神に良くなく封印しがち

ちゃんとした人脈を持ち、情報をキュレーションして伝えてくれる人をたくさん知っている人が強くなるなと思っていて、あくあたん工房のメンバーがそういう人脈形成の一つになれば嬉しいなと思っています。

次にやろうと思っていること

やろうと思っているけど出来ていないとか、まだ足りていないなと思っていることです。

メンバーの成果をアピールする

今居るチームにおいて、得られた成果は自分が動いて出したものだけではありません。チームメンバーが手を動かして書いてくれたコード、ドキュメント、障害調査報告……など色々なものがチームの成果として存在しています。

チームの中で特定個人が目立っていると、成果がその人に吸引されがちです。その結果、新しい仕事がどんどんその人に集中してしまうことが起きがちです。ここでその人が正しく仕事を割り振ることも大事ですが、それを常態化してしまうとその人がチームを抜けた/休んだときに他チームとの折衝を上手くやれる人がおらずチームが機能不全になってしまうという問題があります。

チームメンバーが多くなってきたら別ですが、今くらいの規模なら全員がお互いの代わりを務められるくらいでありたいものです。来年はメンバーの成果を積極的に紹介する場を設け、社内に対してアピールして自分以外にもこういう優秀なメンバーが居ますよ覚えてねと言っていきたいと思っています。

チームに入りやすく、そして出やすくする

まだ人を増やしたいです。そして逆に、今のチームである程度経験を積んだ人が他のチームへ移動したり、他のチームを兼務したりできるようにしたいと思っています。

これは「仕事を任せられる人を増やす」にも繋がってくるのですが、色んなチームに知り合いがいた方が基本的には都合がよいです。そういう意味ではしばらく仕事を共にしたチームメイトが別チームに居るというのは、とても仕事を進めやすくなります。メンバーが「{{ 任意のチーム外で扱っている事柄 }} にも興味がある」と言ったときにそれをサポートできる体制でありたいと思っています。

人の出入りが増えるということは、チームの暗黙的なコンテクストや知識が失われがちになってしまう可能性も高めます。特にインフラ基盤の刷新みたいなドラスティックな変更が入る時期ではなおさらです。当たり前ですがちゃんと資料を残すこと、オンボーディングの道筋を示すこと、その他いろいろをちゃんとやろうと思っています*6

終わりに

みんながつよつよエンジニアになって、お仕事とか美味しい食事を恵んでくれると良いなと思ってこの記事を書いています。
つよつよエンジニアになる方法は、技術的に突き抜ける以外にも色々あります。社内の色んなところで幅を効かせて自分に有利になるよう仕事を進めていきましょう。


*1:うちではあまり明確にあなたはこういうレベルです、みたいなものは示されません。実際の給与評価とこういう仕事を期待していますというマネージャからのコメント・会社が示す大まかなレンジを見て自分の立ち位置を言っています。

*2:スタッフエンジニアとは、おおよそエンジニアの最上級職と言って良いです。ただし内情は様々であり、人によって大きく異なる仕事をしている可能性があります。テックリードやアーキテクトなどのポジションは、スタッフエンジニアの一つの形態と言えるでしょう。

*3:買って読み始めてから知っている人がインタビューに出ていることを知って驚きました

*4:Individual Contributor。チームや人のマネジメントをしない、専門職としてのソフトウェアエンジニア。

*5:ハードルが高いだけなので僕は目をつむって送信ボタンを押します

*6:今のチームでは中途のメンバーからのアドバイスで、今年からちゃんと大きめの実装の前にdesign docを書き始めました。書いた資料を元にチームメンバーで合意をとりつつ、お互いの分かっていないところを補完し合えているのではないかなと思っています。