SwiftSyntaxをつかったセマンティックなコード変換

TL;DR Context: internalスコープのSwiftソース定義を一括でpublic化したい場合(モジュール切出しなどの文脈) Problem: 正規表現によるコード変換では対応しきるのは難しい Solution: SwiftSyntaxのSyntaxRewriterをつかうことで、セマンティックなコード変換を比較的容易に実装できる Result: https://github.com/hrfmmr/swift-public-rewriter Context Swiftプロジェクトで、モデル類のソースを別のモジュールに切り出すといった作業が嵩むことがあり、効率的に行いたいというモチベーションがあった 単体のソースとしてはいずれも以下のようなもの struct Foo { let x: Int let bar: Bar init(x: Int, bar: Bar) { self.x = x self.bar = bar } func doSomething() { let a = ... var b = ... } } extension Foo { struct InnerFoo {} } enum Bar { case x var hoge: Int { switch self { case .x: ... } } } class Baz { private(set) var x: Int = .
Read more

テコ入れ検査可能なプロジェクト管理を考える

Context (ソフトウェア)プロジェクト管理もといスケジュール管理のあり方について 比較的規模のある開発計画を引くとき、人は全体像と着地予想を立てる上でガントチャートを引く 全体像や依存関係/クリティカルパスを把握する可視化ツールとしては有用なのは違いない 一方で、これをベースに進行管理していくと、「間に合うか/間に合わないか」でしか状況評価できない側面があり、むずかしい 想定通りに推移しない場合、「間に合わない」時点で問題が発覚しがち リカバリの初動がとりづらく、その時点で手遅れの場合も INFO->INFO->INFO->SIGSEGV🫠 👀👀👀!!? 状況共有も「ガント上のここをやっている。ここまで出来た」に終始しがちで、個別具体なタスクレベルの調整/問題解決のコミュニケーションはできるものの、スケジュール管理という観点では「ふーん」で済まされがち 「ふーん」を超えたい 「ふーん」の超え方 INFOからSIGSEGVに落ちる前でWARNやERRORを吐けること 「テコ入れの必要性」「抱え込んでいる不確実性/不安量」を観測/検査したい 2つの不確実性の見える化 a.) スケジュール不安 b.) プロジェクトの不確実性の所在 スケジュール不安の可視化 我々は順調なのかどうか、をどう検査するか バッファ消費率という考え方 CCPMにおけるバッファマネジメント 不確実性管理のために、タスクの終了期限ではなくプロジェクトバッファの管理にフォーカスを当てる タスク毎にバッファを設定せず、プロジェクト全体で1つのバッファをもつ 進捗に応じて、バッファ消費率を観測する バッファ消費率の観測のなにがうれしいか バッファ消費率の見える化によって、進行状況がgreenかyellowかredかを検査できる 赤線: redゾーン境界のバッファ消費率(完了時点90%消費) これより上にプロットされていると、危険域 テコ入れの検討を行う 黄線: greenゾーン境界のバッファ消費率(完了時点40%消費) これより下にプロットされていると、安全域 ガントチャートとの違い ガントでも「遅れている」状況自体は把握できる 早期検知できるかどうか
Read more

nginx-datadogで分散トレーシング

nginxのトレース情報を収集できるDatadog nginxモジュールのv1.0.0が最近リリースされていたので試した ref. https://docs.datadoghq.com/tracing/trace_collection/proxy_setup/?tab=nginx#nginx-with-datadog-module docker-compose.yml version: "3.7" services: api: build: context: ./api dockerfile: Dockerfile args: DD_API_KEY: ${DD_API_KEY} container_name: sample-api ports: - 8080:8080 environment: - DD_ENV=dev - DD_HOSTNAME=local nginx: build: context: ./nginx dockerfile: Dockerfile container_name: sample-nginx image: sample-nginx labels: com.datadoghq.tags.env: 'dev' com.datadoghq.tags.service: 'sample-nginx' com.datadoghq.tags.version: '0.1.0' com.datadoghq.ad.check_names: '["nginx"]' com.datadoghq.ad.init_configs: '[{}]' com.datadoghq.ad.instances: '[{"nginx_status_url": "http://%%host%%:81/nginx_status/"}]' com.datadoghq.ad.logs: '[{"source": "nginx", "service": "sample-nginx"}]' volumes: - './nginx/nginx.conf:/etc/nginx/nginx.conf:ro' ports: - "8888:80" environment: DD_AGENT_HOST: datadog DD_TRACE_AGENT_PORT: 8126 datadog: image: datadog/agent:7 container_name: sample-ddagent environment: - DD_API_KEY - DD_SITE=datadoghq.
Read more

