Vuetifyの TreeviewをDrag and Drop可能にしたコンポーネントを作った

この記事は suusan2go Advent Calendar 2019 - Adventar の6日目の記事です。

仕事でVuetifyを使っていて、 TreeviewコンポーネントのUIでドラッグアンドドロップ出来るようにする必要があった。Treeviewというのは以下のように、階層構造をもつデータ構造をいい感じにするコンポーネントf:id:suzan2go:20191207225659p:plain

が、以下のIssueにもあるように現状はドラッグアンドドロップをサポートする予定はないとのこと。

github.com

There is currently no plan to implement drag & drop functionality

というわけで、作った

作ったもの

こんな感じでコンポーネントを定義してやれば、Treeviewかつドラッグアンドドロップ可能なコンポーネントが使えるようになる。

<template>
<v-draggable-treeview
  group="test"
  v-model="items"
></v-draggable-treeview>
</template>

<script>
export default {
  data() {
    return {
      items: [{ id: 1, name: "hoge", children: [{ id:11, name: "hoge-child1" }] }]
    }
  }
}
</script>

github.com

見ての通り、もともとのTreeviewのUIでドラッグアンドドロップが可能になっている

https://user-images.githubusercontent.com/8841470/70327688-b6ca2800-187a-11ea-907e-79d7dc3afca9.gif

中身

実装はめちゃくちゃ力技で、オリジナルのTreeviewがコンポーネントが描画するDOM構造をもとに、Vue.Draggable を噛まして子要素をドラッグアンドドロップ可能な作りで組み直している。そのためprops / event / slotも自分が必要だった最低限しか実装していない… もともとはVuetifyのコンポーネントを拡張して作ろうと思ったのだけど、普段使ってないclassコンポーネントを読み解くのが辛かったので、とりあえず仕事上必要になった最低限の仕様をDOM構造だけトレースして実装するにとどめている。

難しかったのは、複数のTreeA、TreeB... のように複数のTreeがあったときに、TreeBのchildrenからTreeAのchildrenに要素を移した場合の挙動で、結論からいうとこれは単純に親にprops / emitしていく形式だとレースコンディションが起きて意図した結果にならない。TreeBのchildrenからTreeAのchildrenに要素を移した場合にprops / emitを上のコンポーネントに伝えていくと以下のようなことが起こる。

  • [TreeA] から [TreeB] に TreeAの子要素aを移す
  • ① [TreeB]に子要素aを追加したvalueをemitする
  • ② [TreeA]の子要素aを抜いたvalueをemitする

rootコンポーネント[TreeA, TreeB] のようなvalueをpropsとして渡されているとすると、全てをprops / emitで処理する場合にはrootコンポーネントで以下のような形で emit を実施したい。

  • this.$emit('input', [TreeA, 更新後のTreeB])
  • this.$emit('input', [更新後のTreeA, 更新後のTreeB])

しかし実際には①でemitした結果はすぐにpropsとして反映されるわけではないので、 以下のようになりドラッグアンドドロップした要素がどこかに消えてしまう。

  • this.$emit('input', [TreeA, 更新後のTreeB])
  • this.$emit('input', [更新後のTreeA, TreeB])

というわけで、全て一度localのstateにして受け取って、更新があったらemitするような形式にしている。というか仕事で必要だったのは一つのTree内での並べ替えだけで、ライブラリ化するにあたって全てのTreeで要素を異動できるように変えたんだけども、思いの外これが大変で時間を食ってしまった。

まとめ

コンポーネント化してnpmパッケージ化するにあたっては、クックブックがとても参考になりました。Rollup触るのも初めてだったけど割とシュッとかけてよかった。

jp.vuejs.org

Deep Workを読んだ

この記事は suusan2go Advent Calendar 2019 - Adventar の5日目の記事です。

この本を読んだあとも別にTwitterをやる頻度が落ちてない時点で「察し・・・」という感じではるのだけど、 少し前にDeep Work という本を読みました。

最近あまり集中して仕事をしてないなという感覚が結構続いていたときに、だいぶ前にito naoyaさんがRebuild.fmで紹介していたことを思い出して買いました。

