valid,invalid

関心を持てる事柄について

『サウンド・オブ・ノイズ』観た

Netflix で観た。

予告編を観た時の高揚感はすごかったが突然の恋愛要素と感情移入しづらい主人公たちのために後半には気持ちが失速してしまった。

身近なものを楽器にして演奏するのも街中で演奏するのも誰もが思いつきそうなアイディアだけど、それが"音楽"になるまで作り上げていくのはとてもとても大変なことなので、見事にやってのけたライブシーンはどれも良かった。

数年後にライブシーンだけ見返しそうな気がする。

ソフトウェアの世界での slug / スラグ / スラッグの意味

かつての自分と全く同じ気持ちを持った質問者によるurl - What is the etymology of 'slug'? - Stack Overflow('slug' の語源は?)が気に入ったので抄訳。

python - What is a "slug" in Django? - Stack Overflowよりも質問の仕方が良い。

ちなみに今の自分が「'slug' ってなに?」と聞かれて説明するなら「ヒューマンリーダブルな ID」あたりが妥当な回答だろうか。


Q: ‘slug’ の語源は?

‘slug’ は特筆すべき理由のない言葉なのか、それともやはり何らかの意味がある言葉なのでしょうか?あるとき私は会話の中でこの言葉を使用したのですが、「なぜ ‘slug’ って呼ばれているのか」と聞かれたときに私も意味を理解してないと気づきました。

もちろんそれがどのように使われているかは知っているのですが… http://codex.wordpress.org/Glossary#Slug

結局、この言葉の背景にはどんな意味があるのでしょうか?

A: 由来は新聞業界です

‘slug’ は、新聞業界から来ている言葉です。

これは、制作過程で記事に与えられる非公式の名前、つまりコードネームみたいなものです。記事が担当記者(もしくはそれ以前)から編集者を経て印刷機に至るまでの道のりでの呼び名です。例えば「'kate-and-william' の校正は終わった?」のように使われます。

一部のシステム(Djangoなど)では URL の一部として ‘slug’ を使って記事を特定します。 www.mysite.com/archives/kate-and-william という具合です。

遡ると脚本の ‘slug lines’ に行き着くかもしれません。この ‘slug lines’ はシーンの背景、つまり、いつ誰がどこで〜などを表現するものです。後に続く文章を説明するものであるという点で ‘slug’ と似ています。

ライノタイプ*1においては、'slug' は個々の文字の形から作られた一本の線の金属を意味していました。1行ごとに単一の ‘slug’ を作ることによって、従来の1文字ごとに組み合わせを作るやり方が大幅に改善されたのです。

これは推測ですが、'slug' はもともとは(何かしらを押印しなければならないような)偽造されたコインのためのものだったのでしょう。言葉の使われ方が印刷用語に至り、そこから「1行の活字」、そして「記事の要約」へと変遷した様子を、私は容易に想像することができます。そうした印刷の世界からオンラインの世界へ移るのはこれまた実に簡単なことでしょう。

*1:ライノタイプは、キーボードを打鍵する事によって、活字母を並べてそれを鋳型とし、それに溶けた鉛を流し込んで、新聞などの印刷版型を作成する装置である。単語や空白から成る横一行を丸ごと活字にする事が出来る。かつては印刷所などにあった。ライノタイプという名称は、Line of type (一行の活字)を省略したものである。 quoted from ライノタイプ - Wikipedia

レビュー会のすすめ

先週日本にインドネシアのプロダクトマネージャーが来た時に「レビューに時間がかかりがち」「結果として開発のリードタイムが予測しづらくなっている」という悩みを相談してくれた。そのときに「コードレビューを会話しながら行う取り組み - Hatena Developer Blogとかどうですかね、日本のチームでも “レビュー会” ってのをやっていますよ」と伝えようとしたが日本語の記事を渡すのも気が引けたので「あとでまとめておく」ってことにして社内向けに “Encouragement of Review Meeting” という snippet を書いた。

これを日本語でも書き直してみる。


背景

最近プロダクトマネージャーと開発者間でレビューに関してこんな感じの問題があるらしい。

~ ある日 ~