プロジェクト進行を検査する問い

あるプロジェクト進行をペースメーキングする際に、プロジェクトステータスを検査する問いのセットをもって定期観測するやり方がわりと役立ったのでメモ (※ここでいうプロジェクトはソフトウェア開発プロジェクトで、チーム内に閉じる程度の小中規模のものを想定している) 問い プロジェクトの背景や目的は言語化されているか? (プロジェクト進行上の意思決定の土台となるものはちゃんとあるか。eg. 「このprjの本質はxxxだから今回のフェーズではyyyはスコープから外しても」な判断基準の素地) テクニカルなドキュメンテーションがされているか?(Design Docs他。非prjメンバが非同期でキャッチアップ可能なドキュメントとして整備されているか) 可視化されたタイムラインは?(milestone粒度は可変要素に都度影響されない程度のサイズか。大きすぎず細かすぎないか) 「(タイムラインの)これって、具体的に何をするイメージか」(どんな準備が必要になってくるか、プロアクティブな動きとしていつの時点で何が必要になってくるか、イメージもててるか?) 進行過程で、milestone自体に過不足は生じてないか?(フェーズの単位で漏れがあるとつらい) 現時点における、不安要素(削減しておきたい不確実性/リスク)を挙げるとしたら? 直近のmilestoneは?(「ddまでにxxxになっていること」) 現況をワンセンテンスで表現すると?(「xxxなコンテキスト下で、yyyな状況」as-is/to-be) プロジェクトにおけるおよそのvelocityは?(eg. 単位(チケット|ストーリー)消化あたりの着手からPRマージまでのリードタイム) 直近milestoneを守れそうか? green/yellow/redでいうと? その心は? 定性観点(「xxxな不安要素がある/ないから」「なんとなく」) 定量観点(「velocityの推移と残タスクから逆算するとxxxだから」) (if yellow | red: 何がどうなると、greenに近づくとおもうか?) 現時点において、調整/リカバリ/milestone見直しの必要性は? (if need: その心は?) 運用と観点 daily~weekly等で定期観測する 基本的に当初想定通りにいかないのが現実 中盤に差し掛かるまでに、前半戦でなるたけ不確実性/不安要素となっている部分(=リスク)を削減していくことを意識してみていく 確信度が低いところからスタートして徐々に上げていく(上がっていかないのはマズいぞという見方) ステータスの変化(green/yellow/red)を捉える ステータス遷移の機序(「なにがどうなったからそうなったのか」)を言語化する プロジェクトの振り返りのタネ 「(漫然と)やっていた結果おわりませんでした」はナシ 早期検知と調整/リカバリのムーブがないと、信頼を失う 信頼がないと、説明責任やそのためコストがのしかかってくる状況(=プロダクトに向き合えないオーバヘッド)を生み出したりする 「できなかった」を回避するために見積が保守的になりすぎるのも健全でない(プロダクトが主語になってない) 信頼を獲得できるだけのチームcapabilityと得られた信頼を土台に本質に向き合える素地を整えること 正直ベースの見積の下、(対処すべき)不確実性に向き合った結果、 「(スコープ | デリバリ)調整が必要になった」 「youたちがそういうならそうなんだろう」(=説明コスト不要で成立する信頼) あわせて読みたい
Read more

忘れたくない予定のための通知