本の内容

主張はシンプルで、以下。

  • メールを常にチェックするとか、TwitterFacebookを見るとかそういうことやってると、集中力が低く注意散漫な状態で仕事をすることになるぞ!それはシャローワークというんや!
  • SNS見るのやめろ!集中して仕事に没頭しろ!それがDeep Workや!

2行で終わるような話ではあるのだけど、Deep Workをすることでどんなに素晴らしい成果をだせるか、そしていかにDeep Workな環境を作るかということが本書のなかでは解説されています。 ビル・ゲイツが年に二度引きこもって「考える週(Think Weeks)」を設け、その間は引きこもってただ本を読んだり大きな構想を練ったりするみたいな話がDeep Workの例紹介されていますが、当然普通に働いている人が「Deep Workするので今週は引きこもるわ!」とかいうのは難しいわけで、いろんなパターンでDeep Workする方法が紹介されています。

自分がやっていること

本書のなかで書かれている内容のなかでも朝仕事を始めるときに、タイムボックスを作って予定をたてるということを意識してやるようになりました。もちろん今までも今日何やるかというのは当然考えてやってたわけですけど、それをもうちょっと厳密にちゃんとやるようになったということです。できるビジネスマンなら当たり前にやってそうなことなので今更感あってとても恥ずかしいんですけど、具体的には個人のメモ書きとして使っている notion に、以下のようなフォーマットで朝やることを書いて帰るまえに振り返るということをやるようになりました。

## 今日やること
- xxxxの実装を終わらせる ( 10:00~13:00 )
- 昼飯 ( 13:00~14:00 )
- xxxx についてドキュメントにまとめる (14:00〜15:00)
- MTG(15:00〜16:00)
.
.
.


## 明日やること

## K

## P

## T

タイムスパンを区切ることでダラダラTwitterをみながら仕事をしたりせず、良い緊張感をもって仕事ができるようになりました。また自分の想定よりも時間がかかっていることがわかるので、自分の作業見積もりを改善する機会にもなっています。ポモドーロとかも過去に試したんですけど、自分は集中すると数時間没頭したくなるのであまりリズムが合わなくて、なんとなくこんな感じの運用になっています。ただ最近は集中しすぎて仕事をやめられないというパターンもあり、数時間座りっぱなしで結果的に夕方になってお昼ご飯を食べるみたいなパターンも出てきて、あまりよくないなと思っているところ。Apple Watch を買って座りすぎを注意してもらおうかなー。

まとめ

本自体は結構著者の主観による主張みたいなものも多い気がしましたが、自分の仕事の仕方を見直す機会にはなったのでよかったのかなと思います。昔、一ヶ月以上かかる見積もりの機能をどうしても早く出す必要があったとき、エンジニア2人で集中スペース的なところに閉じこもってやったら一週間かからずに完成したみたいなことがあったんですが、あれは今思えば Deep Workだったのかなー。

GoのORM、SQLBoilerのススメ

この記事は suusan2go Advent Calendar 2019 - Adventar の4日目の記事です。

フリーランス始めてから、エムスリーでお世話になった @maeharin さんがCTOをしてる ANNONE という会社をお手伝いしています。自分はその中で、Goによるアプリ向けのAPI,Reactによる管理画面、Flutterアプリ、CloudSQL=> BQの同期、新規事業のRails newなどと幅広く色々とやらせてもらっているのですが、今回はあのね というアプリ向けに作られたGoのAPIのDBアクセスをSQLBoilerに切り替えた話をします。

Go APIの構成

特定のフレームワークは使っておらずルーティングには gorilla/mux、DBアクセスには sqlxを使っていて、シンプルなレイヤードアーキテクチャを採用した以下のような構成になっています。

api
├── application
├── cmd
├── controller
├── domain
├── middleware
├── migration
├── repository
├── util
└── view

DBアクセスはrepositoryに記載されたinterfaceを実装する形で実現されています。

