大量のファイルを一気に消したい時はxargsを使う

先日、テスト実行で生成された大量のログファイルを消そうとしてうまくいかず、少し時間を使ってしまったので書いておく。仮に log0000.json から log9999.json まで10,000ファイルあるとする。
これらを一気に消そうと思い普通にrmを使ったところ、実行に失敗した。

$ rm testsort_*.json
argument list too long: rm

これはエラーの通り、アスタリスクを展開したときのコマンドが長くなりすぎて、システムで処理できないということ。どうすればいいか悩んだが、ファイルを1つずつ削除すれば良い話。
例えばxargsを使えば以下のようにできる。

$ find . -name "log*.json" | xargs rm

(2020/11/16: 以下部分の誤った記述を修正)
xargsは、標準入力として受け取ったデータを1つずつ逐次実行していく。そのため、実行中にディレクトリを覗くとファイルが除々に減っていくのがわかる。forループを使っても良いが、その場合 find コマンドの実行が終了してからでないと削除に移らない。時間がかかる処理の場合正しく進行しているのかわかりにくいため、個人的にはxargsの方が好み。

上の話は xargs -I の場合。デフォルトのxargsは、受け取った全ての引数をまとめてコマンドに渡す。ただし、コマンドが取れる引数の数の限界になるまで引数をセットしたらコマンドを1回実行する。余った引数は、2回目、3回目のコマンドに渡されて実行される。
このため、今回のように大量のファイルを消す場合、可能な限り最大数の引数を取ってrmが何度か実行される。実行中にディレクトリを覗くとファイルが除々に減っていくのがわかる。xargsを使うことで、rmの実行回数が最小限に抑えられ、パフォーマンス的にも良い。
以下、xargsのmanより。

The command line for command is built up until it reaches a system-defined limit (unless the -n and -L options are used). The specified command will be invoked as many times as necessary to use up the list of input items. In general, there will be many fewer invocations of command than there were items in the input. This will normally have significant performance benefits.

styled-componentsが反映されないときは詳細度を調べてみよう

React + TypeScriptで作られたコードにstyled-componentsを適用する作業を進めている。基本はSCSSファイルで指定されたデザインをそのままstyled-componentsに移すだけなのだが、正しく移植したはずなのにデザインが乱れることが最近よく起こっていた。

//元のscssファイル
input.hoge {
    float: left;
    width: 50%;
    margin-right: 10px;
}
//styled-components
const Hoge = styled.input`
    float: left;
    width: 50%;
    margin-right: 10px;
`;

開発者ツールで見てみると、外部のCSSライブラリでもinput要素のデザインが指定されていた。

input[type=text] {
    width: 100%;
    ....
}

そして、SCSSファイルを使った場合は、SCSSファイル内で定義した width: 50% が正しく反映されるが、styled-componentsにした場合は外部CSSライブラリの width: 100% が反映されてしまうことがわかった。

これは、styled-componentsでは通常 .123abc のようにランダム生成されたクラス名1つによる指定として style タグをhtmlに挿入するためである。これに対し、今回のSCSSファイルでは input.hoge のように、クラス名のみならずinput要素も指定されていた。このため、styled-components化したことでCSS要素指定の詳細度が下がり、外部ライブラリの input[type=text] の方が優先されてしまっていた。
現にCSSの詳細度を計算できるサイトで調べてみると、外部CSSファイルとSCSSファイルの詳細度は同じ(->グローバルな外部CSSファイルよりも特定クラスが対象のSCSSファイルが優先される)なのに対し、styled-componentsの場合だけ詳細度が下がっていることがわかる。

f:id:Udomomo:20191106145130p:plain

対応策としては、まずstyled-components内で & を複数使い、クラスを重ねて指定する方法がある。この方法はstyled-componentsの公式ドキュメントにも載っている。

www.styled-components.com

//styled-components
const Hoge = styled.input`
    && {
        float: left;
        width: 50%;
        margin-right: 10px;
    }
`;