という巷によくありそうなモノをつくった。 kidsを保育園に預けていて、定期的に園からのおたよりで「mm/ddまでにxxxをもってきてほしい/つくってきてほしい」といった要求が届く。「歯磨きするから歯ブラシをビニールケースに入れて」「箸を練習していきます。箸を箸袋に入れてそれを昼食袋に」「プール開きに伴いxxx, yyy, zzzを入れたプールバッグを斯々然々..」といったもの。 弊家は家庭ナレッジをすべてScrapboxに集約していて、園からのおたよりもまた例によってScrapboxにアーカイブされている。というところで、記録は問題なくできている一方で、記憶がトぶことがしばしばあり、dueまでに指定のモノを用意できない/持参し忘れるということがしばしばあった。 カレンダに予定を入れておいてもなにかと忘れ、都度Slackのリマインダ通知を設定するのも作業的に面倒くさくてやってられない…と業を煮やした末、「対応を忘れないようにするための通知」を自動化しようとおもった。 (タスク管理といえばタスク管理だから専用のツールで管理という選択肢はあるのだけれども、家庭で扱うツールはなるたけ増やしたくないという事情もあった。ツール増やした先でコメント機能なんかがあるとコンテキストやナレッジが分散しがちでそれは避けたいなど) それ系のGoogleカレンダ+Slackなインテグレーションはおあつらえのものがきっとあるとおもって探したが、ほしかったものはなさそうだったので自前でつくることにした。 つくったもの AWS Lambda+EventBridgeベースのスケジューラ EventBridgeでLambdaを定期実行するスケジューラを定義 LambdaはGoogleCalendar APIを叩いて向こう1週間分の指定カレンダの予定を取得 1日後/3日後/1週間後にあたるイベントを選別し、指定Slack channelに通知 Slack通知サンプル Slackアプリのアイコン画像は以下のような呪文でStable Diffusionで生成したらかわいいのが出来た。生成AI便利。 Cute robot holding a calendar in its hand and reading it out in Monet style Lambda(Pythonランタイム)本体のコードとしては以下のようなもの import datetime import json import logging import os import re from collections import defaultdict from dataclasses import dataclass from enum import Enum from typing import Optional import boto3 import slack_sdk import slack_sdk.
Read more

TerraformのECS定義をecspressoに移行する

Context AWSコンテナ設計・構築入門を読んだ続きとして、 ECSリソースについては他AWSリソースとライフサイクルが異なるとして、ECSはTerraformではなくecspressoによる構成管理を行うとよさそう モチベーション TerraformでECSリソースを管理しようとすると、以下のような課題がある a.) TerraformでECSタスク定義を管理する上での課題 aws_ecs_task_definitionを更新すると、タスクリビジョンの積み上げではなく差替(replacement)の差分が生じる リビジョンを戻すということができない rollback相当のことをしたい場合、前回のタスク定義でまたreplacementする差分をapplyする 課題 リビジョンを遡ることができない 過去のリビジョンを(gitから)探し出すのが手間 迅速なrollbackができない rollbackは思考を自動化した手順で行いたいが、それを妨げる b.) TerraformでECSサービスを管理する上での課題 Auto Scalingによってdesired_countの変更が起き得る aws_ecs_serviceで定義しているdesired_countは、たとえばaws_appautoscaling_target定義によるAuto Scaling発動によって、terraform applyを介さずに状態が変わり得る -> Terraform定義と稼働しているECSサービスとの間に差分が生まれる 課題 定義と実態で差分がある状態でterraform applyが実行されると(定義自体に差分は生じず)、Auto Scalingによるスケールアウトを意図せず打ち消してしまうなどが起きる ignore_changesに含めて意図しない「修正」を行わないようにすることは可能だが、本来のdesired_countを変更したいときなどはignore_changes外す必要があり、トリッキーな対応を要する いずれにせよ、applyが意図した収斂になるか判別できない(tfstateが最新状態を反映しているかが不明である以上は) ecspressoによるECS管理 ecspressoは何を解決するか a.) Terraform ECSタスク定義のリビジョンが残らない/rollback問題 タスク定義の登録(register)や実行(run)が個別で行える ECSサービスのデプロイ(deploy)で、“最新リビジョンのタスク定義を参照(新規登録しない)“オプション(--latest-task-definition)の付与が可能 -> タスク定義登録前のリビジョンへのロールバックが可能(rollback ) b.
Read more

AWSコンテナ設計・構築[本格]入門を読んだ

