ネットの海の片隅で

技術ネタの放流、あるいは不法投棄。

oneshot task に規約と仕組みを導入する

Cookpad TechConf 2018突発 LT で話した内容をもう少しちゃんと書いてみます。

アドホックなタスクの必要性

日々、サービスを運用しているとアドホックなデータの変更や処理の再実行などが必要になります。

典型的には以下のようなケースです。

ここではこのようなアドホックなタスクを oneshot task と呼ぶことにします。

oneshot task をスクリプトとして書く

oneshot task をスクリプトとして書くといくつかのメリットが得られます。

似たようなことをする方法はいくつかありますが、それらの方法と比べたときの主なメリットは次のようなものです。

vs. 管理画面

管理画面をポチポチすれば対応できるものによっても場合によってはスクリプトを書く価値があります。

大量のデータを処理する必要があるときにはスクリプトを書いたほうが簡単確実ですし、スクリプトであればトランザクションなども使えるのでアトミックな更新をすることができます。 また、事前条件・事後条件をチェックした上で必要に応じてトランザクションロールバックするなどより安全に配慮した処理を行なうこともできます。

vs. 手オペ

ここで手オペと呼んでいるのは本番環境で bin/rails console するとか DB に直接 SQL を投げるとかそういったものです。

サービスの最初期など場合によっては便利な手オペですが、ミスを起こしやすくリカバリしにくいので基本的にはスクリプトにしておくべきです。

スクリプトにすることによって、テストを書くことができるようになり、PR にしてレビューできるようになり、Git に残るので過去の変更内容を見返すことができるようになります。

考慮すべきこと

スクリプト化するメリットを書きましたが、スクリプトにすることによっていくつか考えることが生じます。

ファイルをどこに置くのか

Rails には oneshot task の置き場所に関する規約はないので、どこに置くかを決めておく必要があります。

いつまでも残ったら邪魔じゃないのか

一時的に使用するスクリプトリポジトリに入れることによって他のコードへの依存が発生します。 将来、依存されているコードを変更したくなったときに依存している箇所を書き換えてよいのかどうか逐一判断する必要が生じます。

ファイル名・タスク名の命名とかファイル作成とかが面倒

単純にファイルを作るのが面倒という話です。

稀によくあるタスクだと名前が衝突したりするのでぶつからない名前を考えるのが地味に面倒だったりします。

規約と仕組みをつくる

日々、スクリプトを書く必要が生じる中でいちいち上のようなことを考えるのは面倒なので規約と仕組みで解決します。

規約

置く場所を決める

決めの問題なのでどこでも良いんですが、Rails で oneshot task を書くとなると rake task が一般的だと思うので lib/tasks/oneshot/ に入れることにします。

そうすることによって、置く場所をいちいち考える必要がなくなるとともに、このディレクトリにあるファイルは(実行後であれば)消しても良いとわかるようになります。

命名規則を決める

ファイル名・タスク名の命名にも規約をつくり、規約によってファイル名・タスク名にタイムスタンプをつけるようにして YYYYMMDD_foo_bar.rake というファイル名と oneshot:foo_bar_YYYYMMDD というタスク名にします。

そうすることによって、命名がぶつかることを防止するとともに、タイムスタンプを元に消して良いかどうか判断しやすくなったり rm lib/tasks/oneshot/2017* というような一括削除が行えるようになります。

仕組みをつくる

規約をつくったので、その規約にのっとってファイルをつくっていけば良いんですが、規約通りにファイルをつくるというのは明らかに人間よりもコンピュータが得意な仕事です。

そこで規約通りにファイルを作成するための仕組みをつくります。

幸い、Rails には generator の仕組みがあって、普段よく使っている bin/rails g hogehoge を自分でつくれるのでこの仕組に乗っかって bin/rails g oneshot FooBar できるようにします。

generator については以下のページによくまとまっています。

使いまわしたいので gem にした

リンク先を読んでもらうとわかるのですが、generator の仕組みは非常にシンプルで generator をつくるのは全然難しくありません。