上記のようにすると、 & 1つごとにクラス名の指定1つに変換され、コンパイル後の style タグ内では .123abc.123abc のように、同じクラス名で二重に指定するようになる。& を3つ並べればクラス名を三重に指定できる。これはCSSの詳細度を上げるためによく使われるテクニックで、SCSSファイル内でもこの記法を使うことができる。

もう1つの方法として、ターゲットの要素の周囲にWrapper要素を作り、Wrapperのstyled-componentsの中で子要素としてターゲット要素のデザインを指定することもできる。

stackoverflow.com

ただ、マークアップ文書の中にデザイン目的のみの要素が混じってしまうので、個人的にはこの方法はあまり使いたくない。

【Python】動的計画法をやってみる

動的計画法アルゴリズムには前から興味があったが、なかなか身につかなかった。どのようなものか理解するために単純な問題を動的計画法で解いてみることにする。

動的計画法とは

動的計画法は、決まったアルゴリズムを指すものではなく、より広い概念である。動的計画法は、以下の2つの要素から構成される。 - 問題の分解: 問題を部分問題に分割し、その答えを使うことで全体の問題を帰納的に解く。 - 部分問題の結果の記録: 部分問題の答えを記録しておき、それを使うことで重複する処理をなくす。

部分問題への分解ができるような問題であれば、動的計画法が使える。今まで理解できていなかったのは、この定義を十分に押さえないまま、いろいろな問題の応用的な実装例ばかり見ていたせいだったのかもしれない。

動的計画法を使って問題を解く

AtCoderのABC049Cを解いてみる。この問題は過去問精選で紹介されていたが、まだ解いていなかった。

atcoder.jp

英小文字からなる文字列 Sが与えられます。 
T が空文字列である状態から始め、以下の操作を好きな回数繰り返すことで S = T とすることができるか判定してください。
・T の末尾に dream dreamer erase eraser のいずれかを追加する。

後ろから順に見ていくという簡単な解法もあるが、動的計画法の場合、Sのうち任意のn文字目までについて、問題で指定されている単語で作ることができるかを記録していく。n文字目までを作れるかと、その状態からn+1文字目以降を作っていくことができるかとは完全に独立した話なので、部分問題として分割して結果を記録することで、その結果を再利用することができる。

S = input()
dp = [0] * (len(S) + 1)
dp[0] = 1

words = ["dream", "dreamer", "erase", "eraser"]
done = 'NO'

for i in range(len(S)):
    if dp[i] == 0:
        continue
    for w in words:
        if S[i:i+len(w)] == w:
            dp[i+len(w)] = 1
    
    if dp[len(S)] == 1:
        done = 'YES'
        break

print(done)

dp[i] は、文字列Sのi文字目までを words の単語で作れるかを表し、作れる場合は1を入れる。スタート時のために dp[0] = 1 としておいた。
それぞれのループでは、i+1文字目から見て、最初の部分文字列が words の中の単語になっているかを確認する。例えば dream が作れれば、 dp[i+5] を1とする。こうすることで、 dp[i] が1でなければスキップして次のループに行くという処理ができる。
この処理ではループを2回使っているが、 words が定数種類しかないため、計算量は O(n) とみなせるだろう。

動的計画法の使い所

今回はごく単純な例だったが、動的計画法はより複雑な組み合わせ問題のときに力を発揮する。例えばナップザック問題のように、複数のパラメータを自由に変えて最適な組み合わせを求めるような問題の場合、 部分的な最適解をメモしておけば、効率的に解くことができる。パラメータを1つずつ変化させていったとき、同じパラメータの値の組み合わせが異なる場所で登場するような問題であれば、なおさら再利用の効果が高い。

Brewfile作成の際に知っておきたいHomebrewまわりの機能

自分のdotfilesをメンテナンスするにあたって、他の人のを参考にしていたら、Brewfile上で知らないコマンドがいろいろ使われていたのでまとめる。これらをうまく使えば、インストール作業の自動化の幅が広がりそうだ。

Tap