マネージャ「進捗どう?」

開発者「だいたい終わってレビュー待ちの状態かな」

マネージャ「オッケー」

~ 別の日 ~

マネージャ「どう?Pull Request はマージされた?」

開発者「いや、それがまだで…。もう一回 ping してみる」

マネージャ「そうか…どれぐらいかかりそう?」

開発者「うーん、わからん…」

~ 以下、ループ ~


開発者による見積もりがすぐに元々の数字から変わったり、リリース日がずれたり、誰にとっても嬉しくない状況である。

一方、日本の開発者は複雑だったり大きすぎたりする Pull Request を作ってしまったときには “ペアレビュー” や “レビュー会” を設けており、今のところうまく機能している。

前提

そもそもレビューのリードタイムが長くなるのは Pull Request がでかいか複雑か、それに見合う説明が足りてないか、だ。

まず開発者が意識すべきなのは

  • Pull Request を適切なサイズに分割すること
  • 充分な説明、図、wiki などを書くこと

とはいえでかい Pull Request を作らざるを得ないこともあり、得てしてそれは長い間放置されがちである…。

詳細

そうした状況を避けるため、日本の開発者は “レビュー会” というのをやっている。

  1. レビューを求める開発者(レビュイー)はまず Pull Request (必要なら wiki なども)を前もって用意し、ミーティングを予約する
    • 全員が主体的に参加できるよう、レビュワーの数は2 ~ 3人がベストだと思う
  2. レビュイーはミーティングで Pull Request や wiki や図などを見せながら説明する
    • たまにすごく絵を書くのが上手い人にでくわす
  3. レビュワーはいつでも何でも質問して良い
    • たまに激しい意見が飛び交うかもしれないが落ち着いて欲しい
  4. 質問されたこや議論の内容は GitHub の review comments として残す
    • 残さないとあとでわけがわからなくなる
  5. ミーティングが終わったらコードを修正する
    • 結構な量の修正になったとしても指摘内容や実装意図を理解しているので再レビューはかなり簡単

Note

  • 全ての Pull Request がこうした取り組みを必要としているわけではない
  • “ペアレビュー” も “レビュー会” もコミュニケーションツールだということを忘れないこと。普段レビューを円滑にするために行っていることをそのままやるとよい
    • レビュイーは対面だからとか時間を取ってしまうからと気兼ねせずレビュー依頼や質問をすること
    • レビュワーは良いところは褒めつつレビューされたし

終わり



Original post:

Background

I recently heard some @quipper/product-managers (or even developers) are troubled with reviewing time. Is the situation like below?

~ One day ~

Manager: “How is your work going?” Developer: “Almost finished, but… it still needs to go through review.” Manager: “Understood.”

~ Another day ~

Manager: “Did your PR get merged?” Developer: “Ah, not yet. I’ll ping them again.” Manager: “Hmm, I see. How long does the review take?” Developer: “Umm, not sure.”

~ To be continued… ~


It’s so frustrating for everyone. Estimation by a developer easily varies from the original one, and its release date does.

On the other hand, Japanese developers sometimes have kind of “pair reviewing” or “review meeting” for a complicated (large) pull-request in person. So far, it works so well that I’d like to share the way.

Premises

Most of the review time issues are caused by a large/complicated pull request, and lack of adequate description.

So first of all, developers should

  • split pull-requests into adequate size ones if possible
  • write an adequate description, chart, wiki etc. for a feature they develop

Even though the idea above is right, we are sometimes compelled to implement a large/complicated pull request. And it will have been neglected for a long time…

Details

To avoid such situation, Japanese developers sometimes have “review meeting”.

How?

  1. A reviewee who needs a review prepares PR and wiki (if necessary) beforehand, and book a review meeting
    • 2 or 3 is a good number of reviewers. If it’s too much, all of them might not actively join a discussion.
  2. The reviewee explains his/her implementation by showing PR, wiki, chart etc.
    • I know some devs are quite good at to write a chart.
  3. A reviewer can ask/discuss anything anytime in the review meeting
    • Be calm, it’s not an interview.
  4. Questions/Discussion points are noted as a review comment on GitHub
    • If we skip this step, some points will be missed.
  5. After that, the reviewee reflects the content of the review meeting
    • Even if there are so many fixes, it’s much easier to re-review since reviewers know why those changes are needed.