type Repository interface {
    FindClinicByID(q sqlx.Queryer, clinicID int64) (*domain.Clinic, error)
    CreateClinic(tx *sqlx.Tx, clinic *domain.Clinic) (int64, error)
}
// FindClinicByID IDでクリニックを検索する
func (r *repository) FindClinicByID(q sqlx.Queryer, clinicID int64) (*domain.Clinic, error) {
    c := domain.Clinic{}
    query := `
      select
          c.*
      from clinics as c
      where
          c.id = $1
  `
    if err := sqlx.Get(q, cd, query, clinicID); err != nil {
        if err == sql.ErrNoRows {
            return nil, errors.WithStack(NewRecordNotFoundError(fmt.Sprintf("clinic(id: %d) is not found", clinicID)))
        }
        return nil, errors.WithStack(err)
    }
    return cd, nil
}

// CreateClinic クリニックを登録する
func (r *repository) CreateClinic(tx *sqlx.Tx, diary *domain.Clinic) (int64, error) {
    query := `
      insert into clinics(
          , name
          , create_timestamp
          , update_timestamp
      ) values (
          , :name
          , :create_timestamp
          , :update_timestamp
      )
      returning id
  `

    stmt, err := tx.PrepareNamed(query)
    defer stmt.Close()
    if err != nil {
        return int64(0), errors.WithStack(err)
    }
    var id int64
    err = stmt.Get(&id, &diary)
    if err != nil {
        return int64(0), errors.WithStack(err)
    }

    return id, nil
}

sqlxによるDBアクセスのpros / cons

Goではありませんが、過去のプロジェクトでDomaなどSQLを書いてそれをオブジェクトにマッピングする形式のライブラリは使ったことがあり、SQLを書いてオブジェクトにマッピングするという手法はシンプルで気に入っています。特に複数のテーブルをジョインしてオブジェクトにマッピングしたいといったこともシンプルに実現できますし、コードを読めばどんなクエリを発行しようとしてるのかすぐに見える点もよいです。

しかしながら、スタートアップで開発スピードが要求され、変更も多い環境のなかでは、辛い点も見えてきました。

Insert / Updateの記述が辛い

上にも書きましたが、テーブルが増える度に以下のようなクエリとそれをマッピングするコードを毎回1から書く必要があるのは結構しんどいものがあります。カラムが増えたり変わった場合にはも他のクエリで使っているinsert / update文も忘れずに追随していかなければいけません。

 query := `
        insert into clinics(
            , name
            , create_timestamp
            , update_timestamp
        ) values (
            , :name
            , :create_timestamp
            , :update_timestamp
        )
        returning id
    `

そもそも開発初期段階や / 70%の機能ではシンプルなCRUDで十分なケースも多い

柔軟にSelect文がかけるというのはとても良い体験なのですが、全てのテーブルでSelect文をガッツリ書く必要があるかというとそんなことはなく、体感的には70%以上の機能ではシンプルなCRUDがシュッと実現できればそれで十分という感覚があります。むしろ一つテーブルを追加する度にCRUDSQLとstructにマッピングするコードのボイラープレートを大量に書く必要があるのは、特に新規機能開発で2、3のテーブルを追加する必要がある場合には結構ストレスでした。

TypeSafeではない

最初の問題点にも繋がりますが、Create文やUpdate文を長々と書く必要がある割にカラム名TypoしたりDBと対応の異なる型をStruct側に定義してしまったりしてもコンパイル時には気が付けません(これを Type Safeでないと言っていいのかわかりませんが・・・)。カラム追加や変更が合った場合に特にInsert / Update文で間違えなくこれに追随していくのはかなり大変に思えました。

SQLBoiler

上記にあげた課題を全て解決しつつ、普通にSelect文が書こうと思えば書けるようなツールはないだろうかと調べていたところで、以下のブログで紹介されているSQLBoilerというツールにたどり着きました。

Go の ORM / query builder 消耗日記 - blog.izum.in

github.com

SQLBoilerとは

SQLBoiler is a tool to generate a Go ORM tailored to your database schema. とあるように、DBのスキーマをもとにテーブルに対応するstructとCRUDな操作を提供するfunctionを自動生成してくれます。

REATE TABLE pilots (
  id integer NOT NULL,
  name text NOT NULL
);

