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つのタスクをスプリントに入れるかどうかの判断を間違えると、その時点で進捗遅れが確定してしまう。その判断を正しくやるには、スクラム内で各領域の知識と技術力を持ったメンバーが正しい見通しを示さなければいけない。スクラムにマネージャーはいないので、一人ひとりが示す見通しがそのままチームの意思決定となる。
そしてこの正しい見通しを考える力が、今の自分に欠けているところでもある。判断ミスでチーム全体に迷惑をかけることがないよう、自分が知見を持っているところは念入りに調査・準備したうえでスプリントに臨むようにしたい。

「GitHub実践入門」でGitHub Flowを学び直した

gihyo.jp

GitHubは普段の開発でよく使っているので基本的な操作は問題ないと思っていたが、今後恥をかきたくないと思い改めて学ぶことにした。
この本はgitコマンドの基本的な使い方から始まり、GitHubのコンソールの機能の紹介、hubコマンドやいくつかのCIツールの解説(Circle CIやGitHub Actionsは載っていないが)、そして代表的な開発フローなど幅広い内容をカバーしており、これを読めばチームメンバーに迷惑をかけることはなくなるだろう。特に今回読んでみて学びが多かったのは、GitHub Flowの捉え方についてだった。

GitHub Flowに必要なこと

GitHub Flowはmasterブランチを中心に据えた開発フローであり、masterからブランチが切られ、変更がmasterに直接マージされる。この方法の特長は開発サイクルが速くなることくらいに思っていたのだが、GitHub Flowをうまく回すにはそれ以外にも条件があった。

デプロイの自動化

masterから直接ブランチを切るということは、masterが頻繁に変更されることを意味する。そしてmasterは常にデプロイ可能な状態であることが求められる。つまり、masterブランチは高速にデプロイされなければならず、それを実現できる仕組みが不可欠になる。
GitHub Flowでは、そもそも「リリース」という概念はない。1日数回のペースでデプロイが行われている企業も多い。

テストの自動化

デプロイが頻繁に行われるということは、その前のテストも頻繁に行われなければいけない。人間の手でカバーしていたら1日数回のデプロイなどできないので、テストも自動化されなければいけない。
これら2つが合わさることで、CI/CDツールが意味を持ってくる。

PRを小さくする

デプロイとテストを自動化しても、PRのレビューには人の目を入れなければいけない。もちろん文法や記法等についてはlintを入れるなどして自動化すべきだが、ロジックのレビューは必ず必要になる。ここで時間を取らないためには、PRの規模を小さくし、レビュアーが変更範囲を把握しやすくなるようにしなければならない。
レビューに時間を取られると、masterがどんどん変更されていき、それをローカルブランチに取り込んでconflict対応して...となり、ますます対応時間が増える悪循環に陥る。実際に巨大なPRを作ってしまい1か月以上経ってもマージされなかったことがあるので、この話は身につまされた。

事前に変更内容の合意を取る

開発からマージまでの時間を最小限にするには、そもそも実装が全部終わる前に確認を取ってしまえばよい。実装を始めたばかりの段階で、これからどのように変更するかの方針をPRにしておけば、もしその方針が違っていた場合にムダな実装をせずに済む。

全員の能力を底上げする

ここまではシステム上の工夫だったが、最終的にはそれぞれの開発者の能力が求められる。スキルの低い開発者がGitHub Flowを使っても、大量のレビューを食らって大きな工数がかかってしまい、高速デプロイが全然できない。身も蓋もない話だが、言われてみれば正論である。

GitHubのtipsとして今まで教わってきたことが、この本を読んで全てつながった感じがする。GitHub Flowは開発の生産性向上を目指したツールなので、ただの仕事のやり方として惰性でやるのではなく、その意味や狙いを理解したうえで使っていきたい。

【Python】heapqで優先度つきキューを理解する