Note

  • Not all of pull-requests requires “review meeting” and “pair review”.
  • They are just communication tools, so
    • If you are a reviewee, don’t be afraid to ask a review.
    • If you are a reviewer, it’s quite encouraged to pay a high compliment to one’s code.

END

プロダクトの複雑さと引き算の重要性

コードを書く気持ち

「開発しなくても既存の機能や基盤のうえで提供できる価値はあるし、既存機能を組み合わせて新商品や機能を作り出すこと、またはフィジビリティを行うことも場合によってはできる」…なんだかそういうことを常に念頭において開発するようになり、とにかくコードを書ければそれでよいみたいな気持ちがなくなってきた。

もちろんずっとコードを書いていたいのだが、小さい問題解決のために大きな問題を後回しにするようなコードは書きたくない…という迷いがある。

携わってるシステムがそこそこ大きく、一部はレガシーと呼んでも良さそうな年季が入っているので、無策でこれ以上コードを積み上げていくのに少なからず恐怖心もある。

複雑なるもの

なぜ迷いや恐怖心があるのか。複雑なものの上に新しい何かを載せてできるのは「ちょっと複雑なもの」ではなく「かなり複雑なもの」になってしまうからだ。

てきとうな式だと「複雑度10 + 複雑度1 = 複雑度15」 みたいなイメージだ。

大げさに言っているようだがこれはあながち間違ってもいなさそう… というのも、最近参加したメンバーからのフィードバックで「当初の見積もりより開発に4倍時間がかかった」というものがあり、理由を聞いたところ「既存の仕様や実装との兼ね合いが開発途中で判明し、当初に想定していたシンプルなものではなくなってしまった」という。

「見積もりが悪い」と責めるのは簡単だが俺はそうは思っていなくて「見積もりが正確にできないほど複雑なものを相手にしている」というのをチームの共通認識として持つべきだと思う。

立ち向かう案

「複雑だからしょうがない」と言って諦めてもしょうがない。

複雑だからこそ新しいものを1つ足すときには引き算の考えも同時に欲しい。最低でも1つ、可能なら2つでも3つでも同時に引き算することで複雑さを減らしていくしかない。

「何も引くことができない」というような状況の場合、振り返りや分析がきちんとできていない可能性を疑いたい。足す時に理由が必要なように、引くのにも理由が必要だからだ。

もし誰もそのことを気にかけていないなら、足し引きのバランスを担ったり引く理由を追うためのチームがあっても良いぐらいだと思う。

JavaScript や CSS を読み込む時の type 属性はもはや不要

kechol 氏からレビューを受けて知ったのでメモ。

https://google.github.io/styleguide/htmlcssguide.html#type_Attributes によると CSSJavaScript を読み込むときのタグに type attribute は HTML5 では不要、というか非推奨とのこと。

引用

Omit type attributes for style sheets and scripts.

Do not use type attributes for style sheets (unless not using CSS) and scripts (unless not using JavaScript).

Specifying type attributes in these contexts is not necessary as HTML5 implies text/css and text/javascript as defaults. This can be safely done even for older browsers.

<!-- Not recommended -->
<link rel="stylesheet" href="https://www.google.com/css/maia.css"
  type="text/css">
<!-- Recommended -->
<link rel="stylesheet" href="https://www.google.com/css/maia.css">
<!-- Not recommended -->
<script src="https://www.google.com/js/gweb/analytics/autotrack.js"
  type="text/javascript"></script>
<!-- Recommended -->
<script src="https://www.google.com/js/gweb/analytics/autotrack.js"></script>

こういうの、手癖で何も考えず書いたりしてしまう割に頻繁に情報アップデートしてもいないので Google HTML/CSS Style Guide を一通り読んでおいたほうが良さそうだ。

改行をレスポンシブにコントロールする

スマートフォンで閲覧する時は改行してほしい。PC では改行してほしくない。

みたいなデザインを実装している時に、同僚の @kechol 氏にレビューで教えていただいたのでメモ