AWSコンテナ設計・構築[本格]入門 を読んだ コンテナオーケストレータとしてはKubernetesは馴染みがあったがECSはなにもわからない勢だったので、わかりたいモチベーションで本書を手にとった Terraformも何気に触れてこなかったので、その学習も兼ねて本書で扱うシステム構成をTerraformで構築していってみた Terraform化で扱った構成図(Well-Architectedにはしきれていない..) ※以下は単位AZの図(構築するものはマルチAZ構成) github repo: https://github.com/hrfmmr/sbcntr-resources/tree/terraform/terraform 書籍と異なる部分 bastion パブリックサブネットに踏み台サーバを置いている 書籍ではVPC内で行う必要のある作業(Internal ALBへの疎通確認やDB seedingなど)は、Cloud9環境で行う内容となっていた が、個人的には再現性犠牲にしてもローカルマシンから環境構築作業を行いたかったので、VPC内で行う作業用に踏み台EC2を建てる構成にした network 書籍ではコンテナからのアウトバウンド通信はNATゲートウェイを介さずすべてVPCエンドポイントで解決していた が、NATを置く構成にしてる デプロイしたコンテナが意図通りに動作しないことがあった(frontend appからbackend appへの通信が通らないなど)ので、コンテナの中に入って調査したいことがあった その過程でdns-utilsなどのパッケージインストールを行えるようにしたかったという理由(slimイメージだと何も入っていない) 運用readyになったら不要になるやつ ecs タスクロールにECS Exec用のロールを追加してる 上述の「(Fargate)コンテナに入って調査」するためにECS Exec用のポリシーをアタッチしている secrets 書籍ではAWS Secrets ManagerでRDS DBのcredentialsを設定していたが、AWS Systems Managerのパラメータストアで代用している Secrets Managerを使う一方でシークレットの自動ローテーションをOFFにしていたので、なぜパラメータストアではなくSecrets Managerを選択しているのか気になった AWSの公式FAQ をみると、両者のセキュリティモデルに違いはないとしていて、それぞれのユースケースとしてはライフサイクル管理要件ないならパラメータストア/あるならSecrets Managerをつかうという記載があった rds DBはAmazon Auroraをつかう前提だったが、学習のためとはいえさすがにコスト嵩みそうなのが怖かったので、Aurora構成は断念した 「ECSタスクからRDSにつながる」部分を最小で実現できさえすればひとまず良しとして、シングルAZのDBインスタンスを建てるのみの構成にした 感想戦
Read more

git-syncで定期バックアップをとる

Context Obsidian vaultのback up先として、privateのGitHub repoへ置いている iOSアプリとの連携用でiCloud同期は別でやっている iCloudストレージの操作をミスっておじゃんにすることもあろうかと、git repoへもsyncしておいて安心しておきたい 日々差分が生じているが、push作業は手動でおもいついたらやっている。アドホック運用。(数日だったり1週間空くこともありがち) syncを自動daily実行されるしくみがほしい git-syncをつかうとよさそう git-syncを実行する git-syncをcloneしてPATHを通す ❯ git clone https://github.com/simonthum/git-sync.git ❯ sudo ln -snfv `pwd`/git-sync /usr/local/bin/git-sync Obsidian vaultのあるディレクトリへ移動 ❯ cd /path/to/ObsidianVault git-sync用のgit config設定 ❯ git config --bool branch.main.sync true ❯ git config --bool branch.main.syncNewFiles true .git/configに以下が追加される [branch "main"] remote = origin merge = refs/heads/main + sync = true + syncNewFiles = true git-sync実行 ❯ git-sync git-sync: Preparing. Repo in .
Read more

Obsidianでタスク管理する

というのがこの頃個人的最適解となっているのでそれについて整理してみた TL;DR Obsidianつかうとタスク管理とナレッジ管理を圧倒的操作性の下で完結できてうれしい 前提 タスク管理について述べていく前にまず前提として「タスク」が指すものについての補足 以下で述べられる「タスク」は基本的にソフトウェアエンジニアリング的なことを行う過程で発生する「技術的なタスク(実装事/リサーチ事他)」を想定している 「タスク管理」のスコープとするところ 主体は 個人に閉じた話(チームで何らか共同でタスク管理していく話とは別) スコープ 「なにをせねばならんか」「いますべきことは」「やるなかで得られた知見の蓄積」をいかに効率的に行うか、が主眼 「いついつまでに間に合いそうか」といった進捗管理的なことは関心の対象外 タスク管理の基本要素 タスク管理どうやってますかを語る上で、避けて通れない系の基本要素を整理してみると、おおまか以下がありそう a.) (タスクを)一覧する b.) (タスクを)選択する c.) (タスクを)開始/再開する d.) (タスクを)実行する タスク管理における基本要素毎の課題感 a.) ~ d.)の4項目について、それぞれの課題感を書き綴ってみると a.) (タスクを)一覧する 課題感 「TODOリスト」のつらさ わすれないようにするために人は「TODOリスト」をつくる いずれ長くなってくる リストと向き合うたびに認知負荷がかかる 本来必要なのは「いま集中して取り組むやつ」だけ それ以外のも目に入ってくる。ノイズ。 「あれやんなきゃ」とおもいついたら「リスト」に移す 「リスト化」自体がコスト 「リスト」へのコンテキストスイッチが起きる 入力が面倒 複数コンテキスト(ie. プロジェクト)のTODOアイテムを扱う必要が生じた場合どうするか コンテキスト境界でTODOリスト自体分ける? 複数のTODOリストを管理する TODOリストを行き来する必要が生じる。管理コストがつらくなる。 1枚のTODOリストで複数コンテキストを区別する?
Read more