先日のAtCoder Beginners Contest 141に参加したのだが、問題Dを解くことができなかった。
この問題を言い換えると、要は「数値型の要素で構成される配列において、最も値が大きいものを1/2にするという作業を繰り返し、最終的な要素の合計を求める」というものだった。そこで愚直に以下のようなコードを書いたら、あえなくTLEに。

N, M = list(map(int, input().split()))
price = list(map(int, input().split()))
total = sum(price)
for i in range(M):
    max_index = price.index(max(price))
    discount = max(price) // 2
    total -= discount
    price[max_index] -= discount
print(total)

max() で最も値が大きい要素を毎回探しているのだが、これだと配列の長さN * 処理の回数Mで、計算量がO(NM)になってしまう。
ここで使うべきだったのが、優先度つきキューである。

優先度つきキューとは

要素を挿入していってひとつずつ取り出せるところは通常のキューと同じだが、取り出す順序が先入先出や後入先出ではない。優先度つきキューでは、名前の通り各要素に優先度がついており、その優先度が高い順に要素を取り出すことができる。

ヒープの仕組み

優先度つきキューを実装したデータ構造のうち、最もよく使われているのがヒープである。ヒープは木構造の概念を持つ。木構造は木の末端のノードを除いて子ノードを持ち、親は子よりも必ず高い(低い)優先度を持っている。このようにすることで、優先度が最も高い(低い)要素を簡単に取り出すことができる。また、新たに要素を挿入するときは、親と自分を比較することを繰り返せば良い。
この木構造にも、二分探索木や平衡探索木など様々な実装がある。

Pythonのheapq

Pythonにもヒープを実装したheapqモジュールが用意されている。通常のPythonリストと同じ感覚で使えることを重視しているらしく、以下のような特徴がある。

  • indexが0始まり
  • heappop() するとヒープの中で値が最小の要素が返る

このため、Pythonのヒープaでは a[k] <= a[2*k+1] and a[k] <= a[2*k+2] が成り立ち、a[0]が最小の値を持つ。すなわち親が子よりも値の小さい要素を持つ2分探索木構造になっている。
あくまで2分探索木をリストの形で表しているだけなので、 a[len(a)-1] (リストの最後の要素)が最大の値を持つとは限らないことに注意*1

heapqの実装

heapqにおいて、新しく値を挿入するときは以下のような実装になっている。

def heappush(heap, item):
    """Push item onto heap, maintaining the heap invariant."""
    heap.append(item)
    _siftdown(heap, 0, len(heap)-1)

def _siftdown(heap, startpos, pos):
    newitem = heap[pos]
    # Follow the path to the root, moving parents down until finding a place
    # newitem fits.
    while pos > startpos:
        parentpos = (pos - 1) >> 1
        parent = heap[parentpos]
        if newitem < parent:
            heap[pos] = parent
            pos = parentpos
            continue
        break
    heap[pos] = newitem

新しい要素を挿入しうるpositionを末尾に設定し、その親にあたる要素と新しい要素を比較する。もし新しい要素の方が値が小さければ、親の要素を子階層に下ろし、親が元いた位置にpositionを移動させる、というのを繰り返している。比較の結果、新しい要素の方が値が小さいとなれば、その位置に新しい要素を挿入する。 parentpos = (pos - 1) >> 1 の部分はビット演算であり、インデックスを2進数で右に1ビットずらしている。こうすることで親のインデックスを求めることができる。

同様に、値を取り出すときは以下のような処理が行われる。

def heappop(heap):
    """Pop the smallest item off the heap, maintaining the heap invariant."""
    lastelt = heap.pop()    # raises appropriate IndexError if heap is empty
    if heap:
        returnitem = heap[0]
        heap[0] = lastelt
        _siftup(heap, 0)
        return returnitem
    return lastelt