TapはHomebrewに登録されていないライブラリを、各ユーザがbrewコマンドで管理できるようにするための機能。 brew tap <user/repo> で、GitHub上にあるライブラリを登録できる(このコマンドの後にURLを指定すれば、GitHubレポジトリ以外のライブラリも登録可能)。 brew tap コマンドで、自分が登録したTapの一覧を見ることができる。
dotfiles上でよく見るのは、 後述のCask-versionsやCask-fonts、後は heroku/brew の登録あたりだろうか。 github.com

Cask

オープンソースではないソフトウェアやアプリケーションの中にも、CaskとしてHomebrewで管理されているものがある。例えば brew cask install slack とすればSlackをインストールできる。
これを使えば、App Storeからダウンロードしたり、Applicationフォルダにアイコンをドラッグしたりする手間がないので、自動化がはかどる。dotfilesを作る際はぜひ使っておきたい。
github.com

Cask-versions

Caskのバージョン管理をするためのプロジェクト。デフォルトの機能ではないので、使うには brew tap homebrew/cask-versions としておくことが必要。
これを使うと、Caskとして公開されているアプリケーションの一部について、古いバージョンやBeta版をインストールできるようになる。 github.com

Cask-fonts

Caskと同じように、fontをインストールできる。fontの設定を手動でやるのは面倒なので、使いたいfontがある場合はぜひこれを試したい。Cask-versionsと同様に brew tap homebrew/cask-fonts しておくことが必要。 github.com

mas

masはmacApp Storeを操作するcliツールである。これを使えば、Caskに登録されていないアプリケーションも自動でインストールできるようになる。XCodeをmasで入れている例もあった。
masはHomebrewのプロジェクトではないが、Homebrewと連携しており、 brew install mas をしておけば、Brewfile内で使うことができる。 github.com

この辺りを知っていれば、端末を変えたときの初期設定時間が数十分の一になりうる。今の端末が使えるうちから、設定の自動化のためにいろいろ整備しておきたい。

AWS Budget + AWS Chatbotで使用料金のアラートをSlackに飛ばす

以前EKS上のClusterを一月以上に渡って止め忘れ、大変な請求が来てしまったことがある。

二度とこのような悲劇を起こさないよう、AWSの料金が高くなってきたらSlackに通知が来る仕組みを作ることにした。

AWS Chatbotについて

aws.amazon.com

AWS Chatbotは、7月にβ版が公開されたばかりの新しいサービス。これを使うと、SlackにAWSリソースについての情報を飛ばすことができる。今まではSlackに情報を送るためには自前でLambda関数を作ってCloudWatchと連携させるしかなかったので、とても期待が持てる。
上のページにもあるが、AWS Chatbotにメッセージを届けるには、メッセージを発信するサービスとAWS Chatbotとの間にAmazon SNSを介する必要がある。Amazon SNSはメッセージのpub/subサービスで、メッセージ送受信のインターフェイスを担ってくれる。

Amazon SNSの設定

まずSNSトピックを作成しておく必要がある。

docs.aws.amazon.com

トピックを作成したら、AWS Chatbotの設定に進む。画像ではサブスクリプションとして https://global.sns-api.chatbot.amazonaws.com が指定されているが、これはAWS Chatbot側でこのトピックを指定すれば自動で入るので、ここで設定する必要はない。

f:id:Udomomo:20191014203555p:plain

注意として、Amazon SNSのアクセスポリシーには、 Id に一意の識別子、 Resource にそのトピックのARNを指定しておく必要がある。自分はこれを忘れて通知が飛ばず詰まってしまった。
また、現時点でAWS ChatbotはAmazon SNSのコンソールからのメッセージ送信には対応していない。送信をテストしようと思っても飛ばないので注意。

AWS Chatbotの設定

次にAWS ChatbotとSlackを連携させる。 Configure new client を押すと、Amazon ChimeかSlackを選択できる。Slackを選択すると、今ログインしているSlackのワークスペースと自動で連携する設定ができる。(当然だが、自分がそのワークスペースのadminユーザである必要がある)
その後、どのチャンネルに飛ばすか、どのSNSを購読するかを選択する。

