パステル色な日々

気ままに綴るブログ

納得すること

チームメンバーに大筋の説明をした時の話。想像してたよりもあまりよくできなかった。

目標にしてたみんなの納得感はあまり作り出せなかったと思う。なぜか。

  • 自信なさそうな振る舞いをした
  • 自分の中で納得感が作り出せなかった(結論へのロジックに納得ができなかった)
  • 質問に答えられなかった
  • 板書があまり活用できなかった
  • 自分で自分を卑下した

こんなのが理由だと思っている。すべてチームメンバーの前でやってしまったのがまずかった。

自信なさそうな振る舞い

リーダーにこんな振る舞いされちゃついていきたくなくなりますよね。こういうやるときはやるリーダー像もあるかもしれないけどあまり納得がいってない

自分の中で納得感が作り出せなかった

できた大筋に対してそこに至る道筋に納得ができてなかった。なので要点だけをかいつまんでチームメンバーに納得感を作り出すプレゼンができなかった。まずは自分が納得できることが大切だ。

質問に答えられなかった

納得ができなかったから言わずもがな

板書があまり活用できなかった

今回は全員で顔をあわせて話をしたかったのでテーブルにコンパクトなホワイトボードを使って事前に色々板書を用意して説明に使おうとした。

納得感が薄かったので資料としてのわかりやすさにも精彩を欠いた

自分で自分を卑下した

質問に答えられなくて代わりに答えてもらった。そこで「だめだねー俺」と思わず言ってしまった。情けない気持ちで一杯でも不安に感じてもらうわけにはいかないのでどっしり構えたい。

自分はよく準備するタイプなので納得感が薄いとだんだん言っていて不安になってきて、それがもろに表面に出た。絶対納得した上で進めるようにする。困るのは自分なのだ。

想像を超える瞬間

僕は割と念には念を入れるタイプ。リーダーとして宿題を出された。自分なりにしっかりと考え抜いもはや隙きはない状態にしたと思ったが、フィードバックで普通に想像を超えられてしまった。僕からしてみると「あーたしかにそれも考えないといけないわー」とうなずくばかり。

フェーズは進み大まかなストーリーの合意も取れた。次はストーリーに沿ってどんな課題から向き合うのか。棚卸しと優先順位付けだ。

まだ上流工程で用件を話す段階なので少人数でスピード重視で進めることに。まだどうやってやるのかの話はしてないのとスピードがほしいのでエンジニアの力はまだ借りない。

UXエンジニアもチームにいてどういう働きができるのか事前に聞いた上で今回はもっと先の話のために準備してもらうことにした。

想像を超える瞬間があるとまだこの会社で学ぶことは多いなと感じる。自分も同僚の想像を超えられるようになりたいな。

キーパーソンは誰だ

引き続き目標の言語化。今後の行動の方向性を決めるものなのですごく大切だ。

開発シーンでは開発チームと問題が遠くなる場合もあるが今日は近い場合、そして一緒に解決する場合の話。

チームのオーダーをくれた人

チームが作ったものに対して承認をする人。この人と話をすると合格の方向性が見えてくる。抽象化された目標でも明後日の方向に向かって作られたものは承認されないのだ。この人とは合格の方向性を握ろう。

事業の偉い人

一緒に仕事をする人の上長。ミッションを与えている。進んでほしい理想像を知っている。方向性を決める参考にしよう。なぜそのミッションになったのかもっと聞いてみよう。さらにその先○年後のビジョンなども聞いてみよう。

一緒に仕事をする人

実際の課題を体系的に感じ取っている人。一番課題に近い人。直近の戦略を決めるときの参考になる。どんな問題やどんな人が存在しているのか聞いてみよう。対策をとっているのならどんな人にどんな対策をとっているのか聞いてみよう。技術でサポートできるのはどんなことだろうか。目標達成のためのストーリーが描けるかも すると自ずと優先順位が導き出せそうだ

ここまで話をすると作戦が決まった。ポイントは当事者だけで話をすること。小さくやっていく。

誰と会話するべきなのかはきっちり手持ちの情報を照らし合わせて決めていこう。狙いを持って話を聞きに行くことが肝心。

話のときには着目ポイントに沿ってメモしよう。すると議論に一貫性が生まれる。