def _siftup(heap, pos):
    endpos = len(heap)
    startpos = pos
    newitem = heap[pos]
    # Bubble up the smaller child until hitting a leaf.
    childpos = 2*pos + 1    # leftmost child position
    while childpos < endpos:
        # Set childpos to index of smaller child.
        rightpos = childpos + 1
        if rightpos < endpos and not heap[childpos] < heap[rightpos]:
            childpos = rightpos
        # Move the smaller child up.
        heap[pos] = heap[childpos]
        pos = childpos
        childpos = 2*pos + 1
    # The leaf at pos is empty now.  Put newitem there, and bubble it up
    # to its final resting place (by sifting its parents down).
    heap[pos] = newitem
    _siftdown(heap, startpos, pos)

まず heap.pop() で最後の要素をpopさせる。次にheap[0]の値を記録したうえで、その2つの子同士を比較し、値が小さい方を親の階層へ上げるということを繰り返す。
この繰り返しの末に、最も下の階層に到達したら、値が親の階層へ上がって空((正確には空になるというのはあくまで概念にすぎず、実際には元々の値が残っている。 _siftup() は、まずheap[0]の位置に最後の要素を仮で挿入しておき、そのうえで「2つの子同士を比べて小さい方の値で親の要素を上書きする」ということを繰り返しているにすぎない。))になった場所を起点に _siftdown() を始め、最後の要素を入れる位置を確定させる。これでヒープ構造を保つことができる。あとはもともとのheap[0]の値を返せばよい。

また、通常のリストをヒープソートしてヒープに変換する heapify() というメソッドも用意されている。これの実装は以下の通り。

