- Electron
XvfbによるElectronのヘッドレス化(Playwright × ElectronをDocker上で動かす)
Electronにはヘッドレスモードはない。そのため、GUIの無いシステムではElectronを使うこともできないわけで。たいてい問題はないが、つまりDocker上で動かすことができないということになる。
XvfbはX Virtual FrameBufferのことで、X Window Systemの仮想ディスプレイのサーバーである。これを使うことでElectronをヘッドレスモードで動かすことができる。下記のドキュメントを参照。
https://www.electronjs.org/ja/docs/latest/tutorial/testing-on-headless-ci
具体的にPlaywright × ElectronをDocker上で動かせるようにしたいときに使える。下記はDockerfileの例(
yarn e2e
でPlaywrightのテストを実行することを想定している)。FROM mcr.microsoft.com/playwright:latest@ WORKDIR /app RUN apt-get install -y xvfb RUN npm uninstall -g yarn \ && rm -rf /usr/local/bin/yarn /usr/local/bin/yarnpkg COPY ./package.json ./package.json RUN corepack enable \ && corepack install RUN yarn install --immutable RUN yarn playwright install chromium COPY . . CMD ["xvfb-run", "yarn", "e2e"]
注意するべきはこれだと動かない場合がある。というのもDockerの実行プロセスがPID 1で動いておりシグナルハンドルが出来ないから…だと思う。–initオプションを付与すると動くようになるので、多分そうだと思う。
後もう1つ、大抵の場合はWindowsで動かす場合が多いので、お手持ちのCIのWindows環境で動かすほうがいいと思う。Github Actionsであればwindows環境がある。
- javascript
package.jsonのdependenciesのバージョンをpinするシェルスクリプト
Renovateでは依存関係をpinすることを推奨していますし、yarnではdefaultSemverRangePrefixによりデフォルトでバージョンを固定化出来ます。
しかしこういった設定をする前にあれこれインストールしてしまって、後からpinするの、ちょっと面倒ですよね。renovateが勝手にやってくれるはずなんですが、なんかやってくれないし。
下記はそれをやってくれるスクリプトです。#!/bin/bash while read -r package current_version; do latest_version=$(yarn info "$package" version --json | jq -r '.children.Version') jq --arg package "$package" --arg latest_version "$latest_version" '.dependencies[$package] = $latest_version' package.json > tmp.json && mv tmp.json package.json done < <(jq -r '.dependencies | to_entries[] | "\(.key) \(.value)"' package.json)
注意点として下記があります。
jq
が必要ですdependencies
を対象としています- 2行目・4行目の
dependencies
をdevDependencies
に書き換えればdevDependencies
を対象に出来ます
- 2行目・4行目の
yarn
を利用していますpnpm
の場合はlistが使えると思います- その場合2行目の
jq
のコマンドもおそらく変える必要があります
- Next.js
Next.jsでビルド中かどうかを判定する(Next.jsのビルド時だけエラーになる処理に対する対応)
NEXT_PHASE
という環境変数を利用することで判定できます。またはNEXT_IS_EXPORT_WORKER
でも判定可能です。背景
下記のような、
環境変数がなかったらエラーになる
処理を書こうとします。config.tsconst fetchEnv = (key: string): string => { const value = process.env[key]; if (!value) { throw new Error(`環境変数 ${key} が設定されていません`); } return value; } export const OIDC_ISSUER = fetchEnv('OIDC_ISSUER');
この処理を、例えば
app/api/auth/login.ts
等で下記のように記載するとします。app/api/auth/login.tsimport { OIDC_ISSUER } from "@/config"; const issuer = new Issuer({ issuer: OIDC_ISSUER, // ... }); export function GET() { // ... } export const dynamic = "force-dynamic";
動的なページであれば、実行環境の環境変数が利用されるのでビルド時には本来不要です。そのためDockerなどでビルドするときも環境変数は不要なはずです。
一方で上記の状態でビルドを行うと、Next.jsのビルド時に実際のコードが読み込まれ、fetchEnv("OIDC_ISSUER")
が実行されます。理由
理由としてはNext.jsのビルド、特にApp Routerを有効化している際には
export
という処理が走るからです。export
は簡単に言えば静的なページを作成するための処理です。App Routerで常に実行されるのは、App Routerのデフォルトの挙動が静的なページを生成するためのものだからと想像しています。静的なページを作成するためには当然対象のページのコードを読み込む必要があります。その際に上記のコードであれば
fetchEnv
関数の実行まで始まってしまうため、環境変数がない場合はエラーになってしまいます。対応方法としてはいくつか考えられそうです。
fetchEnv
をビルド時に実行しないようにする例えば環境変数を定数ではなく関数にするとか。
config.tsconst fetchEnv = (key: string): string => { const value = process.env[key]; if (!value) { throw new Error(`環境変数 ${key} が設定されていません`); } return value; } export const getOidcIssuer = () => fetchEnv('OIDC_ISSUER');
Dynamic Importにするとか。
app/api/auth/login.tsexport async function GET() { const { OIDC_ISSUER } = await import('@/config'); const issuer = new Issuer({ issuer: OIDC_ISSUER, // ... }); } export const dynamic = "force-dynamic";
やりたいことに対して対応が大きすぎる気もする。
const lazy
が欲しいですね。ビルド時のみエラーにならないようにする
Next.jsでビルド中かどうかを判定するために環境変数が存在します。それが
NEXT_PHASE
です。NEXT_PHASEについて示唆されているDiscussionが一番詳細に記載されています。
NEXT_PHASEの値の定義として下記の5つの値が存在します。上記で
export
時に実行されると記載されていますが、NEXT_PHASE
はphase-export
ではなくphase-production-build
となっていることに注意です。export const PHASE_EXPORT = 'phase-export' export const PHASE_PRODUCTION_BUILD = 'phase-production-build' export const PHASE_PRODUCTION_SERVER = 'phase-production-server' export const PHASE_DEVELOPMENT_SERVER = 'phase-development-server' export const PHASE_TEST = 'phase-test'
また、それだとビルド中なのかExport中なのか判断がつかないため、
NEXT_IS_EXPORT_WORKER
という環境変数も存在します。trueという値が入ります。下記のように環境変数を利用してビルド時のみエラーにならないようにできます。
config.tsimport { PHASE_PRODUCTION_BUILD } from "next/dist/shared/lib/constants"; const fetchEnv = (key: string): string => { const value = process.env[key]; if (!value) { if (process.env.NEXT_PHASE === PHASE_PRODUCTION_BUILD) { return ""; } throw new Error(`Missing environment variable: ${key}`); } return value; }; export const OIDC_ISSUER = fetchEnv('OIDC_ISSUER');
- docker
DockerでCOPYを用いずにpackage.jsonを使ってyarn installする(RUN実行時に一時的にマウントする)
Dockerについて半日ぐらい調べたときの成果2。
RUN --mount
について。COPYコマンドは最終成果物に含まる。例えば、
COPY . .
で全てのファイルをコピーすると、最終成果物には全てのファイルが含まる。これは不要なファイルが含まれるため、最終成果物のサイズが大きくなる。マルチビルドステージにより解決できるが、それ以外にもRUN --mount
を使うことで解決可能だ。
基本的には公式ドキュメント通りだが、下記のようにすると、pakage.json
を成果物に含めずにyarn install
が可能だ。FROM node:22 AS build RUN yarn install
ただし
--mount
とCOPY
で大きく違うところが1つあり、既存のディレクトリをtargetとしたときの挙動である。
COPYはリファレンス内にて下記のように記載されている。If it contains subdirectories, these are also copied, and merged with any existing directories at the destination. Any conflicts are resolved in favor of the content being added, on a file-by-file basis,
サブディレクトリが含まれている場合、それらもコピーされ、コピー先の既存のディレクトリとマージされます。競合がある場合は、ファイルごとにコンテンツが追加されるように解決されます。
一方でRUN --mount、正確にはbind mountについてはリファレンスにて下記のように記載されている。
If you bind-mount a directory into a non-empty directory on the container, the directory’s existing contents are obscured by the bind mount.
コンテナ内の空でないディレクトリにディレクトリをバインドマウントすると、ディレクトリの既存の内容がバインドマウントによって隠されます。
つまり、COPYはディレクトリをマージするが、–mountはディレクトリを上書きするような挙動になる。
- docker
Dockerのマルチステージビルド
Dockerについて半日ぐらい調べたときの成果1。マルチステージビルドについて。
公式ドキュメント以上や、その他既にたくさんある記事以上の情報はないが、少なくとも令和のDockerfileでは必須な知識だと思う。
下記のように、FROMが2個以上出てくるのが特徴だ。例えばWebpackによりバンドルしたファイルをnodeで実行するときなんかにとても効力を発揮する。
FROM node:22 AS build WORKDIR /app COPY . . RUN npm install && npm run build FROM node:22 AS runtime WORKDIR /app COPY /app/dist /app/dist CMD ["node", "dist/index.js"]
ステージというのは簡単に言えばFROMで定義したイメージのことで、上記ではビルドと実行の2つのステージがある。Dockerの最終的な成果物は最後のステージのイメージになる。この場合は
runtime
ステージだ。
COPY --from
により異なるステージの中で生成されたファイルをコピーできる。上記では最終的に実行に必要なバンドル後のapp/dist
ディレクトリをruntime
ステージにコピーしている。これの最大の利点は最終的な成果物のサイズが小さくなることだ。1つのステージしかなければ(明示的に削除しない限り)
node_modules
やsrc
など不要なファイルがふくまる。とくにnode_modules
なんかはデカくなりがちであるため、これを削除することでイメージのサイズを小さくできる。 - docker
Dockerでモノレポのpackage.jsonをいい感じにコピーする(ディレクトリ構造を維持しつつコピーする)
Dockerについて半日ぐらい調べたときの成果3。
COPY --parents
について。モノレポ環境でDockerのビルドをしたい時に、
yarn install
をしたい場合、2つほど問題が発生する。- 他パッケージに依存しているときはモノレポのルートでCOPYをする必要がある
yarn install --immutable
をする場合は、すべてのモノレポのpackage.json
が必要
上記を解決するためには、モノレポのルートをビルドコンテキストとして指定する必要がある。そこで各パッケージの
package.json
をコピーしたいが、COPY ./packages/*/package.json .
のようなコピーの方法は使えない。というのもこれだと./packages/backend/package.json
を./package.json
にコピーした後./packages/frontend/package.json
を./package.json
にコピーする、という感じの動作になる。つまり、最後にコピーしたpackage.json
が残る。これを解決するのにはCOPY ./packages/backend/package.json ./packages/backend/package.json
のように、コピー元とコピー先を指定する必要がある。これだとパッケージが増える度に指定する必要がある。そこで**COPY --parentsを利用する。これはコピー元の親ディレクトリを保持したままコピーしてくれる**、上記の課題を解決してくれるすぐものだ。
COPY --parents ./packages/*/package.json ./
とやると、
./packages/backend/package.json
を./packages/backend/package.json
にコピーし、./packages/frontend/package.json
を./packages/frontend/package.json
にコピーする、という動作になる。ドキュメントに記載通りだが、上記はstableな機能ではないため、
# syntax=docker/dockerfile:1.7-labs
を先頭に扶養する必要がある。 - vscode
もうAuto Rename Tagは不要だった
Auto Rename TagはVSCodeの著名な拡張の1つです。
上記拡張のNoteにも記載があるのですが、実はこの機能がVSCodeのビルトインとして統合されています。
デフォルトで有効化されていませんが
editor.linkedEditing
という設定をtrueにすることで有効になります。 - sql
SQLのUPDATE RETURNINGで更新対象外のテーブルをJoinする
更新処理を実行した後、対象以外のテーブルをジョインしてRETURNINGして欲しくなった。
まぁ、これに関しては、実装的に1つのクエリでやりたいことが2つ出てしまうのであまり良くないと思ったので、結局は更新処理と取得処理を分離した。通信に関してあまり詳しくはないが、仮にトランザクション中であれば接続貼りっぱなしだと思うので通信のRTTも気にならんだろうし。
ただ、JOINしてWHEREしたいケースにも利用できる(というか、どちらかというとそちらがメイン)。FROM句を利用する。UPDATEにFROM句が使えるんだな。下記はPostgreSQLのリファレンスより引用し、RETURNINGを追加している。
UPDATE employees SET sales_count = sales_count + 1 FROM accounts WHERE accounts.name = 'Acme Corporation' AND employees.id = accounts.sales_person RETURNING employees *, accounts.*;
- sql
PostgreSQLのSKIP LOCKED
PostgreSQLを利用して、キューのようなものを作成したくなったところ、SKIP LOCKEDというものを知りました。
名前の通り、ロックされている行ををスキップしてくれるものです。例えば下記のようなテーブルを作成してみます。
CREATE TABLE tasks ( id SERIAL PRIMARY KEY, status TEXT NOT NULL DEFAULT 'pending', created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP ); INSERT INTO tasks (id) VALUES (1), (2), (3), (4), (5);
現状こんな感じ。
$ select * from tasks; id | status | created_at ----+---------+------------------------------- 1 | pending | 2024-06-29 12:34:50.000000+00 2 | pending | 2024-06-29 12:34:51.000000+00 3 | pending | 2024-06-29 12:34:52.000000+00 4 | pending | 2024-06-29 12:34:53.000000+00 5 | pending | 2024-06-29 12:34:54.000000+00
ここで、下記のようなトランザクションを実行します。まだコミットしていないので対象の行はロックされたままです。
$ BEGIN; $ SELECT id FROM tasks ORDER BY created_at LIMIT 1 FOR UPDATE; id ---- 1
この間にトランザクション外から下記のコマンドを実行してみます。
$ SELECT id FROM tasks FOR UPDATE SKIP LOCKED; id ---- 2 3 4 5
この様に、ロックされたid 1の行をスキップしました。
PostgreSQLのリファレンスにはSELECT - ロック処理句に記載があります。
こちらの機能はSQL標準ではないようですが、MySQLにも存在しています。おそらく8.0から。 - npm
npm scriptsで引数をいい感じに分割して複数コマンドに分配したい
タイトルからだとかなり読みづらいが、下記のようなことをしたい。こういった欲求は度々起こるが、その度調べては難しそうだったので諦めていた。簡単に言えばnpm scriptsの内部で引数を分離したいのである。
lint・prettierを一発でやるコマンド{ "scripts": { "lint:fix": "eslint --fix", "prettier:write": "prettier --write", "format": "???" }, }
上記のスクリプトがある前提で、下記を期待する。
$ npm format __tests__/index.test.ts > npm lint:fix __tests__/index.test.ts && npm prettier:write __tests__/index.test.ts
例えばシンプルに下記のようにする。
lint・prettierを一発でやるコマンド{ "scripts": { "lint:fix": "eslint --fix", "prettier:write": "prettier --write", "format": "npm lint:fix && npm prettier:write" }, }
そうすると最後に引数がくっつくだけである。
$ npm format __tests__/index.test.ts > npm lint:fix && npm prettier:write __tests__/index.test.ts
npmだけではそういうことはできなさそう だが、ここでnpm-run-allの存在を思い出した。もう6年前から更新されてない。
結論としてはnpm-run-allで今回の欲求は満たせた。Argument placeholdersという機能を利用すれば良い。lint・prettierを一発でやるコマンド{ "scripts": { "lint:fix": "eslint --fix", "prettier:write": "prettier --write", "format": "run-s 'lint:fix {1}' 'npm prettier:write {1}' --" }, }
$ npm format __tests__/index.test.ts > npm lint:fix __tests__/index.test.ts && npm prettier:write __tests__/index.test.ts
最後の
--
はscripts側にないと想定通りに動作にはならない。 - vscode
ESLintがCLIだと動くのにVSCodeだと動かない
下記の条件を満たしていると、VSCodeのバージョンが1.90.0(2024-06-12時点での最新)以下で動作しない。
- typescript-eslintを利用している
- monorepoを利用している
- ESMである・
eslint.config.mjs
である languageOptions.parserOptions
のproject
およびtsconfigRootDir
を設定しているimport.meta.dirname
を利用している ← これが原因
原因としては、VSCode1.90.0の内部で利用されているElectron 29のNode.jsのバージョンが20.9.0であるため、
import.meta.dirname
が利用できない。
import.meta.url
は利用できるため、下記のような回避策で解決できる。eslint.config.jsonimport { fileURLToPath } from "url"; const filename = fileURLToPath(import.meta.url); const dirname = import.meta.dirname ?? path.dirname(filename); export default tseslint.config( ..., { languageOptions: { parserOptions: { project: ["./tsconfig.eslint.json", "./packages/*/tsconfig.json"], tsconfigRootDir: dirname, }, }, }, ..., )
Node.js 20.11.0で追加されている ので、Electron v30(Node.js 20.11.1)では
import.meta.dirname
が利用可能になるはず。 - vscode
VSCodeでyamlを開いた際、候補を表示しない
yamlを編集していると候補が邪魔をしてきて鬱陶しかったので設定を入れた。
下記を記載すると、入力するだけで候補の表示がされなくなる。Macであればctrl-space
(入力ソースの変換と被っているが)で候補は表示される。settings.json"[yaml]": { "editor.quickSuggestions": { "comments": "off", "strings": "off", "other": "off" } }
P.S.
1回上記の設定を入れた後に無効化しても全然候補が表示されなくなった。今回参考にしたのは下記の記事。
- gcloud, solve
brew upgrade後gcloudコマンドが動かなくなった
brew upgrade後、
gcloud
コマンドを叩くと下記のようなエラーが発生するようになった。ERROR: gcloud failed to load. This usually indicates corruption in your gcloud installation or problems with your Python interpreter. Please verify that the following is the path to a working Python 3.8-3.12 executable: /Users/sa2taka/.pyenv/versions/3.11.1/bin/python If it is not, please set the CLOUDSDK_PYTHON environment variable to point to a working Python executable. If you are still experiencing problems, please reinstall the Google Cloud CLI using the instructions here: https://cloud.google.com/sdk/docs/install Traceback (most recent call last): File "/Users/sa2taka/libs/google-cloud-sdk/lib/gcloud.py", line 106, in gcloud_exception_handler yield File "/Users/sa2taka/libs/google-cloud-sdk/lib/gcloud.py", line 183, in main gcloud_main = _import_gcloud_main() ^^^^^^^^^^^^^^^^^^^^^ ...(中略)... ImportError: dlopen(/Users/sa2taka/.pyenv/versions/3.11.1/lib/python3.11/lib-dynload/_ssl.cpython-311-darwin.so, 0x0002): Library not loaded: /usr/local/opt/openssl@1.1/lib/libssl.1.1.dylib Referenced from: <...> /Users/sa2taka/.pyenv/versions/3.11.1/lib/python3.11/lib-dynload/_ssl.cpython-311-darwin.so Reason: tried: '/usr/local/opt/openssl@1.1/lib/libssl.1.1.dylib' (no such file), '/System/Volumes/Preboot/Cryptexes/OS/usr/local/opt/openssl@1.1/lib/libssl.1.1.dylib' (no such file), '/usr/local/opt/openssl@1.1/lib/libssl.1.1.dylib' (no such file), '/usr/local/lib/libssl.1.1.dylib' (no such file), '/usr/lib/libssl.1.1.dylib' (no such file, not in dyld cache)
エラーの上部に解決策が色々出てるが、原因は下部のほうにある
openssl@1.1
がないことだ。どうやらbrew upgrade
を叩いた際に消えたようだ。
とりあえずopenssl@1.1
をダウンロードすることで解決できる。$ brew install openssl@1.1
- notion
Notionの引用のショートカット
一般的なマークダウンでは
>
が引用であるが、Notionではトグルに割り当てられている。/quote
とすることで引用にできるが、それ以外に"
または|
がショートカットとして割り当てられている。確かに|
は一般的な引用のスタイルだし、わかりやすいかもしれない。 - typescript,eslint
TypeScript ESLintがモノレポ環境でOOMになるのを解決
TypeScript ESLintはTypeScriptの型情報を利用して、よくあるミスを洗い出してくれるいい子ですが、そのかわり1回TypeScriptのパーサーを挟むので重い・遅い・メモリを食うという状況になります。
重すぎるので今までCI以外では切っていたんですが、Flat Config化するにあたり、せっかくなら普段のVSCode上でエラーも出したいしということでTypeScript ESLintを全体有効化したところ、設定が誤っていて今までモノレポ環境のLintでTypeScript関連のルールが動いていなかったことが判明。エラーが2つぐらいでるようになったのは良いのですが、何やったってCIがメモリエラーで死ぬ。何なら32GBある私のPCもメモリエラーが吐かれる。
下記のようなエラーですね。FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
調べたところ、ちょうど今日解決策がissueに投稿されていました。
EXPERIMENTAL_useProjectService
というプロパティを設定すればよいようです。flat configであれば下記のように
parserOptions
を設定しましょう。eslint.config.jsexport default [ { languageOptions: { parserOptions: { ..., EXPERIMENTAL_useProjectService: true, }, }, } ]
EXPERIMENTAL_useProjectServiceは名前の通りまだexpreimentalな機能です。
TypeScript ESLintが重い理由はts.CreateProgram
を利用しているからです(多分)。
で、上記を有効にすると、こんな感じでASTを取得します。useProgramFromProjectService.tsconst program = service .getDefaultProjectForFile(scriptInfo!.fileName, true)! .getLanguageService(/*ensureSynchronized*/ true) .getProgram();
これがなぜ早くなるのか、正直全くわかりませんが実際にOOMが解決されるのでなにか意味があるのでしょう。
Copilot3.5では無理でしたが、Claudeで聞くといい感じに答えてくれます。全然情報が無いですね…。私のJS Test Outlineもts.Program利用しているので、ProjectServiceの利用を検討してみようかしら。
- git
Gitで最後に触っていたブランチに戻る
最近よく「一瞬だけこのブランチに移動して、その後もとに戻る」という操作をする。しかし、びっくりするぐらい短期力に難がある私にとって3分前に切り替えたブランチ名は忘れるのである。
調べると最後に触っているブランチへ戻る方法があった。$ git switch - # 上は下記の糖衣構文。-1の部分を2とか3とかにすればもっと遡れる。HEADみたいなもんすね $ git switch -@{-1}
またreflogにもログが残っている。
$ git reflog | grep switch 12345abce HEAD@{0}: checkout: moving from master to release
参考:
https://qiita.com/ginpei/items/2e0cd22df0670b3a1c3f
https://ginpen.com/2022/12/09/git-back-to-last-branch/
https://zenn.dev/yajamon/articles/422ecab49804f9 - raw
指標はハックされる〜グッドハートの法則
私は、何らかの人間に対する計測時にハックされる可能性を必ず考慮する。
例えば給料の査定(私自身にそんな権限はないが)だったりもそうだし、生産性を上げるときの計測対象なんかは最近専ら議論している。こういった話をした時にチームメンバーから教えていただいたのが、グッドハートの法則。
グッドハートの法則
グッドハートの法則は「指標が目標となると、それは良い指標ではなくなる」と説明されている。これだと、巷で噂のKPIは良い指標ではないのかと突っ込みたくはなるが、言いたいことはそうではないと思う。裏まで読むと「指標にすべきではない指標が目標となると、それは良い指標ではなくなる」、言ってしまえば目標とすべき指標はちゃんと選べよということかもしれない。
例えば、生産性の目標としてPR数を指標とした。PR数が上がれば生産性が上がったよねとする。そうなると、俺は通常より細かい単位でPRを作る。そうすれば生産性が上がるから。本当の目標は「生産性を上げる」なのに「PR数を作る」という、俗に言うと手段と目的が入れ替わっている状態となる。
これじゃ駄目だよねという話。
複数の指標を測る・加えて一部の指標を定性的な目標にする、等少なくともハックできないようなものにする。または、目的 = 指標(目的 ∈ 指標)となるような指標を選択することが回避に大切だと思う。
例えば生産性を測るためにFour Keysを利用する。この場合目的 = 指標とは言いづらいが、統計的に有為であるため問題はないといえる。
具体例としては コブラ効果 などもその例だろう。寓話っぽくて好き。
また、キャンベルの法則という似たような法則もある。
- unicode
Unicode正規化
言語処理の文脈で、度々Unicode正規化という言葉を聞く。言葉や処理自体は知っていたが、いくつか種類があるようなので改めて調べてみた。
Unicode正規化(ユニコードせいきか、英語: Unicode normalization)とは、等価な文字や文字の並びを統一的な内部表現に変換することでテキストの比較を容易にする、テキスト正規化処理の一種である。
具体的に、正規化には4種類ある。
- NFD
- NFC
- NFKD
- NFKC
末尾のD・CはそれぞれDecomposition(分割)・Composition(結合)である。Dの場合は正規化した上で分解したまま、Cの場合は分解した後結合する。
Kの有無だが、これは正準等価(K無し)・互換等価(K有り)の違いらしい。具体的には調べていないが、正準等価に比べ互換等価の方が緩く分解されるらしい。JavaScriptにおいては
String
にnormalize
メソッドが生えているため、それを利用することでノーマライズが可能である。$ node > const char = "ギ" undefined > char.normalize("NFD") 'ギ' > char.normalize("NFC") 'ギ' > char.normalize("NFKD") 'ギ' > char.normalize("NFKD").length 2 > char.normalize("NFKC") 'ギ' > char.normalize("NFKC").length 1
- エンジニアリング
SPACEという生産性のためのフレームワーク
SPACEという生産性のためのフレームワークを知った。
生産性のフレームワークは有名所だとFour Keysがあるが、SPACEは下記の5つの指標で構成されている。
- Satisfaction and well being
- 開発者が自分の仕事、チーム、ツール、文化にどれだけ満足しているか
- ストレスチェックや、開発者が必要なツール・リソースを持っているか、
- Performance
- 開発者が書いたコードは、想定されたことを確実に実行したか?
- 品質や信頼性、定量的に見るならFour Keysにおける変更障害率あたり
- Activity
- 業務を遂行する過程で完了した行動やアウトプットの数
- 論文内では設計ドキュメント・仕様、PR、コミット、レビュー数となっている。Four Keysのデプロイ数が当てはまりそう
- Communication and collaboration
- メンバーやチームがどのようにコミュニケーションをとり、協力し合うか
- 論文内では、ドキュメントのみつけやすさや、レビューの質、コミュニケーションのネットワークメトリクスやオンボーディング時間が書かれている
- Efficiency and flow
- チームやシステム全体で中断や遅延を最小限に抑え、作業を完成、進行できているか
- Four Keysにおけるサービス復元時間・変更のリードタイムあたりが当てはまりそう
Four Keysと比べると、人の満足度、コミュニケーション的な部分が加わっている印象。
システムの健全さを表す指標がFour Keysなら、チームの健全さを表す指標がSPACEなのかな、と感じた。ちなみにForu KeysやこのSAPCEをまとめて DPE(Developer Productivity Engineering) と呼ぶこともあるらしい。
- Satisfaction and well being
- javascript
JavaScriptにおける剰余の正負
JavaScriptにおける剰余の正負の結果を毎回忘れる。 常に被除数と同一 と覚えればいいと学んだ。
被除数はすなわち「割られる数」でありa % b
においてa
である。$ node Welcome to Node.js v20.5.0. Type ".help" for more information. > 17 % 7 3 > 17 % - 7 3 > -17 % 7 -3 > -17 % -7 -3
Wikipediaに言語ごとにまとまっていて非常に良かった。
- typescript
VSCodeにおいてTypeScriptの自動インポート対象から省きたい場合
autoImportFileExcludePatterns
というフラグがある。これを設定することで、自動インポートの対象から省かれDX(Developer eXperience)が向上する。例えば、
User
というよくあるクラス名は既存のライブラリからもExportされていることが多い(Sentry
やDataDog
など)。
これらを指定すればそれらのライブラリからの自動インポートを省いてくれる。弱点としてはVSCodeにしか対応していない(正確に言えばTS Serverのオプションなので設定さえすればVimとかでも問題ないとは思う)のと、ライブラリごと省かれてしまうこと。
どうしても回避したいなら独自のTS Language Server Pluginを書けば良いと思うが…。ちなみにTypeScript4.8以降の機能。
https://devblogs.microsoft.com/typescript/announcing-typescript-4-8-rc/#exclude-specific-files-from-auto-imports - データ構造/アルゴリズム
Trieというデータ構造
文字列検索の文脈でTrie(トライ)というデータ構造を知った。
retrieval
から取られたようだ。
検索文字列の辞書がある場合、その辞書の長さに関わらず一定のオーダーで検索できるのが強みらしい。 - Next.js
Next.jsでMDXを使う
画像周りが正直面倒くさ過ぎたため、MDXでなんとかできれば嬉しい。
内部的にはremark
を使っているらしい。ちなみに昔僕が使おうとしていたのはmarked
で、現在使っているのがmarkdown-it
。https://nextjs.org/docs/pages/building-your-application/configuring/mdx
- ライフハック
VSCode上でGithubのPermalinkを取得する
選択範囲に対して、行番号を右クリックすることで、Permalinkが取得できる。
- ライフハック
TIL
TILはToday I Learnedの略で、今日学んだことを振り返る意味がある。
GithubにTILリポジトリを作り、Markdownか何かを作るフレームワーク的なものが広がったらしい。Slackのtimesチャンネルによく「こんなのあるんだ」的なのを呟いていたが、振り返りづらすぎるため、僕のBlogサービスにTIL的な機能を載っけてみる。
ほぼブログみたいなもんだが、もうちょい腰を据えずに自分向けの記事を書いていくつもり。