ALTER TABLE pilots ADD CONSTRAINT pilot_pkey PRIMARY KEY (id);

CREATE TABLE jets (
  id integer NOT NULL,
  pilot_id integer NOT NULL,
  age integer NOT NULL,
  name text NOT NULL,
  color text NOT NULL
);

ALTER TABLE jets ADD CONSTRAINT jet_pkey PRIMARY KEY (id);
ALTER TABLE jets ADD CONSTRAINT jet_pilots_fkey FOREIGN KEY (pilot_id) REFERENCES pilots(id);

生成されるコードはこんな感じです。これとは別にFind等のメソッドがたくさん生成されます。

type Pilot struct {
  ID   int    `boil:"id" json:"id" toml:"id" yaml:"id"`
  Name string `boil:"name" json:"name" toml:"name" yaml:"name"`

  R *pilotR `boil:"-" json:"-" toml:"-" yaml:"-"`
  L pilotR  `boil:"-" json:"-" toml:"-" yaml:"-"`
}

type pilotR struct {
  Licenses  LicenseSlice
  Languages LanguageSlice
  Jets      JetSlice
}

type Jet struct {
  ID      int    `boil:"id" json:"id" toml:"id" yaml:"id"`
  PilotID int    `boil:"pilot_id" json:"pilot_id" toml:"pilot_id" yaml:"pilot_id"`
  Age     int    `boil:"age" json:"age" toml:"age" yaml:"age"`
  Name    string `boil:"name" json:"name" toml:"name" yaml:"name"`
  Color   string `boil:"color" json:"color" toml:"color" yaml:"color"`

  R *jetR `boil:"-" json:"-" toml:"-" yaml:"-"`
  L jetR  `boil:"-" json:"-" toml:"-" yaml:"-"`
}

type jetR struct {
  Pilot *Pilot
}

DBとの対応だけではなくJSONのタグも記述してくれるので、自分は後述する通り使っていませんがDBで取得した結果をそのままレスポンスとして返せばいいようなシンプルなアプリでは便利かもしれません。外部キー制約をもとにrelationを自動で貼ってくれ、nullableなものには null パッケージを適用してくれます。

基本的なデータの操作

詳細はREADMEに譲りますが、SQLBoilerでコードを生成すると以下のような操作は全て自動生成されたstructと関数だけで実装することが可能になります。

// IDで引く
clinicl, err := models.FindClinic(db,  clinicID)
// 全て取得する
clinics, err: = models.Clinics().All( db)
// where
clinics, err := models.Clinics(qm.Where("pref_id = ?", prefID), qm.And("name = ?", name)).One(db)
// insert
err :=clinicl.Insert(tx, boil.Infer())
// update
_, err := clinic.UPdate(tx, boil.Infer())
// Relationships
prefecture, err := clinic.Prefecture()

また必要なときにはsqlxのようにSQLを書いてオブジェクトにマッピングするという手法を取ることも可能です

// Custom struct for selecting a subset of data
type JetInfo struct {
  AgeSum int `boil:"age_sum"`
  Count int `boil:"juicy_count"`
}

var info JetInfo

// Use query building
err := models.NewQuery(Select("sum(age) as age_sum", "count(*) as juicy_count", From("jets"))).Bind(ctx, db, &info)

// Use a raw query
err := queries.Raw(`select sum(age) as "age_sum", count(*) as "juicy_count" from jets`).Bind(ctx, db, &info)

あのねアプリ向けAPIでの使い方

ActiveRecord-like productivity と語っているため?かはわかりませんが、SQLBoilerは自動生成したstructを使ってDBの内容をそのままJSONにして返すということも可能な作りになっています。しかし、以下の観点から元々存在していたrepositoryレイヤを活用してSQLBoilerの依存範囲をrepositoryパッケージにとどめています。

  • もともと存在するrepositoryレイヤを活用すれば、sqlxから安全に移行することが可能であること
  • SQLBoilerへのアプリケーションの依存を強めると、SQLBoilerから脱却することが難しくなること
  • Go言語の性質上、生成されたstructの挙動をオーバーライドするような実装をすることが難しいこと