Featureモジュール間の依存解決

Context iOSアプリ UIKitコードベースとの付き合い feature-modularizedなマルチモジュール構成をとった場合の課題 課題 Featureモジュール間の画面遷移をどう実現するか why: featureモジュール同士は依存関係をつくれない(循環参照) so: FeatureAからFeatureBの画面(VC)インスタンスを生成したい場合、globalに定義される共通の抽象インタフェースへの依存で解決する必要性が生じる VC生成にあたり、それにぶら下がる依存解決上の課題 ときに、single source of truth用途(インメモリで保持される状態アクセスなど)のsingletonインスタンスをFeatureモジュール間で引き回したい。暗黙依存でなくDI(constructor injection)の形式で。テスト容易性。 得てして、このインスタンスが実際に用いられる層はVCではなく、依存先の層(model/usecase)であるケースがほとんど 依存解決のためのインスタンスのバケツリレー 使用側(下位)のコンストラクタ(DI仕様)変更に伴う、上位側からの依存注入し直しバケツリレー変更のつらみ 依存解決やり方 モジュール内に存在する各VCを生成するためのFactoryなインタフェースをglobalに定義(eg. 各Featureモジュールが共通に依存するCoreモジュールに定義するなど) 各Featureは共通化されたFactoryインタフェースを用いて別Featureの画面を生成し、遷移先として指定する 各社の知見 cookpad社の取り組み ViewBuilderがFeature内VC生成のインタフェース Coreに定義されるEnvironmentをつかってbuild buildの中でRouter初期化時にenvironmentをDI Router内で任意のViewDescriptor(これはCoreレベルで公開)を初期化、environment.resolve(descriptor)でVCインスタンスを得る application targetで、Environment protocol準拠することで、ViewDescriptor毎のVCインスタンス生成の具象コードを実装 課題 Environmentのresolve実装するとき、Descriptorの網羅が必要 Descriptorはstaticなenumではないので、case網羅のためにコンパイラチェックの恩恵を受けられない switch caseを網羅するのに工夫が必要(実際cookpadでは存在するDescriptorのcase網羅が行われるようにコードの自動生成のしくみを構築していて、カバーされていないcaseがあればコンパイルエラーになるようにしているとのこと) しくみの構築と運用のコスト もっとラクにコンパイルタイムでの依存解決を保証するしくみがあるとうれしい はてな社の取り組み uber/needleを用いたDIコードの自動生成(≒ Dagger) 型安全なEnvironment実装生成不要でViewBuilder相当のDI定義を各Featureモジュールに寄せられる 本体app targetではRootComponentとそれにぶら下がる各FeatureComponentが必要とするFactoryを定義するだけで済む 依存解決の不足をコンパイラチェック可能に 実装How uber/needle CoreにVCインスタンス化用のBuildable定義 FeatureでComponent定義 画面に対応するBuildable型を返却するBuilderを内部で実装 app targetでRootComponentを定義 childとなる各FeatureComponentのDependencyに必要なfactory定義を施す singletonを引き回したい場合は、 NeedleFoundationのsharedでfactoryをラップすることで解決できる 図 app target RootComponent Features Feature Alpha FeatureAlphaComponent FeatureAlphaFooBuilder Feature Bravo FeatureBravoComponent FeatureBravoFooBuilder Core AlphaFooBuildable BravoFooBuildable 成果物 https://github.
Read more