ただ、複数のプロジェクトで同じような generator を何度も書くのは面倒なので、使いまわせるように切り出して oneshot_task_generator という gem に切り出しました。

使い方

$ bin/rake generate oneshot FooBar

という感じで実行すると lib/tasks/oneshot/20180205_foo_bar.rake というファイルができます。中身はこんな感じ。

# Skeleton for this file was generated by `bin/rails generate oneshot FooBar`
# For copy and paste: `bin/rake oneshot:foo_bar_20180205`
namespace :oneshot do
  desc ''
  task foo_bar_20180205: :environment do
  end
end

基本的には規約通りのディレクトリに規約通りのテンプレを吐き出すだけのシンプルなものです。

カスタマイズ

gem にする前に運用していたバージョンでは oneshot でよく使うコード(e.g. トランザクション)をテンプレに埋め込んでいました。

そういった簡単なカスタマイズもできるようにしています。

# config/initializers/ohesnot_task_generator.rb
OneshotGenerator.configure do |config|
  config.body = <<-BODY
ActiveRecord::Base.transaction do
  # Write transactional code here
end
  BODY
end
# Skeleton for this file was generated by `bin/rails generate oneshot FooBar`
# For copy and paste: `bin/rake oneshot:foo_bar_20180208`
namespace :oneshot do
  desc ''
  task foo_bar_20180208: :environment do
    ActiveRecord::Base.transaction do
      # Write transactional code here
    end
  end
end

おわりに

自分が欲しいものを規約と仕組みに落とし込んで、しばらく運用してみたところ便利だったので gem にしたというお話でした。

技術的にも思想的にも別に大したことはしてないんだけど、こういう地味に便利になるみたいなのが結構好きです。

使ってみて「ここをこうして欲しい!」とか「英語がクソ」みたいなのがあれば issue なり PR なりいただければと思います。

モデリングについて

カフェで設計を考えていたらいろいろ思うところがあったので、カフェでのツイートを中心にしつつ自分向けにまとめてみる。

モデリングとは

ここで言うモデリングは「人間の営みをコンピュータで実装するために分割して名前をつける行為」くらいの意味を意図している。 また、普段書いているのが Web アプリケーションなので、その前提を含んでいる。

人間の営みを理解するためには分析や対話を通じて概念を見出していく必要がある。

そして、実装するためにはだいたいロジックとデータが必要なんだけど、良い振る舞いをするモデルを得るためにはどちらかに偏ることなく総合的に考える必要があると思っている。 プログラムにはプログラムの、データベースにはデータベースの都合があるが、どちらかに寄せてしまうともう一方の表現力がなくなったり得意なことが活かせなくなったりしてしまう。

これ自体は前から意識していたことだが、自分で思っているよりも遥かに頻繁に意識の切り替えを行なっていたことに気付いた。

良いモデリングをするためには概念を発見する力を磨くとともに、プログラムとデータベースの両方に対する知見を深めていく必要がある*1

モデリングの難しさと個人的な感覚

上の定義では「分割して名前をつけるだけ」のモデリングだが、個人的にはかなり難しいことだと思っている。

半ば冗談でよく言っているのだけど、これは人間が高性能すぎるのが悪い。

だから、直感に基いてモデリングすると、実は分割が足りなかったみたいなことがよく発生する*2

もちろん、初めからより良いモデルに到れれば良いんだけど、神ならざる身である我々が完璧なモデルをつくれるわけはないのでモデルを修正する必要が生じること自体は問題じゃない。 むしろ、現在のモデルが現実をうまく扱えないときはより良いモデルに至るチャンス。

こういった「初めから100%は無理」「少しずつ良くしていく」というような心境があって「ドメインを掘る」「モデルを磨く」といった語法を使うことが多い。

さらにポエミーなツイートを引用しておくと、こんな感じ。

モデリングの重要性

こんなにも難しいモデリングだが、モデリングを頑張ると何がうれしいのか。 大きく3つあると思っている。

ドメインに対する理解