See the Pen Responsive Line Break by Masato Ohba (@ohbarye) on CodePen.

CSS のメディアクエリで改行タグの display スタイルを操作すればよい

Clojure で初めての unless マクロを書く

7つの言語 7つの世界』を読みつつ Clojure 入門している。その中でマクロに関する記述があったが LISP のマクロに馴染みがないので、コードを見ても何が起きているのか瞬時にわからなかった。

正解のコードを少しずついじりながら、なぜそう書かないといけないのか、なぜそう書くと動くのかを確かめてみた。

ちなみに例は unlessで、これはマクロ入門でおなじみらしい。

失敗例

まずは挙げられているダメな例から。

(defn unless [test body] (if (not test) body))
#'user/unless

(unless true (println "Lose Yourself"))
Lose Yourself
nil

body が評価されてしまっている。testfalse のとき以外は評価してほしくない。なのでこれがダメなのはわかりやすい。

正規順序でなく作用的順序で評価してしまうと無限ループが発生してしまう例が SICP にもあった(気がする)。

正しい unless

以下のようになるようだ。

(defmacro unless [test body]
  (list 'if (list 'not test) body))

これをいじってみる。

if を即時評価していないのはなぜ

'if の部分は以下のように書けるのではないだろうか?

(defmacro unless [test body]
  (if (list 'not test) body))

(unless true (println "Nothing to tell."))
Nothing to tell. ;; 評価されてしまっている
nil

(unless false (println "This is it."))
This is it.
nil

はい、body が評価されてしまっているダメ。

(macroexpand '(unless cond body))
body

上記の unless をマクロ展開してみると body を実行するだけのマクロになってしまっていることがわかる。

not を即時評価していないのはなぜ

では、'not の部分は以下のように書けるのではないだろうか?

(defmacro unless [test body]
  (list 'if (not test) body))

(unless true (println "Nothing to tell."))
nil

(unless false (println "This is it."))
This is it.
nil

一見成功しているように見える…!

だがそれが命取り…!実は駄目…!

上記の unless をマクロ展開してみると条件部が既に評価されてしまっていることがわかる。

(macroexpand '(unless condition body))
(if false body)

;; 条件は false になるのに println が実行されない
(unless (= 1 2) (println "This is it."))
nil

正しい unless をマクロ展開すると条件部がまだ評価されていないことがわかる。

(macroexpand '(unless cond body))
(if (not cond) body)

;; ちゃんと unless 呼び出し時に評価されている
(unless (= 1 2) (println "This is it."))
This is it.
nil

defn で書いたらどうなる?

(defn unless [test body]
  (list 'if (list 'not test) body))

(unless true (println "Nothing to tell."))
Nothing to tell.
(if (not true) nil)

(unless false (println "This is it."))
This is it.
(if (not false) nil)

(class (unless false (println "This is it.")))
This is it.
clojure.lang.PersistentList

(list 'if (list 'not test) body) が評価されてしまった後のリストが返ってくる。body も評価されてしまっているので println が実行されている。

関連する関数などの知識

quote

評価してほしくないフォームに quote 関数を適用すると評価を抑制できる。

ダメなマクロの例を成り立たせるために body' で遅延評価させられる。もちろん呼び出し側でコントロールしているのでよくない。

(defn unless [test body] (if (not test) body))
#'user/unless

(unless true (quote (println "Lose Yourself")))
nil

;; ' 記号でも書ける
(unless true '(println "Lose Yourself"))
nil

defmacro

defmacrodefn のように関数を定義するが、その戻り値は実行時にコンパイラにマクロとして解釈されるようになる。

(doc defmacro)
-------------------------
clojure.core/defmacro
([name doc-string? attr-map? [params*] body] [name doc-string? attr-map? ([params*] body) + attr-map?])
Macro
  Like defn, but the resulting function name is declared as a
  macro and will be used as a macro by the compiler when it is
  called.
nil

;; 確かに Macro と解釈されているようす
(doc unless)
-------------------------
user/unless
([test body])
Macro
  nil
nil

7つの言語 7つの世界

7つの言語 7つの世界