Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Amazon ECS デプロイツール ecspresso の開発を支える「正しい抽象化」の探求...
Search
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
FUJIWARA Shunichiro
November 13, 2025
Technology
11k
13
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Amazon ECS デプロイツール ecspresso の開発を支える「正しい抽象化」の探求 / YAPC::Fukuoka 2025
YAPC::Fukuoka 2025発表資料です
FUJIWARA Shunichiro
November 13, 2025
More Decks by FUJIWARA Shunichiro
See All by FUJIWARA Shunichiro
作るべきものと向き合う - ecspresso 8年間の開発史から学ぶ技術選定 / 技術選定con findy 2026
fujiwara3
9
4.5k
さくらのクラウドでのシークレット管理を考える/tamachi.sre#2
fujiwara3
2
330
パフォーマンスチューニングのために普段からできること/Performance Tuning: Daily Practices
fujiwara3
9
6.7k
alecthomas/kong はいいぞ
fujiwara3
7
2.5k
ecspressoの設計思想に至る道 / sekkeinight2025
fujiwara3
12
3.6k
さくらのIaaS基盤のモニタリングとOpenTelemetry/OSC Hokkaido 2025
fujiwara3
3
4.1k
監視のこれまでとこれから/sakura monitoring seminar 2025
fujiwara3
12
5.8k
k6による負荷試験 入門から日常的な実践まで/Re:TechTalk #01
fujiwara3
2
610
困難を「一般解」で解く
fujiwara3
10
4.3k
Other Decks in Technology
See All in Technology
Chart.js が簡単に使えるようになっていたので OGP 画像生成に使った話
kamekyame
0
170
実装は速くなった、レビューはどうする? ― 自身のレビューをAIで再現させるサーヴァントエンジニアリングのすゝめ / Implementation got faster. So what about reviews? — An invitation to Servant Engineering: Recreating your own code reviews with AI
nrslib
7
4.3k
AWSシリコン最前線 〜AI時代のチップ選択を読み解く〜
htokoyo
1
240
MCP Appsを作ってみよう
iwamot
PRO
4
170
製造業のクラウド活用最適解〜AI,DXを加速するデータ基盤の作り方〜
hamadakoji
0
420
noUncheckedIndexedAccess、3時間、1万円。 / noUncheckedIndexedAccess, 3 Hours, 10,000 JPY.
kaonavi
1
340
AI駆動開発が変える、大規模開発の前提 ーHuman in the Loop から Human on the Loop へ / AIE2026
visional_engineering_and_design
29
21k
チームで実践する AI-DLC 思考の軌跡を残すチェックポイント設計
belongadmin
0
3k
価格.comをAI駆動で全面刷新する ー 30年分の技術的負債を返し、次の30年の土台をつくる ー / AI Engineering Summit Tokyo 2026
tkyowa
50
57k
ChatworkとBPaaS 異なる特性で学んだAI機能開発の ベストプラクティス
kubell_hr
2
3.2k
Platform engineering for developers, architects & the rest of us (AI agents)
danielbryantuk
0
190
タクシーアプリ『GO』の実践的データ活用
mot_techtalk
3
170
Featured
See All Featured
A Guide to Academic Writing Using Generative AI - A Workshop
ks91
PRO
1
320
Digital Projects Gone Horribly Wrong (And the UX Pros Who Still Save the Day) - Dean Schuster
uxyall
0
1.6k
Why Your Marketing Sucks and What You Can Do About It - Sophie Logan
marketingsoph
0
160
What's in a price? How to price your products and services
michaelherold
247
13k
Visualizing Your Data: Incorporating Mongo into Loggly Infrastructure
mongodb
49
10k
A brief & incomplete history of UX Design for the World Wide Web: 1989–2019
jct
2
390
Unsuck your backbone
ammeep
672
58k
sira's awesome portfolio website redesign presentation
elsirapls
0
270
Organizational Design Perspectives: An Ontology of Organizational Design Elements
kimpetersen
PRO
1
720
Game over? The fight for quality and originality in the time of robots
wayneb77
1
190
Site-Speed That Sticks
csswizardry
13
1.2k
Understanding Cognitive Biases in Performance Measurement
bluesmoon
32
2.9k
Transcript
Amazon ECS デプロイツール ecspresso の 開発を支える「正しい抽象化」の探求 2025-11-14 YAPC::Fukuoka 2025 藤原俊一郎
(@fujiwara)
自己紹介 @fujiwara (X, GitHub, Bluesky) @sfujiwara (hatena, mixi2) 2011〜2024 面白法人カヤック
2025-02〜 さくらインターネット ISUCON 優勝4回 / 運営(出題)4回 github.com/kayac/ecspresso github.com/fujiwara/lambroll
kayac/ecspresso 1000+ Amazon ECSデプロイツール 「エスプレッソ」と読みます 2017/11〜(8年間継続開発中) 国内企業で広く利用されている 50名以上のコントリビュータ 普段の開発とメンテはほぼ @fujiwara
ひとり(個人開発に近い)
ecspresso の特徴 Amazon ECSの管理と操作を行う IaC CLIツール create deploy rollback verify
diff status scale ... 「タスク定義」 「サービス」を AWS SDK / AWS-CLI と同じ構造の JSONで管理 それ以外のリソース(VPC、LBなど)は 管理しない
今日のテーマ: 「正しい抽象化の探求」 ecspressoの設計思想 他の IaC ツールと比較した抽象化アプローチ 抽象化のトレードオフと持続可能性 2025年設計ナイト「ecspressoの設計思想に至る道」の続編です https://speakerdeck.com/fujiwara3/sekkeinight2025
責任範囲 システム構成 アプリケーション Consul,nginx,Fluentd... EC2 OS IAM, VPC, LB, RDS...
👥 カヤック 👥 MSP ecspressoの設計思想に至る道 2014〜2015年 成功体験 Consul + Stretcherによる デプロイシステムを構築 100台規模のEC2オートスケールで 大成功 2016年 失敗から学ぶ 新規開発で同じ構成を採用 開発(カヤック) / インフラ管理(MSP) 責任が曖昧になって双方が苦労
責任範囲 システム構成 ECS App,nginx,Fluentd... IAM, VPC, LB, RDS... 👥 カヤック
👥 MSP ecspressoの設計思想に至る道 2017年 責任分界点を明確化 新タイトルは同じ座組でも 「ECS上全部」と「それ以外」 → ecspressoを開発 2020年 設計思想の言語化 「操作範囲をあえてECSのみに限定」 他のリソース(VPC,LB...)は別の手段で ライフサイクルが違うものの管理は 手段も分ける
「ecspressoの設計思想に至る道」とは 教訓:設計とは「どこで切るか」 適切な境界線は組織構成によって異なる 責任範囲と操作する範囲を一致させることが重要 変更頻度が少ないインフラ(VPC, LBなど)は Terraform / CloudFormation などで管理
変更頻度が高いアプリケーション(ECSタスク定義、サービス)は ecspresso で管理 インフラ要素のIDは tfstate, CFn Outputs/Exports から参照できる それぞれの管理はチームが分かれることも多い お互いの変更による影響を最小限に抑え、開発効率と安定性を両立
アーキテクチャ/実装をほかのIaCツールと比較する 「ecspressoの設計思想」に至った背景を踏まえた上で… 「AWS SDKをほぼそのまま使う薄いラッパー設計」 =あえて抽象化しないのが ecspressoの 特徴 これにより得たものを他のIaCツールと比較しながら探求していきます
ECS(を管理できる) IaC ツールを比較する ecspresso: kayac/ecspresso AWS SDKをほぼそのまま + テンプレート関数 Terraform:
hashicorp/terraform-provider-aws 独自DSL(HCL)でリソースを宣言的に管理 プラグインアーキテクチャ/マルチクラウド対応 AWS CDK: aws/aws-cdk プログラミング言語でリソースを定義 抽象化レベルを選択可能 AWS Copilot CLI: aws/copilot-cli ECS/App Runnerを簡単にデプロイ Opinionated な設計
ecspresso: JSON + テンプレート記法 JSON は AWS SDK とほぼ同じ構造 {{
must_env }} : 環境変数を参照 {{ tfstate }} : Terraformで作成し たリソースをtfstateファイルから参照 JSON と Jsonnet の両方をサポート
Terraform : HCLで統一した世界 HashiCorp Configuration Language JSONやYAMLがデータ構造をシリアライ ズするためのフォーマットであるのに対 し、HCLは構造化された設定フォーマッ トを構築するために特別に設計された構
文とAPIです github.com/hashicorp/hcl より引用翻訳 SDKの executionRoleArn → HCLでは execution_role_arn (統一された命名規則)
AWS CDK: 選べる抽象化レベルとプログラミング言語での記述 TypeScript, Python, Java, C#, Go など複数言語で記述可能 L1
Construct: AWSリソースをほぼそのまま表現(≒SDK/CFn) L2 Construct: よく使うパターンを抽象化した高レベルAPI L3 Construct: アプリケーション全体を構築するための包括的な抽象化 この発表では出てきません 最終的には CloudFormation (CFn) に変換されてからデプロイされる トラブルシューティング時には CFn の知識が必要になることも
AWS CDK (L1 Construct) ほぼ AWS SDK / CFn と同じものをTypeScriptで記述
AWS CDK (L2 Construct) L1に適切なデフォルト自動生成を追加して簡潔、型安全に記述可能 family , networkMode , requiresCompatibilities
は自動設定(デフォルト値) executionRoleArn もロール自体を自動生成するので省略可能 image → ContainerImage.fromRegistry で型付き cpu / memoryLimitMiB は数値で指定 (SDK/CFnでは文字列) メソッドチェーンでリソースを構築 taskDefinition.addContainer(...)
AWS Copilot CLI Opinionated な設計 サービス全体の関連リソースを manifest(YAML) で一元管理 これだけでECSで動くアプリケーシ ョンをデプロイできる
ALB, VPC, Subnet, NAT Gateway, Security Group...すべて自動作成 最終的には CDK 同様 CFn に変換さ れてからデプロイされる←重要
ECSを管理できる IaC ツール - それぞれ「良いところ」がある ecspresso: AWS SDKをほぼそのまま + テンプレート記法
概念・用語の読み替えが必要ない ドキュメントはAWS公式を見ればOK Terraform: 独自DSL(HCL)でリソースを宣言 統一された構文と命名規則で複数リソースを管理 複数のクラウドやSaaSでも同じような記述 AWS CDK: 抽象化レベルを選択、プログラミング言語で記述 L1〜L3までの抽象化レベルで柔軟に設計可能 より少ない記述(L2)、IDEサポート、コード補完で記述性が高い AWS Copilot CLI: Opinionated な設計 簡単な記述でWebサービスに必要な関連リソースを自動構築
抽象化アプローチの違いがもたらす実装への影響 ツールを使う側としては 「それぞれ良いところがある」 しかし開発・メンテナンスする側には大きな違いが……? そこを探求していきます
実例(1) ECS native Blue/Green デプロイメントへの対応 2025-07-17にECSに追加された Blue/Green デプロイメント機能 CodeDeployを使わずにECSネイティブで実現可能に これ以前はECSのみではRolling
updateしかできなかった
ECS Blue/Green - API レベルでの変更点 ECSサービスの設定で deploymentConfiguration.strategy に BLUE_GREEN を指定可能に
loadBalancers[].advancedConfiguration が追加 @@ -22,7 +22,7 @@ "deploymentConfiguration": { - "strategy": "ROLLING" + "strategy": "BLUE_GREEN" }, @@ -31,6 +31,11 @@ "loadBalancers": [ { + "advancedConfiguration": { + "alternateTargetGroupArn": "arn:aws:elasticloadbalancing:***", + "productionListenerRule": "arn:aws:elasticloadbalancing:***", + "roleArn": "arn:aws:iam::***:role/ECSServiceRole" + },
ecspressoの対応 +133 -52 https://github.com/kayac/ecspresso/pull/861 AWS SDK Go v2 を最新に更新 B/G
関係の struct や定数追加 実は deploy はこれだけで対応完了 ロールバック用の API 呼び出しを追加 これまではロールバック自体ができ ないので別途実装していた 進行中のデプロイメントに対して ecs.StopServiceDeployment(...) 2025-07-19 にマージ(ECS B/G発表2日後)
Terraformでの対応 +1,759 -85 https://github.com/hashicorp/terraform- provider-aws/pull/43434 internal/service/ecs/service.go +540, -3 APIに追加された要素 (
advanced_configuration など) に対応するコードを追加 ドキュメントとテストの追加 このPRはdeployのみ ロールバックは別PR #43986 (2025-08-27)で対応 +419 −31 2025-07-18 にマージ(ECS B/G発表1日後)
ECS Blue/Green - ecspresso vs Terraform 対応比較まとめ 抽象化の選択によって追従時の実装コストに差が出てくる deploy操作のみの対応比較 抽象化の種類
実装に必要な対応 変更行数 ecspresso AWS SDK JSON そのまま SDKの更新のみ 数行 Terraform 独自DSL (HCL) SDKとDSLの相互変換 ドキュメント・テスト追加 1,500行以上 TerraformがDSL抽象化コストを払って得ているもの 「ECS以外のリソース、他のクラウドやSaaSをも管理できる統一された世界」 このコストはコミュニティ(受益者、開発者)の大きさ故に支払える面もある ほぼ個人開発の ecspresso で選択するのは……?
実例(2) 複数LB対応の比較 2019-07 ECS サービスが ALB/NLB の複数LBに接続できるように ECS GA (2015-04)
から4年後に対応 公開用ALBと内部用ALBを分けるユースケースに対応 管理用ALBは内部ネットワークからのみアクセス可能、など
複数LB対応 - API レベルでの変更点 ECSサービスの設定で loadBalancers[] に複数LBのターゲットグループを指定可能に ECS 最初のリリース時から loadBalancers
は配列なので構造は変更なし "loadBalancers": [ { "containerName": "nginx", "containerPort": 80, "targetGroupArn": "arn:aws:elasticloadbalancing:****" }, + { + "containerName": "nginx", + "containerPort": 80, + "targetGroupArn": "arn:aws:elasticloadbalancing:****" + } ]
複数LB対応 - ecspresso / Terraformでの対応 ecspresso :「何もしない」で対応完了 ECSサービスの loadBalancers は元々配列なのでSDKも変更不要
Terraform : load_balancer 要素の最大値=1を削除しただけ https://github.com/hashicorp/terraform-provider-aws/pull/9411 テストとドキュメントも更新 // aws/resource_aws_ecs_service.go "load_balancer": { Type: schema.TypeSet, Optional: true, ForceNew: true, - MaxItems: 1, // この制限を削除 Elem: &schema.Resource{ Schema: map[string]*schema.Schema{
複数LB - AWS Copilot CLIでの対応 Copilot CLI の GA は
2020-01 (v0.1.0) 誕生時点で ECS 自体は複数LBに対応していた しかし Copilot はずっと複数LB非対応のまま…… Problem create another loadbalancer #3013 - 2021-11 2022-01 NLB 対応が追加 (v1.14.0) ALBとNLBをひとつずつ使うことはできる 複数のALBやNLBを使うことはできない http: path: '/' target_port: 80 nlb: port: 8080/tcp
複数LB - AWS Copilot CLIでの対応 2023-03 単一LBの複数のリスナーに対応 (v1.27.0) 複数LBにはまだ対応できていない http:
path: '/' target_port: 80 additional_rules: - path: '/admin' target_port: 8080
複数LB - AWS Copilot CLIでの対応は最後までされなかった 2024-07 最後の新機能追加リリース v1.34.0 2024-11 メンテナンスモード入り発表
(開発終了) 2025-04 最後のリリース v1.34.1(Go 1.23対応のみ) 2020-01 のGAから5年も経たずに開発終了してしまった
抽象化の種類とそれが生む制約
IaCツールに共通する「抽象化」 ecspresso、Terraform、AWS CDK、Copilot CLI すべてに共通する点 各ツールは ECS に対する変更操作を「抽象化している」 例えば ECS
の deploy は以下の API で実現される 1. ecs.RegisterTaskDefinition 変更された新しいタスク定義を登録 2. ecs.UpdateService サービスを新しいタスク定義に更新 3. ecs.WaitUntilServicesStable サービスが安定するまで待機 (厳密には API ではなく SDK のユーティリティ関数) 構成変更を反映するために必要になる手続きを隠蔽している
IaCツールに共通する「抽象化」 ecspresso, Terraform, AWS CDK(L1)は ECS(タスク定義、サービス)の構造は「抽象化していない」 AWS CDK(L2)は簡潔で安全な記述を可能にするが 「構造は変えていない」
どれも同じ概念を構造として表現している (タスク定義の例)
ところが AWS Copilot CLI は… アプリケーションの構造全体を(LB などの外部リソースを含めて)「抽象化している」 ECSが持つタスク定義、サービスなどの概念は隠蔽されている Copilot CLIのサービス
!= ECSサービス
Copilot CLIの「構造の抽象化」がもたらした制約 1サービスには1LBのみ接続可能というDSL設計 → 複数LBには最後まで対応できなかった http: path: '/' 「簡単な記述ですべてを自動構築する」という設計思想 vs
「現実にECSで運用されるサービス」=ユーザーが任意に構築したアプリケーション 現実のアプリケーションが必要とする複雑さは「簡単な記述」でカバーしきれない
すべてを抽象化して扱う難しさ 例: Copilot CLI では CDN / TLS終端 (CloudFront +
ACM) も自動構築できる cdn: terminate_tls: true [Feature Request]: Configure SSL policy of the CDN (cdn.ssl_policy) #5932 # こう設定したいという要望 cdn: ssl_policy: TLSv1.2_2019 workaround は CFn YAML パッチを使うこと (抽象化とは…?) # copilot/environments/overrides/cfn.patches.yml - op: add path: /Resources/CloudFrontDistribution/Properties/DistributionConfig/ViewerCertificate/MinimumProtocolVersion value: TLSv1.2_2019
すべてを抽象化して扱う難しさ - Copilot CLI ストレージ対応の例 manifestで管理できるもの = EFS, Ephemeral Storage,
SNS, SQS CFnテンプレートがサービス/環境スタックに直接組み込まれる addonで対応するもの = S3, DynamoDB, Aurora Serverless copilot コマンドがCFnテンプレートを自動生成 非対応 = ElastiCache, OpenSearch Service, etc. copilot/{service}/addons/ に手動でCFnテンプレートを作成する どれも Copilot がサポートしていないカスタマイズは CFn の層で対応するしかない 現実世界のアプリケーションを相手にすると抽象が破れて具象が漏れてくる (CDK Overrides という手段もあるけど、それなら最初からCDKで良いのでは)
The Law of Leaky Abstractions - 漏れのある抽象化の法則 "All non-trivial abstractions,
to some degree, are leaky." すべての非自明な抽象化は、程度の差があれ漏れがある Joel Spolsky (2002) https://www.joelonsoftware.com/2002/11/11/the-law-of-leaky-abstractions/
2021年時点の自分の感想 https://x.com/fujiwara/status/1382504564327288835
ecspresso の抽象化アプローチ
ecspresso が抽象化するもの ECSに対する操作 (deploy, rollback, etc)は抽象化することで便利に使える ecspresso deploy 開発当初は ECS
Rolling update のみサポート (2017) CodeDeployを使った Blue/Green デプロイが可能に (2019) ECSネイティブ Blue/Green デプロイにも対応 (2025) ecspresso rollback 開発当初は ECS にロールバック機能なし(2017) 「ひとつ前のタスク定義に戻す」操作で独自に実現 CodeDeployを使った Blue/Green デプロイのロールバックに対応 (2019) ECSネイティブ Blue/Green デプロイにも対応 (2025) ECS API 側にロールバック用 API が追加されたのでそれを利用
ecspresso が抽象化しないもの ECSの構造 (タスク定義、サービス) は抽象化しないことでコストを最小化 JSON / Jsonnet でほぼそのまま記述 ドキュメントは
AWS 公式を参照すればOK 細かい機能にもすべてアクセスできる 結局実運用で必要になるのは細かい機能 例えばタスク定義の ulimits は Copilot CLI は非対応 AWS SDK / API の変更に迅速に追従 最小限の開発リソースで対応可能 実世界で「ユーザーが構築するアプリケーション」は多様で複雑 あえて構造を抽象化しないことが明確なメリット
構造を抽象化せずに使いやすく - 構成ファイルを自動生成 ECSの生の構造はかなり複雑 ← 人間が一から書き起こすのは大変 ecspresso init --service myservice
AWSに存在している実リソースから構成ファイル類(JSON/Jsonnet)を自動生成 直後に ecspresso deploy でもう始められる 最初の一歩「デプロイ時にイメージタグを書き換えたい」 1. "image":"foo:{{ must_env 'IMAGE_TAG' }}" に書き換え 2. IMAGE_TAG=xxx ecspresso deploy これだけで既存ECSサービスを ecspresso で IaC 管理可能に 「構成ファイル」↔︎「実リソース」の双方向変換可能 なのが重要
構造を抽象化せずに使いやすく - Jsonnet + native functions Jsonnet(jsonnet.org): JSONのスーパーセット。より簡潔・強力な記述が可能 ただし、最終的に生成するのはあくまでJSON
構造を抽象化せずに使いやすく - Jsonnet + native functions 人間が書きやすく読みやすい = ecspresso にとっての
DSL 人間は Jsonnet で柔軟に記述 JSON に変換して AWS SDK / API に渡す Jsonnet は生の JSON も扱える = 既存の JSON + テンプレート記法もそのまま使える 互換性維持、段階的な学習、移行が可能
DSL やりすぎになりがち問題への対処 DSL/汎用言語でコード化 → 複雑な構造も柔軟、簡潔に記述できる コード化による抽象化はついついやりすぎる → 読めない、保守が困難 ecspresso での解法
「JSONを escape hatch(非常口)にする」 ecspresso render (taskdef|servicedef|config) 1. Jsonnet + native function で記述した構成ファイルを読み込んで解釈 2. 生の JSON (APIに渡すもの) で標準出力に出力 複雑化したコードをリファクタしても 同一のJSONを生成できれば結果が同一なことを保証できる
安心してデプロイ/リファクタリングができる ecspresso diff JSONレベルでの実リソースとの差分を表示 $ ecspresso diff --- arn:aws:ecs:ap-northeast-1:123456789012:service/ecspresso-test/nginx-local +++
ecs-service-def.json @@ -38,5 +38,5 @@ }, "placementConstraints": [], "placementStrategy": [], - "platformVersion": "1.3.0" + "platformVersion": "LATEST" } --- arn:aws:ecs:ap-northeast-1:123456789012:task-definition/ecspresso-test:202 +++ ecs-task-def.json @@ -1,6 +1,10 @@ { "containerDefinitions": [ { "cpu": 0, "environment": [], "essential": true, - "image": "nginx:latest", + "image": "nginx:alpine", "logConfiguration": { "logDriver": "awslogs",
「勝ちに不思議の勝ちあり、負けに不思議の負けなし」 松浦 静山(まつら・せいざん=1760年3月7日~1841年8月15日) 野村克也監督の座右の銘としても有名 「ecspresso の設計思想に至る道」は組織構造から偶然生まれた 「あえて構造を抽象化しない設計」は、実は怠惰ゆえの産物 ECSの複雑な構造体を抽象化するのは面倒なのでサボった、という意識(当時) しかし結果的には 「ecspresso
が8年間少ないリソースで継続開発でき、価値を発揮している」要因 (2020年に設計思想を言語化したときに気づいた) → 不思議の勝ち
「勝ちに不思議の勝ちあり、負けに不思議の負けなし」 Copilot CLI の設計思想は「開発者体験の簡素化を最優先」 Opinionated = ベストプラクティスに基づいた「正解」を提供 しかし「すべてを抽象化して扱う」設計思想がゆえに 新規構築は簡単だが、移行は困難 複雑なアプリケーションにDSL内で対応しきれない
抽象(DSL)が破れたところから具象(CFn)が漏れ出す 現実のユーザーのニーズを満たせなかった? 特にエンタープライズ領域は制約が多い 他の(生き残った)IaCツールとは抽象化アプローチが根本的に異なっていた
まとめ - 「正しい抽象化の探求」 「どこで切るか」が設計の本質 責任分界点を明確にする ecspresso は ECS 以外を相手にしなかったのが成功の要因 操作は抽象化してもよい、構造は抽象化するコストが高い
現実の複雑さから目を背け続けることはできない 「すべてを抽象化して簡単に」80%をカバーしても、20%には対応できない escape hatch は重要 DSL / 汎用言語はやりすぎになりがち いつでも生の構造に戻れる / 差分が見えると安心 生殺与奪の権を他人(が作ったツール)に握らせない
None
GitHub Sponsors で ecspresso 他の開発を支援していただけると嬉しいです https://github.com/sponsors/fujiwara