大きくうなずき、話に割って入らない。深掘り、向きを変え、欲しかった答えを聞き出そう。

話は色んなエピソードを分解して再構成したり、抽象化と具体化を意識するだけで見え方が変わる。抽象度合いは話す相手によりレイヤーが違う事業責任者に具体策を聞いても組織構造の意味がない。

曲者

ちゃぶ台を返しそうな人。鶴の一声でいろいろ変えられるひと。ストーリーが決まったらこの人に合意を取ろう。

チームメイト

この時点で同意を一度取ろう。このあと具体策がでてきてもストーリーに沿っていれば疑問は生まれない。納得感を作り出そう。

目標の言語化の間にいろんなキーパーソンが存在する。ひたすら話をしてまとめて戦略を練って話を聞くことの繰り返し。こうして戦略は練られていく。

チームの一歩先を行く人

チームを率いるリーダーになりました。 日記のような形で日々の学びを書き出したくなったので頻度上げて書いていこうと思います。

なので自分に対する話し言葉で書き残します。

チームメイトの一歩先を行き、水先案内人になろう

リーダーに求められることは大きく2つあった。

  • チームに期待されている成果を上げること
  • チームメイトの先を行き水先案内人になること

特に下について、だいたい1Qを見渡すことから始めることに。大変。 チームメイトが頑張ってくれたのに結果を出せなければリーダーの責任と言われる。全くそのとおり。 戦略をしっかり練って水先案内をしないといけない。なのでリーダーの仕事は考えることっぽい。 このあたりで意識が引き締まった。

大きなプロセスイメージを描く

早速わからなくなったのが次にどうすればいいか。 先を行くにあたりどういう動きをすればいいんだろう。

  1. 目標言語化
  2. 目標共有
  3. タスク設定
  4. アサイ

こうらしい。水先案内人をするってことは抽象度が高い目標をチームメイトに見せる前にある程度具体化の作業が必要。 なぜなら新しい開発をするに当たり、問題提起した人がいるわけで、それに関わるキーパーソンからのヒアリングはすることになるから先んじてやっておけばチームメイトが迷いなくスタートを切れる。 抽象度が高いと不安になるし、それに集団で立ち向かって機能的に動けるほどまだチームは成熟してない。ならばリーダーが率先して動いたほうが良い。

業務のつながりを意識したコミュニケーションを取る

大きい組織になれば分業制は当たり前。そうなると仕事のパスが行われるときにたいてい取りこぼしがある。つながりを意識したコミュニケーションを取ることで個々の知識が流動的につながっていることを感じられてチームとして一体感を作れそうだと思った。

メンバーの体験設計でも生きる。チームメイトのバックグラウンドなんて多種多様。ならばちゃんとチームメイトが直前にお世話になっていた人 (コーチやメンター)に話を聞きに行くと目指していくキャリアやどういう人なのかわかる。これはメンバーが気持ちよく仕事をできるようにする材料になる。大いに役立ちそうだ。

まとめ

まだ動き出したばかり、言葉もつまり、ヘルプを出すシーンも多い。悔しいがしょうがない。とりあえず明日の動き方を決めて今日は良く寝よう。

UIから考えるモデリング in Elm

Modelは大切

皆さんは何を考えながらモデリングしてますか?まずはElm Architectureを見てみましょう。

elm_architecture
Elm Architecture

ViewはModelに依存しUpdateもModelに依存しています。したがってModelの影響の大きさは言わずもがなです。

ちょっとしたModelの修正も影響範囲が多くて溜息をつくことは少なくありません。影響は少ないほうがいいですよね。

今回はモデリングについて考えてみましょう。

UIから考える

今回はこの動画にある方法を使ってモデリングをやってみようと思います。

youtu.be

リチャードさんはまずはUIから考えようと言っています。インライン編集可能なブログ記事のページを考えてみましょう。

blog_article_page
ブログ記事のページ

TitleとStoryという本文で構成されたブログ記事のページです。記事の作成者はインライン編集ができます。

ペンのアイコンをクリックすると右のページようなインライン編集状態になります。Saveボタンをクリックすると左のページのTitleが編集したものに置換されます。

みなさんもこのようなページをモデリングしてみてください。それでは次に私がモデリングしてみます。

type Article
    = Article { title : Title, story : Story } Property