モデルを考えていく過程でドメインを掘っていく必要がある。 この営みはだいたい「◯◯とは?」のような問いを通して概念を明確にしていく作業になるので、結果としてドメインに対する明確な理解が得られるようになる。

堅牢さと柔軟性

残る2つ、堅牢さと柔軟性は相反するように見えて相関しているのではないかという仮説が浮かんだのでまとめて書く。

明確に認識された概念に基づいたモデルは「それが何であるか」が非常に明瞭になっている。

そのため、各モデルに対してしっかりとした制約を課すことができるし、仕様変更や認識のアップデートが発生した場合にもどの箇所にどういった変更を加えるかが明白になる。

一方、適切に分割されていない貧弱なモデルを元に実装すると、柔軟性を確保するためにデータをゆるく持つことで対処する必要が生じてデータのカタさが失われるし、分割されていないモデルに対してロジックを追加していくと神モデル的な何かが生まれたりする。

難しさを伝える難しさ

ここまでいろいろと書いてきたモデリングだが、その難しさを人に伝えるのも難しいと思っている。

貧弱なモデルを元に実装してもとりあえずのところは動くし、生産性の測定などをするにしても生産性の測定自体が難しいのに生産性に対するモデルの影響となるともっと難しいので、価値を示しにくい。

また、仕事で行なったモデリングは大半の場合において公開できないだろうし、モデルはドメインがあって初めて存在するので単体で OSS にするなどは難しそうなので、外部から観測することも難しい。

このあたりはなんとかしていかないといけないと思っている。

おわりに

コーヒー代830円の代わりに得たいろいろな気付きや思いをつらつらと書いてみた。

難しく大変な領域だと思うが、昔から好きな領域なので今後も引き続きやっていきたいと思う。

*1:ここではよく使う RDB を挙げてツイートしているが、他のデータストアももちろん考慮する必要がある。

*2:逆の「別だと思っていたことが実は同じだった」もあるけど、相対的に少ない。

『ベタープログラマ』を読みました。

この本です。

前から気になってたので年末年始の休みを利用して読んでみました。

感想

端的に言ってしまうと、あまりオススメしません。

訳がしんどい

原文を読んだわけではないので、本当に訳が良くないのか、そもそもの文章が良くないのかはわからないんですが、結果だけ見れば文章が非常に読みにくい。

日本語としてかなり不自然な文章が結構な頻度で出てくるので、読書中の集中を持っていかれるという点で厳しいし、そんな訳を見ていると原著の文意がちゃんと訳されているんのか不安になる。

具体性が薄い

目次を見た時点ではかなり期待していたんですが、実際に読んでみると内容がそれほど深くなくて説得力があまりない。

代わりに読んだほうば良さそうな本

総ページ数や金額としてはペターブログラマ1冊より多く/高くなってしまいますが、以下のような本を読んだほうが得るものは圧倒的に多そう。

達人プログラマー

ちょっと古いですが、いわずと知れた名著だけあってやっぱり良い内容。

具体的なプラクティスやそれを支える考え方みたいなものを紹介してくれているのでそのあたりの深さというか具体性もあって良い。

情熱プログラマー

エモ担当の本。 プログラマとしてどう行きていくべきかみたいなところはこの本のほうが良いと思う*1

Teem Geek

人付き合い部門。

『ベタープログラマ』では人間関係とかコミュニケーションについても触れられているんだけど、そのあたりの基本的な心構えは『Team Geek』が良いと思っている。

コード部門

『ベタープログラマ』第1章ではコードの書き方について触れているんだけど、コードの書き方なら『オブジェクト指向設計実践ガイド』かなーと*2

あとはベタだけど、『リーダブルコード』とかも良いんじゃないかな。

おわりに

先述したように、訳が気になってしまったのもあってあまり集中して読めていないかもしれないので、『ベタープログラマは良い本だぞー!』という意見があったらぜひ聞きたいです。

*1:わりと合う合わないはありそうな気がするけど。

*2:Ruby (とダックタイピング)を前提にしているので他の言語の人には合わないかもしれない。わからん。