def heapify(x):
    n = len(x)
    for i in reversed(range(n//2)):
        _siftup(x, i)

forループによって指定されている n // 2 - 1 というのは、2分探索木において子要素を持つ要素のうち、インデックスが最大のものである。すなわち下から2番目の階層から、自分とその子要素だけによる部分的な2分探索木をヒープ構造になるようにするのを繰り返している。

heapqで問題を解く

冒頭の問題をheapqを使って解くと、以下のように簡単に書ける。heappop() は最小の値の要素を返すため、最大の値を返してもらうためには-1を掛けておく必要がある。

import heapq

N, M = list(map(int, input().split()))
price = list(map(lambda x: int(x) * (-1), input().split()))

heapq.heapify(price)

for i in range(M):
    max_price = -(heapq.heappop(price))
    new_price = max_price // 2
    heapq.heappush(price, -(max_price // 2))

print(-(sum(price)))

ヒープへの要素の挿入・削除の計算量はO(logN)であり、M回繰り返してもO(logN * M)なので、処理時刻は大幅に短くなる。 heapify() の計算量も最悪でもO(N * logN)である。

*1:例えば、空のヒープに3, 2, 4, 9, 7の順で値を入れると、ヒープの中身は[2, 3, 4, 9, 7]となる

TDDワイワイ会でテスト駆動開発に再挑戦しました

tddyyx.connpass.com

最近会社のチームでTDDをやり始めたばかりなのですが、チームの先輩に誘われてTDDをやってみるイベントに参加してみました。TDD本は読んだことがあるのですが、あまり腹落ちしきっておらず、もう一度読まないといけないかなと思っていたタイミングだったので、実践で理解を深める良い機会でした。

進め方

4人1チームに分かれて、cyber-dojoのお題をやっていくというオーソドックスなスタイルなのですが、一番良かったのはワイワイ会の運営スタッフの方にファシリテーションを行っていただけたことです。TDDの進め方のスライドがあらかじめ準備されており、導入部分が型として磨かれていたので、スムーズにTDDに入ることができました。

今回のTDDはモブプロ形式を取り入れ、REDの状態からDriverが作業を開始し、最低限の実装でGREENにする->コードをリファクタリング->新しいTodoのテストを書いてREDになったら交代、というサイクルでした。以前会社のチームで一回モブプロをしたときは、上の3ステップの1つが終わるごとにDriverを交代していたのですが、上記1サイクルを1人でやる方がTDDの勘所をつかみやすくて良いかもしれません。

↑テスト結果がREDでもGREENでも、予想通りであればみんなで場をワイワイ盛り上げます。ここ大事。

Todoを整理する

今回最も学びになったのは、コーディングに入る前のTodoを処理ではなく振る舞いをもとに考えることでした。コードを書く立場としてどうしても実装単位で考えがちですが、振る舞いから考える方が仕様を網羅するという意識を持ちやすいうえ、テストケース分解・責務分解につながりやすくなります。

TDDとスクラム

今回はTDDを体験した後、他の参加者の方と「TDDをスクラム内でどう活かすか」という議論になりました。
個人的には、Todo整理にはスクラムでのプランニングに通じるところがある感じを受けました。弊社のチームの場合、実装に移る前のプランニングスプリントで要求を要件に落とし込み、PBIを作ってから細かな実装タスクに落とし込むというやり方を取っているのですが、テストを書くことはそこで決めたPBIを表現し担保するという意味合いを持っているのかもしれません。
とはいえ、スクラムの中核にTDDを置くこともまた正しくはなさそうです。DB設計など、変化することが少なくなおかつアプリケーション全体に大きな影響を及ぼすようなものは、TDDで高速改善していくスタイルには合わないでしょう。むしろ、PBIに紐づく実装タスクの方がTDDの威力を発揮できそうです。これからTDDを取り入れる場合、小さな実装タスクから始める方が失敗しにくいかもしれません。

※ちなみに今日は「誰も全く知らない言語でいきなりFizzBuzz TDDをやってみる」という猛者チームをいくつか見かけました。ドキュメントを見て書き方を議論しながら実装するというハードモードなやり方なので人を選びそうですが、ついていければ言語学習のステップを一気に駆け抜けることができる気もします。たぶん。

Builderscon 2019に参加しました

builderscon.io

金曜日にBuildersconに行ってきました。今回聞けた発表は全部で3つだけでしたが、この1年で経験が増えたためか、闇雲に部屋を回っていた去年よりもそれぞれの発表を面白く聞くことができました。とても満足しています。

はじめてのB2B SaaSデータモデリング

自分の会社でもB2B SaaSを扱っており、まさにデータベース設計に起因する課題を抱えている状態なので、とても身につまされた発表でした。ドメインマスターと対話してドメイン設計するのに1ヶ月突っ込んだのは、なかなかできない英断だと思います。また、null値を使いたくなったら設計を見直す、ビジネス上の提供価値やソフトウェアの未来を熟知したうえで判断基準にするなど、データ設計を考えるうえでの新しい指針も数多く聞くことができました。
個人的には「大手クライアントが解約時にデータを全削除のうえpdfで送るよう求めてくる」「営業がうますぎてユースケースに合わないクライアントを取ってきてしまう」など、B2B SaaSならではのあるあるも満載で楽しかったです。

コンパイラをつくってみよう

Go言語でコンパイラを作るライブコーディングでした。いくらリハーサルを積んだとはいえ、ライブなんてよほどその言語に手慣れてないとできないですね... エラーが出たときにコードの誤っている部分を指摘するお客さんも現れ、強い人の空間が出来上がっていました。
今回は数字の足し算をコンパイルするだけでしたが、これはぜひやってみたいです。Go言語のコンパイラをGo言語で書けるのはハードルが下がっていいですね。

現代フロントエンドに欠かせないWebpackとBabelを理解しよう!

WebpackとBabelの歴史や、内部の仕組みまで踏み込んだ内容でした。あまりこの辺りのことは深く掘り下げていなかったので、「知らなかったを知る」という言葉通りとても興味深かったです。 Babelのコード変換の途中で使われているASTというデータ構造は今日初めて知りました。JSをASTに変換できるウェブサイトもあるとのことなので、いろいろ試してみます。

astexplorer.net