type Property
    = ReadOnly
    | WriteTitle Title


type alias Title =
    String


type alias Story =
    String


-- MODEL

type alias Model =
    { article : Article
    }

Titleのインライン編集に着目してモデリングしてみました。皆さんのモデリングと見比べてどうでしたか?

それでは次のモデリングと見比べてみましょう。

type Property
    = ReadOnly
    | WriteTitle Title


type alias Title = String


type alias Story = String

-- MODEL

type alias Model =
    { title : Title -- READ/WRITE
    , story : Story -- READ/WRITE
    , property : Property -- READ/WRITE
    }

前者と後者は何が違うのでしょうか?後者はレコードの要素をいつでも操作することができます。

そのためPropertyがReadOnlyであってもtitleとstoryは変更できます。もちろんそのようにしないロジックを構築すれば防ぐことができます。

update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        ToggleEditTitle ->
            ( { model | property = WriteTitle model.title }, Cmd.none )

        SetTitle title ->
            case model.property of
                WriteTitle _ ->
                    ( { model | property = WriteTitle title }, Cmd.none )

                ReadOnly ->
                    ( model, Cmd.none )

        SaveTitle ->
            case model.property of
                WriteTitle title ->
                    ( { model | title = title, property = ReadOnly }, Cmd.none )

                ReadOnly ->
                    ( model, Cmd.none )

それでは前者の場合はどうでしょう。

module Article exposing (Article, empty, mapTitle, toggleTitle)


type Article
    = Article { title : Title, story : Story } Property


type Property
    = ReadOnly
    | WriteTitle Title


type alias Title =
    String


type alias Story =
    String


empty : Article
empty =
    Article { title = "", story = "" } ReadOnly


toggleTitle : Article -> Article
toggleTitle (Article edits property) =
    case property of
        ReadOnly ->
            WriteTitle edits.title
                |> Article edits

        WriteTitle title ->
            ReadOnly
                |> Article { edits | title = title }


mapTitle : (Title -> Title) -> Article -> Article
mapTitle modifier ((Article edits property) as article) =
    case property of
        ReadOnly ->
            article

        WriteTitle title ->
            WriteTitle (modifier title)
                |> Article edits
module Page.Article exposing (..)

import Article exposing (Article)


-- MODEL

type alias Model =
    { article : Article
    }

-- UPDATE

update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        ToggleEditTitle ->
            ( { model | article = Article.toggleTitle model.article }, Cmd.none )

        SetTitle title ->
            ( { model | article = Article.mapTitle (always title) model.article }, Cmd.none )

        SaveTitle ->
            ( { model | article = Article.toggleTitle model.article }, Cmd.none )

いかがでしょうか。モジュールを2つに分けてみました。

これならArticleはReadOnlyのときにtitleを変更できません。なぜならそのような関数がArticleモジュールから公開されてないからです。

また前述のパターンと異なり、WriteTitleコンストラクタも公開していません。このためArticleモジュール内部にArticleの実態は隠蔽されています。

この方法をOpaque Typeと呼びます。テクニックの一つですが動画では必須とまで言われています。

これで、Articleモジュールを編集してもUpdateやViewに影響が出ないように作ることができました。ネストが浅くなり多少見やすくなってないでしょうか。

また、title編集時の状態を WriteTitle Title と定義しているのもポイントです。

type alias Model =
    { title : Title
    , story : Story
    , editableTitle : Maybe Title
    , editableStory : Maybe Story
    }

少し定義を変えてみました。それではこの状態はとり得るのでしょうか。

type alias Model =
    { title : Title
    , story : Story
    , editableTitle : Just Title
    , editableStory : Just Story
    }

インラインで編集可能なのは一箇所だけとします。するとeditableTitleとeditableStoryの両方がこのような状態を取ることはありえません。

このようなアプリケーションにありえない状態を作り出すことが可能なモデリングを避けることでバグの少ないアプリケーションを構築できます。不可能な状態はモデリングで防ぐことができます。

youtu.be

サマリ

  • UIから考えることでモデリングができる
  • Oppaque Typeで変更に強いモデルを作ることができる
  • 不可能な状態 (Impossible state) を排除することができる

所感

またもや動画からモデリングの方法の一部を紹介しました。Elmでアプリケーション開発をしているとモデルを変更する機会は多々訪れます。