f:id:Udomomo:20191014201433p:plain

AWS Budgetの設定

最後にAWS Budgetでアラートを設定する。アラートの飛び先として、先程作成したSNSトピックのARNを指定すれば良い。SNSトピックが正しく設定できていないと、ARNを指定しても無効である旨のエラーが出てしまう。
なお、飛び先としてメールアドレスも指定しないと先に進めないようだ。

f:id:Udomomo:20191014203349p:plain

試しにアラートの基準を予算の5%到達に設定したところ、無事にSlack(とメール)に通知が届いた。通知はリアルタイムではなく、今回の場合数時間後になって届いた。

f:id:Udomomo:20191014204058p:plain

今回の作業を通じて、SNSやChatbotだけでなく、ポリシーについての理解も深めることができた。今後はアラートを予算の80%くらいにして運用していくつもり。

YAMLでハッシュの入れ子を作りたい時

先日CircleCIの設定ファイルを書いていたとき、思うように読み取ってくれず詰まったのでメモ。

YAMLのインデントは、ファイルの中で統一されてさえいれば任意の数で良いのだが、一つ意識しなければいけない場合がある。
下のようなyamlを書くと連想配列とみなされ、 yamlHello が並列のキーとして扱われてしまう。

- yaml: 
  Hello: "Hi"
[
  {
    "yaml": null, 
    "Hello": "Hi"
  }
]

自分がやりたかったのは {"Hello": "Hi"}yaml の値にすることだった。
このように、リストの中にあるハッシュのキーに対し、さらにハッシュを値として持たせたい場合は、以下のようにもうひとつ深いインデントをつける必要がある。

- yaml: 
    Hello: "Hi"
[
  {
    "yaml": {
      "Hello": "Hi"
    }
  }
]

特にサービスの設定ファイルなどではネストが深いデータ構造を書かされることが多いので、覚えておくと良い。

スクラム未経験者が初めてスクラムチームに入るときに必要な考え方

先月から、スクラムチームに入って仕事をするようになった。スクラムというやり方自体が会社としてほとんど前例のない取り組みであったため、うまくいっている所もあれば、チーム外に非常に迷惑をかけてしまっている部分もある。
個人的には、1人とか2-3人での仕事しかしたことがなかったため、スクラムの考え方が今までの仕事での考え方と大きく違うことに驚かされた。事前にスクラムガイド等は読んだものの、実際にスクラムを回してみて改めて分かったことも多いので、それをまとめておく。

なお、今のスクラム開発チームにおける詳細な進め方やアジェンダは書かない。スクラムは経験主義に基づいており、細かな手順に従うのではなく、各プロジェクトやチームの状況によって進め方を柔軟に変えることが良いものとされている。そのため、一つの進め方を記録してもあまり意味はない。
また、プロダクトオーナー(PO)の考え方・責務の話は除き、スクラムの開発チームでの考え方のみを書いている。

まず読むと良いもの

以下の資料を参照して基本的な用語の意味などを理解しておくと、スムーズに入りやすくなる。特にアジャイルプリンシプルとスクラムガイドは必須。