実装としては以下のような感じになっており、アプリケーション用のstructである domain.Clinic をそのままSQLBoilerに渡さずmodels.Clinicマッピングし、また自動生成された models.Clinicをrepositoryの外には出さずに domain.Clinic し直すという感じになっています。sqlxのときにSQLを書いていたのがオブジェクトのマッピングに変わってしまっているといえばそうなのですが、Goのコード上でかけるのでIDEのサポートでfill-structといったツールを使ってガッとstructを埋めることもできますし、何よりちゃんと補完が聞くのでSQLを生で書いたいたときよりもかなり生産性があがりました。

// CreateClinic クリニック情報を登録する
func (r *repository) CreateClinic(tx boil.Executor, clinic *domain.Clinic) (int, error) {
    model := models.Clinic{
        Name:            clinic.Name,
        CreateTimestamp: time.Now(),
        UpdateTimestamp: time.Now(),
    }
    err := model.Insert(tx, boil.Infer())
    if err != nil {
        return 0, errors.WithStack(err)
    }

    return model.ID, nil
}

// FindClinicByID IDでクリニックを検索する
func (r *repository) FindClinicByID(q boil.Executor, clinicID int) (*domain.Clinic, error) {
    model, err := models.FindClinic(q, int(clinicID))
    if err != nil {
        return nil, errors.WithStack(err)
    }
    clinic := mapToClinic(*model)
    return clinic, nil
}

structの詰め替えをするのは非効率ですし、大量のデータを処理しなければいけないときにはパフォーマンス上の問題となる可能性もありますが、通常のアプリ向けAPIでは問題になるケースはそれほど多くないのではないでしょうか。

使ってみた所感

元々の狙いどおり、sqlxで辛さを感じていた箇所はSQLBoilerでかなり楽をすることができるようになりました。一方でSQLBoilerも万能ではなく、例えば現在のところLeft Outer Joinをサポートしていません。

github.com

複雑なクエリを発行する必要がある場合や上述したstructの詰め替えコストを許容できない場合には、sqlxは引き続きよい選択肢なのかなと思います。場合によっては併用もありなのかなーと思いつつ一つのアプリで2つのDBライブラリを要求するのはなぁ・・・みたいなことを思っています。

PR枠

ANNONE では、こんな技術を使って開発してます。

全方面エンジニア募集中のようなので、興味のある方はぜひ @maeharin さんにDMを!

大変な家事・育児をお金の力で楽にする

この記事は suusan2go Advent Calendar 2019 - Adventar の3日目の記事です。

自分は去年の11月に二人目の子供が生まれて、現在3歳になる娘と専業主婦の妻と合わせて4人家族で生活しています。2人の育児は、あんなに大変だった1人目の育児がイージーモードに感じるくらい大変でした。特に夜ご飯〜お風呂〜寝かしつけの時間帯はワンオペだと辛く妻だけではダウンしてしまうので遅くとも20時には帰宅、下の子は夜泣きがいまだに酷くて一緒に寝ている妻は朝起きるのがかなりしんどいので(定期的に自分も交代しています)、朝ごはんと幼稚園のお弁当はほとんど自分が作っています。

フリーランスで稼働時間の融通がきく今だからこそ自分も家事育児を最大限やってなんとか回ってるというのが実情です。しかしフリーランスといえど仕事の忙しくなるタイミングというのはあって、そうなると自分も妻もキャパを超えてしまってめちゃくちゃストレスになっていました。

エンジニア関連で子育てというと、家族でSlack使って家事育児を効率化とかTrelloで情報共有とか家庭内スクラムだとかキラキラした事例がたくさんあります。エンジニアとしてそういうのもいいなーと思いますが、そんなこと考える余裕もないのでとにかく早く何とかして楽になりたいというのが正直な気持ちです。というわけで、フリーランスで働いていて比較的金銭的に余裕のある今だからこそできる「金で殴る」という解決方法を取ることにしました。

というわけで参考になるかわかりませんが、自分がいまお金をかけていることを紹介します。

育児

ベビーシッター