そのたびにModel, Update, Viewのすべてを変更するのはとても大変です。今回の方法を用いてなるだけ変更の影響がもれないように気をつけてみてください。

しかし、モデリングのポイントはこれだけではありません。UI StateとDomainをどのようにモデリングするのかという話題もあります。

フロントエンドでDDDをしようものならもっと複雑になることでしょう。一概にこれが正解というわけではないので皆さんの課題に沿った最善の方法を模索していきましょう。

そしてどんどん私に教えてください。 (違

Elmのモジュールを作るときはどういうとき

モジュールを作るとき

youtu.be

いきなりですがこの動画を見てください。この動画はElmの作者のEvanさんによる発表です。

まず見て驚くのはElmの場合、一つのファイルに対してプログラムを育てていくことではないでしょうか。通常システムを作るときには責務を切り分けて責務ごとにモジュールを作成することが結果的にファイル分割につながっていました。

Elmでも同様にElm ArchitectureによりModel, Update, Subscribe, Viewにシステムが分割されています。しかし単一の責務ごとにモジュールを作成することなく一つのファイルにアプリケーションを構築していきます。

この発表いわく通常それは400から1000行くらいのコードまで成長するそうです。そのあたりからモジュール作成の兆しが訪れることが述べられています。

Modelの構築の仕方に習っていくと新しいCustom Typeを作る機会が訪れます。まずはそのCustom Typeを作成して、そのCustom Typeにまつわる関数を作ります。

こうしてできたCustom Typeとそれにまつわる関数を別モジュールに切り出します。これがElmにおけるモジュール作成のタイミングと作成方法です。

このことはドキュメントにまとまっています。

Modules · An Introduction to Elm

モジュールの変更による影響範囲を閉じ込める

youtu.be

またいきなりですがこの動画を見てください。この動画はEvanさんの同僚NoRedInkで働くRichardさんの発表です。

動画で述べられている通りModelに依存しているシステムは多いのでModelの変更は影響が多岐にわたることが多いです。これをModel内の変更に留めるにはどうするといいでしょうか?

積極的にOpaque Typeを使うことを主張しています。Opaque Typeについては下記の記事が参考になります。

Advanced Types in Elm - Opaque Types – Charlie Koster – Medium

Elm PackagesのAPI Documentにも記述があります。

Design Guidelines

適切なインターフェース (関数) を作成し、exposingで公開するものを絞り込むことで変更をモジュールに閉じ込めることができます。

module Post exposing (Post, fromDoc)

type Post
  = Post doc
  
  
fromDoc : doc -> Post
fromDoc =
  Post

やりすぎないこと

Evanさんはやりすぎないことを注意としています。Custom Typeによるモジュール化を進めると最初のうちから再利用を意識して同じロジックをまとめたがります。

そんなときには一呼吸おいて問題が起きてから対処するようにしましょう。例えばページによって同じCustom Typeでもデータ構造が異なることがあります。

module PageX exposing (..)

type Doc
  = Doc Title Content

type alias Title = String

type alias Content = String
module PageY exposing (..)

type Doc
  = Doc
    { title : String
    , content : String
    , description : String
    }

早い段階から共通化を見越して過剰に手を加えるのではなく実際に重複するロジックが出てくるまで待ってみましょう。

共通したデータ構造があるのならコードを改善してシェアしましょう。このようにElmでは作っていく最中に発見してリファクタリングを積極的に繰り返すことを勧めているように感じます。

それは強力な型と言語設計によりリファクタリングをしやすい環境が整っているからでしょう。

サマリ

  • Elmでは必要な時が来るまでモノリシックなモジュールでアプリケーションを育てていく
  • カスタムタイプが増えてヘルパー関数ができたときがモジュールを作るサイン
  • モジュールはカスタムタイプに対する責任を持つ
  • Opaque Typeを積極的に使い影響範囲をなるべくモジュール内に限定する
  • 過度なモジュール化はすべきではないリファクタリングすることを念頭に置く

所感

今回はElmのモジュールについて記事にしました。Elmではこのような思想がありますが、一つのファイルで1000行もコードを書いていてはチーム開発をしているとコンフリクトが多発しそうですね。

また、モジュールの作り方にこのような指針がないとチームで開発ではこの関数はどのモジュールに属するのがいいのか迷ってしまうシーンがあります。

JavaScriptとは異なるモジュールの作成プロセスですが、Elmではこのようなプロセスをおすすめしていることを紹介しました。

Elmはどうやって超小さいアセットを実現したのだろう

Elmはとても小さいアセットを作ることができる

We had a community member working on a 49,315 line application try the new version and he got 114kb after compilation, minification, and gzip. That is in the ballpark of the 100kb produced by the 2000 lines in the Vue implementation!

blog/small-assets-without-the-headache

リンク先の画像が示すように、Elmはリリースされた0.19で十分に小さいJavaScriptファイルを生成することができるようになりました。RealWorldアプリケーションでの検証結果ですが、ひと目で主要なフロントエンドのライブラリ利用時よりも小さくなっている事がわかります。

0.19リリース時に公開されたこのブログ記事を見て僕は「すごいなー」と感心しました。一体どうやってこの小さなファイルを生成することができるようになったのでしょう。

デッドコードを削除する

僕はしばしば課題に直面すると同じような課題に対してアプローチしているライブラリがないか探す事があります。JavaScriptにはNPMという素晴らしいシステムがあるので、簡単にプロジェクトにライブラリを導入できます。

しかし、ライブラリのコードをすべて利用することは殆どないのです。ライブラリが公開しているAPIはどれも魅力的なものですが課題を突破するためにはいくつかのAPIで事足りる事が多いからです。

デッドコードはこうしたときに生まれます。利用してないコードがあってもライブラリをインストールした以上、ビルド時には無視できない存在となっています。もちろんライブラリだけではなくプロジェクト内のコードだって同じように考えることができるでしょう。

巨大なプロジェクトでは利用されてないコードが生まれることを見落としていることもあるかもしれません。webpackに代表されるようにツリーシェイキングによってこのようなデッドコードは削ぎ落とされることになりますが、完璧に削ぎ落とせてないそうです。

どうやらwebpackはモジュール単位でルーチンを走らせており、モジュールの粒度が大きくなればなるほど利用してないコードが紛れ込む可能性が高くなります。直接APIを呼んでない場合でも別のルーチンから呼ばれている場合もあるためライブラリが増えれば増えるほどデッドコードは増加する傾向にあります。

対策としてlodashに代表されるように関数ごとにモジュールを分ける方法があるものの、JavaScriptではどのモジュールでも実行時に関数の変更ができるので今呼ばれてないからと言って簡単に削除することはできないのが実情です。

しかし、Elmの関数は実行時に再定義と削除ができないため、メイン関数から到達可能であれば残して到達できなければ削除することができます。これがElmがアセットを小さく保つことができた1つ目の理由です。

また、Elmの場合はNPMとは異なり、package.elm-lang.orgでライブラリを公開しており、ここで公開されているライブラリはすべてElmで書かれているので安心して同じように削除できるのです。

レコードフィールドをリネームする

Elmにおけるレコードとは次のようなものです。

student =
  { name = "Haruka"
  , age = 17
  }

フィールドとはnameageを指していいます。これを参照する場合はstudent.nameとなりますが、0.19ではコンパイルの前後でs.nとリネームされるのです。

驚くべきことにこれで5% ~ 10%縮小するそうです。やはりこれもライブラリを含めたエコシステム全体で機能できます。

サマリ

  • Elmは0.19で主要なライブラリのどれよりも小さいJSファイルを生成する事ができる
    • デッドコードを削除する
    • レコードフィールドをリネームする

所感

非常に小さなアセットの生成が可能になった背景には言語の特性とその特性沿ったエコシステムがありました。一方でElm 0.19には多くの破壊的変更が含まれています。

compiler/0.19.md at master · elm/compiler · GitHub

コンパイラの最適化にはこのような変更を盛り込むしかなかったのでしょうか。Elmは純粋な関数型プログラミングの土壌を用意してくれていますが、JavaScriptに変換する際に問題を作り出してしまうこともあったのではないでしょうか。

変更の裏にはこうした課題を突破できないのでやむを得ず適用した変更したものもあるかもしれません。世界観を一貫して守りきる思想の強い言語という雰囲気を感じ取ることができました。それにしても破壊的変更が多くて大変です。