スクラムにおける開発チームの責務

  • このドキュメントでの「スクラムにおける開発チーム」とは、開発を行うメンバーとスクラムマスターを指す。プロダクトオーナーは含まない。
  • スクラムにおける開発チームは、チーム外に対して以下のことを担保する。

    • 自己組織化:
      • チーム外から何も指示されなくても、ミッション達成に向けて何をどのように行うべきかを自分たちで判断し実行できる。仮に判断が間違っていても自分たちで気づいて修正できる。
    • チーム内での作業の完結:
      • 必要な職能を持つ開発者が揃っており、開発・リリースを全てチーム内で完結させられる。
    • プロダクトの完成:
      • 各スプリントごとに、特定の機能や修正が反映され動かせる状態のプロダクトを作り切ることができる。単に動けば良いのではなく、考えうる限り最良の仕様・設計に落とし込んで作ることが求められる。
    • 透明性:
      • チーム内の進捗や、今どのような作業をしているか・どのような問題が起こっているかについて、POやチーム外に緊密に連携する。
  • スクラムにおける開発チームは、本来は以下のことは担保しない

    • 要件の内容:
      • 何を開発するかは、本来はプロダクトオーナーの責務。
    • 納期/スコープ :
      • どれだけの速度で開発できるかは、やってみなければわからないというのがスクラムの立場。無理に締め切りに合わせようとすると、開発プロセスに歪みが生じる。そのため、スクラムチームは納期に責任を負わない。スクラムの状況に応じて納期やスコープを調整するのは、本来はプロダクトオーナーの責務。
        • 進捗が遅いときにスクラムの回し方を変えたりスコープの変更を相談したりするのは良いことだが、締め切りに合わせようとして無理をするというソリューションは取るべきでない。
        • そもそもアジャイル開発の12の原則の中には「持続可能性」が定義されている。これは、一定のペースを維持して開発するサイクルを作ることで、開発の不確実性を減らすという意味がある。そのため、スクラムが1スプリントで消化できる以上のタスクを無理やり詰め込まれると、そのチームがスクラム・ひいてはアジャイル開発を維持できなくなる。
      • このままでは間に合わないのにどうしても締め切りもスコープも変えられないのであれば、スクラムという形式を中止すべき。
    • あらかじめ決められた水準以上のパフォーマンス:
      • 「経験主義」「持続可能性」の考え方のもとでは、1スプリントでどれだけのストーリーポイントを消化できるかを事前に予測したり、コミットメントしたりするのは不可能。1-2サイクル回してみて実績を測ることで、初めて1スプリントの消化量を計算し、スケジュールの見込みをチーム外に共有できる。逆にそれまでは、いつまでに開発が終わるかをチーム外に対して約束することはできないし、すべきでない。
      • 逆に、自分たちの見積もりに基づいて1スプリントごとにどこまで終わらせるかを決める時は、それを守る責任が発生する。

スクラムマスターの責務

  • 理想的には、スクラムマスターはスクラムが円滑に回ることにのみ責任を負う。
    • スクラムの円滑な実施を阻害するものであれば、あらゆることに対して対応する。(例: 仕様の細かい部分がわからない、技術力の差がありすぎてついていけないメンバーがいる、特定のタスクで時間を取られている等)
    • 逆に、プロダクトの仕様やリリースの期日などに対しては、本来は責任を負わない。そこに責任を負うべきなのはプロダクトオーナー。
  • スクラムマスターは以下のようなものではない。
    • 上司/チームリーダー:
      • スクラムチーム内は自律的な組織であるため、スクラムマスターが他の人に命令するという構図ではない。
    • ファシリテーター:
    • 開発者:
      • 作業に没頭してチーム全体の状況に意識が向きにくくなってしまうのであれば、スクラムマスターは開発タスクをやらなくてもよい。

スクラムに必要なもの

進め方に決まりはないが、以下のものは絶対に必要と言われている。

1. ユースケース

  • ユーザにとって何が達成されれば良いかを表した言葉。プロダクトオーナーとの間でゴールの認識を合わせるために必要。
  • ユースケースは「ユーザは◯◯することができる」という形で考えると良い
  • ユースケースは以下の条件を満たす必要がある
    • 意味が曖昧なところや、複数の解釈ができるところがない
    • チーム間での認識の齟齬がない
    • プロダクトオーナーに確認を取り合意する

2. プロダクトバックログアイテム(PBI)

  • プロダクトに必要なものの一覧。次に何をやるべきか・どこまで終わったを整理するために必要。
  • このバックログスクラム開発チームとPOの両方が確認し、定期的に優先順位を入れ替える。
  • スプリントは個々のPBIベースで進めていく。ストーリーポイントをつけるのもPBIに対して。PBIの開発が終わり、成果物に反映されたかどうかでスプリントの成果を判断する。
  • PBIをどの粒度まで落とすかは特に決まりがない。チームにとって進捗判断がしやすい粒度を模索するしかない。