まず真っ先に考えたのがこれでした。週一でも預けられるとかなり楽になるはずだなーと。しかしながら、0歳児にも対応してくれて、かつ3歳児も一緒に見てくれるというベビーシッターの方はなかなか見つからず、見つかっても自分たちの希望するような時間帯で対応いただける方は見つかりませんでした。下の子も最近1歳になったので、また検討はしたいなと思っています。

乳幼児一時預かり事業

自分の住んでいる横浜市では乳幼児一時預かり事業というのをやっています。これは生後57日~小学校入学前の子供を理由を問わず預かってくれるもので、市の事業だけあって自己負担は1時間あたり300円以下とかなり良心的です。 www.city.yokohama.lg.jp

探してみると自分たちの近所でもこの事業に参加している施設があったので、いまは週一で子供を預かってもらっています。ただし事前予約は二ヶ月前からかつかなり激戦で、キャンセル待ちからの繰り上げでギリギリ預けられるみたいなパターンが多いです。定期預かりの申し込みも年1かつ先着順のようなので、かなり熾烈な争いになっています…

託児所の一時預かり

託児所も利用しています。こちらは買い物やクリニックにいくときに妻が使っていますが、当日予約OKの施設は特にありがたいです。上記の乳幼児一時預かり事業と比べると当然ですが少し高くなり、1時間あたり1000円~といった感じになっています。

mamas-smile.com

家事

食洗機やお掃除ロボットは既に導入済みで、以下のようなサービスを利用しています。

料理

二人目が生まれる前から利用していますが、平日の夜ご飯はヨシケイに頼っています。メニューを考える必要がなく、食材を買いに行く必要もないのでお勧めです。

yoshikei-dvlp.co.jp

掃除

家事代行は色々とサービスがありますが、子供のいる家に知らない人を上げるとなると仲介しかしない事業者のサービスはちょっと嫌だったので、ちょっと割高ではありましたがダスキンのメリーメイドというサービスを利用していて、週1日来てもらっています。

www.duskin.jp

家事代行のサービスでは専門の掃除器具などは使わず、家にあるもので掃除するという形態になっているのですが、さすがそこはダスキンで掃除のクオリティはめちゃくちゃ高いです。シンクやお風呂が「そういえば新築のときはこんな色してたな…」と思うほどピカピカになりました。もちろん掃除だけでなく洗濯物を取り込む、洗い物をするといった家事もやってくれます。ただし料理は別料金になってしまうようです。こちらは首都圏だと、1週間に1回または、2週間に1回(1回・1名・2時間)で9,900円となっています。

まとめ

上記のようなサービスを利用しはじめて家事・育児の負担が幾らかは軽減され、よくイライラしてしまっていた妻も自分も少し心に余裕が持てるようになりました。育児・家事を楽にしたいけどどうしたらいいか分からない!という人も多いと思うので、誰かの参考になれば嬉しいです。

スクラム開発を始めるときに自分がやったこと

この記事は suusan2go Advent Calendar 2019 - Adventar の2日目の記事です。

今年の前半にお手伝いしていた某社で、スクラム開発の導入をしました。自分が開発チームに入ったタイミングでは、カンバンツールを使って週次で振り返り(KPT)とタスクの状況確認が行われており、特定のチームはなく大きめな機能開発については案件ごとに非エンジニアも含めたチームが組まれているような状況でしたが、以下のような課題感を強く持ちました。

  • 同じタスクがいつまでもカンバンに残り続けていて、リリースサイクルが遅い
  • エンジニア間で機能開発のコンテキストが共有されていないので、レビューコストが高い
  • 1ヶ月以上の期間開発されたのに、仕様変更やビジネス上の都合により結局リリースされないものがある
  • これらの問題を解決・改善していけるような仕組みがない

この状況を改善するためにスクラム開発の導入を進めたのですが、どんなふうに始めていったか自分の振り返りも兼ねて、またこれから始める人の参考になるようにまとめておきます。

スクラム開発始める前編

現状の課題感を言語化する