3. タスク

  • 各PBIをさらに分解してできる、個々の開発タスク。
  • 1つの目安として、1タスクが30分くらいで終わるくらいの粒度に分けると良い。
    • これはあくまで目安でしかなく、本当に30分で終わったかどうかで速度を測るのはナンセンス。目的はタスクの粒度を十分細かくし、見通しを良くすること。
    • 調査系のタスクがある場合、発散しやすく、議論しながらやるのにも向かない。PBIを作る際に行うのがベスト。
  • タスクを切り終えたら、後はモブプロ形式で全員で進めても良いし、タスクを個々人に割り振っても良い。
  • タスクは「個人が責任を負うもの」ではない。状況によってはチーム内でヘルプを求めたり、アサインを他の人に変えてもらっても良い。逆に自分に余裕があるときは、積極的に他の人のタスクのヘルプに入る。

4. ストーリーポイント

  • PBIに対してストーリーポイントをつける。
    • タスクにはストーリーポイントをつけない。スクラムの進捗は「どれだけのタスクをこなしたか」ではなく「PBIが完了したか」で判定すべきであるため。
  • ストーリーポイントをつける時は、既に完了した特定のタスクを基準にするなどして、メンバー間で「1ポイント」がどれほどのものかの認識を合わせる必要がある。

5. スプリントレビュー

6. レトロスペクティブ

  • 今スプリントを振り返り、次スプリントで新たに行うNext Actionを決める。
  • Next Actionは以下の2つを満たす必要がある。
    • 誰が・何を・いつまでに行うかが明確である。(悪い例:「個人でもっと勉強する」)
    • 今スプリント内で起こった課題に対応している。
  • 課題に対する有効なNext Actionが思いつかないのなら、無理に出す必要はない。その場しのぎのNext Actionを出したりレトロスペクティブに時間を使いすぎたりするくらいであれば、Next Action無しで次のスプリントに入る方が良い。
    • 有効なNext Actionが思いつかない場合、課題の認識がぼやけている・課題解決のための材料/データが足りていない・チーム内の改善だけでは手に負えない課題である等が考えられる。
    • 「次のスプリントを同じように進めてデータを計測し、本当に課題かどうか判断する」というのも立派なNext Actionの一つ。

スクラムに入ってみて

最初は仕事に対する考え方があまりにも違いすぎてとまどうことが多かった。PBIを作る暇があったら速くタスクを進めたいとさえ思ったこともある。
ただ、下手にタスクを速く進めてしまうと、仕様に対する認識のすれ違いが発生したりして、余計な手戻りが起こりえる。チーム内での共通認識を取る・POとも合意を取るというステップをこまめに行うことで、急な要件追加や不確実性にも対応できるようにしているんだということが初めて理解できた。

一方で、最大の失敗は「プランニングの段階でそのスプリントのタスクを詰め込みすぎ、結局終わらなかった」というパターンがよく起こってしまっていることにある。スクラムでは、各スプリントでやると決めたタスクはそのスプリント中に必ず終わらせる責任が生じる。レトロスペクティブを通じて見積もりを修正するというのはもちろんだが、スクラムの成功は各メンバーのスキルにも大きく依存するということを痛感している。
1回のスプリントの期間は1週間~長くても1月ほどなので、1つのタスクをスプリントに入れるかどうかの判断を間違えると、その時点で進捗遅れが確定してしまう。その判断を正しくやるには、スクラム内で各領域の知識と技術力を持ったメンバーが正しい見通しを示さなければいけない。スクラムにマネージャーはいないので、一人ひとりが示す見通しがそのままチームの意思決定となる。
そしてこの正しい見通しを考える力が、今の自分に欠けているところでもある。判断ミスでチーム全体に迷惑をかけることがないよう、自分が知見を持っているところは念入りに調査・準備したうえでスプリントに臨むようにしたい。