既に箇条書きで当時感じた課題を書いちゃってますが、自分の課題感はリリースサイクルが遅い というところから始まっていて、それを徐々にWikiにまとめて言語化していきました。スクラム始めよう!となると全てがスクラムで解決できそうな気がしますが、言語化してみると例えばリリースサイクルが遅いというのは実は単純にプロセスの問題だけじゃなくて、デプロイフローの問題やアプリケーションの品質の問題だったりチームのサイズの問題だったりということが見えてきます。スクラムで何を解決しようとしているのかというのをハッキリさせる意味でも、一度何が問題なのかを文章にしたのは良かったです。

期待値調整をちゃんとやる

期待値調整というと語弊があるかもしれませんが、スクラム開発をやるにあたって「スクラムをやればこれが解決します!」という言い方はしないように心がけました。そこの期待値だけが高くなっちゃうと、「スクラム始めたけど全然見積もり通りいかない / リリース早くならないじゃん」みたいな感想を持ってしまう人が出てくるからです。スクラムのプロセスを踏襲したから何かが良くなるわけじゃなく、計画・振り返りのサイクルのなかで自分たちで良くしくんですよと言う伝え方を心がけました。

  • スクラム開発のプロセスをやったから今の課題が解決するわけじゃないですよ
  • スクラム開発でわかるのは「いまなにができていないか」ですよ
  • 課題を解決するのはスクラム開発じゃなくて自分たちですよ
  • 少しずつチームで改善していきましょう

こういうコミュニケーションを心がけていたので、スクラム開始後も自然と「振り返りで改善していきましょう!」という雰囲気ができていたのかなと思います。

スクラム開発始めるとき編

インセプションデッキをやる

インセプションデッキはアジャイルサムライで紹介されているプロジェクトの全体像を示すためのドキュメントです。ドキュメントというと誰かリーダー的な人が作って配布するようなイメージを持つかもしれませんが、ワークショップ的にチームで集まって作っていくことに意味があるものだと自分は思っています。

インセプションデッキのテンプレートは以下からダウンロードできます。 github.com

割と新規ビジネスとか新規事業とかを念頭にテンプレはつくられている感じがするので、適宜差し替えて使いましょう。例えば既存のプロダクトがあってチームを作っていく場合には、エレベータピッチとかは不要かも。自分はチーム名を考える項目を入れたりしました。 The Agile Inception Deck | The Agile Warrior

自分はインセプションデッキのなかでも 我々はなぜここにいるのか俺たちのAチーム の項目が好きです。自分も自分でやってみるまではインセプションデッキやることにどこまで意味があるのか懐疑的だったんですけど、今までやった全てのチームで「チームの目指すゴールが明確になる」「何を重視するかという価値観が揃う」感覚があり、何よりチームとしてやってやるぞ!という気持ちになったのでチームのキックオフにはお勧めです。

とりあえず最初はスクラムガイドに書いてあるとおりやる

これよく言われてるやつですが、まずは守破離の守ということで、スクラムガイドの内容をカスタマイズしたりせずやりました。個人的に、まずはカスタマイズせずにやってみることで良かったのは、「これは何のためにやってるんだっけ?」というのをスクラムガイドに求めることができたことかなと思います。特にスクラムで開発を始めた当初は「これは何をしていく会だっけ?」とか、「これは何のためにやっているんだっけ?」という疑問・質問が多発するので、そんなときにスクラムガイドをもとに説明できます。またとりあえず型通りにやってみることで、スクラムガイドで目指していることをチームで体系的に理解できていく感じがしました。

まとめ

スクラム開発を始めて2ヶ月くらいのタイミングで契約を終了してチームから抜けてしまったので正直あまり偉そうに何か成果を語れるわけではないのですが、外から様子を見る限りとても強いスクラムチームになっているようで、自分のやったことが少しでも貢献につながっていれば嬉しいなと思っています。本記事では導入までをざっとポイントを絞って書きましたが、スクラム開発をこれから始めようとしている人たちの参考になれば幸いです。

参考文献

スクラム現場ガイド -スクラムを始めてみたけどうまくいかない時に読む本-

スクラム現場ガイド -スクラムを始めてみたけどうまくいかない時に読む本-

The Agile Inception Deck | The Agile Warrior

スクラムガイド

スクラムリファレンスカード

MacBook Pro (15-inch, 2018)のディスプレイが黄ばむようになったので修理にだしてた。

この記事は suusan2go Advent Calendar 2019 - Adventar の1日目の記事です。

suzan2go.hatenablog.com

この記事から3ヶ月も立たずに、今度はディスプレイが黄ばむようになってしまった。 状態としてはこんな感じ。

f:id:suzan2go:20191202170424j:plain

家に返ってきてリビングでMBPを触っていたときに、妻から「なんか画面焼け起こしてない??」と言われて気がついた。 「Night Shiftのオンオフを繰り返すと良くなる」なんていう情報もあり、そんなバカな…と思ったのだけど、試してみたら実際直ってしまったのは笑った。

仕事しているときはIDEの画面をMBPの画面で開きっぱなしにして、ブラウザやコンソールのログなんかを外部ディスプレイに映すというスタイルなんだけども、これで直るということはそのせいか・・・??と思ったけど、翌日また黄ばみ(しかも今度は反対側)が発生していたので、おとなしくApple Storeへ。

ジーニアスバーで

店員さんに見せたところ、以下のようなことを教えてもらった。

  • この症状はディスプレイの圧迫によって起こるパターンが多い
  • 特にMBPを頻繁に持ち運びする人によく見られる
  • リュックなどに縦で入れていた場合に、下側がこうなる場合が多い
    • かばんは下の方に物がたまるので、圧迫されやすいらしい

思えば、日によって黄ばみのでる箇所が違ったのだけど、たしかにかばんにいれるときの向きに依存していた。Night Shiftのオンオフするとよくなったように見えたのは謎だけど、圧迫が原因なら単純に時間経過でよくなっただけなのかもしれない。

今回もAppleCareに入っていたので無料でした。金曜の夕方にストアにあずけて、月曜の昼に自宅に返ってきました。

フリーランスになって、MBPを持ち運ぶ機会が激増したせいか、今年はMBPの不調が多くて困る… 懐古厨みたいな発言になってしまうけど、自分が大学院生のときに使ってたMBPは全然壊れなかったのになーというところまで書いて、そのMacも使い始めて二年目にHDDが不調になって取り替えたことを思い出した……

MacBook Pro (15-inch, 2018)のキーボードがバグるようになったので修理にだした。

ここ1〜2ヶ月、異常にtypoが増えて困っていた。

こちらの修理プログラムの対象であることは知っていたのだけど、一つ一つキーボードのキーを打っていっても再現しなく、ビックカメラAppleカウンターにもっていっても様子見ということになってしまっていた。

support.apple.com

あるときにキーの中心を外して打つと再現することが分かったので、速攻でジーニアスバーを予約して修理の依頼をした。

ジーニアスバーで

店頭でちゃんと上記の挙動が再現するかとても不安だったけど、無事再現。スタッフの方と話していると以下のようなことを教えてくれた。

  • バタフライキーボードの不具合はとても多い
  • 原因のほとんどは隙間にはいったホコリやゴミ、キーのすり減りが原因
  • 無償で交換可能だが同じ構造のものになるので、また同じ不具合が起こる可能性がある

修理にだした場合には1週間くらいかかるとのことだったが、木曜にだして土曜に戻ってきた。

キーボードの不具合をソフトウェア的に解決する

キーが二重に入力される問題についてはソフトウェア的に解決する方法もある。

github.com

github.com

自分はどちらも試したが、Unshakyのほうが高機能でキーごとに設定ができたりするので良さそうな感じだった。使わないときにくらべるとだいぶtypoは減ったものの、すり抜けて入力されてしまう場合もちょいちょいあり完璧というわけではないので、修理プログラム対象の場合には素直に修理にもっていくのがよさそう。

まとめ

ここしばらく本当にtypoが多くて自分がおかしくなったのかと心配になってたんだけど、無事?キーボードの不具合ということがわかってよかった。構造上また同じ問題がおこる可能性があるということなので、いままでAppleの純正のキーボードを使い続けてきたが、少なくとも仕事でコードを書くときには別のキーボードを使って行く気持ちになっている。