同じものを見てもらおう。弱さも見せよう。

とあるチームのリーダー的な人たちから「ちょっと相談したいことがあるんですけどいいですか?」って言われて「はいー。暇ですよ」って話を聞いた。

## 相談

「実は、進捗が遅れているので、どうしたらいいかと思いまして」

うん。

リカバリーするために、こういう風に動いてもらえないか?とメンバーに提案しようかなと思うんですけど、本当にこれでいいのか迷ってて・・・どう思いますか?」

## うーん

まず、分かってると思うけど、頑張りによるリカバリーは、ないよね。だって、今まででもチームは手を抜かずに全力で一番良いと思う方法でやってるもんね。

だから、リカバリーとして考えられるのは2つ:

  • スコープを削る
  • もうちょっと良いやり方がないか実験してみる

今提案しようとしてるのは、後者ってことよね。それは悪くないと思うし、メンバーも従ってくれるとは思うよ。でも

## それよりも

そもそも「メンバーが自分たちと同じ思いを持ってくれないと思ってること」が問題なんじゃないかな?

あなたたちは「遅れているのを何とかしたい」と思っているのに、それを「メンバーは思ってくれない」と思ってるから、自分たちが決めて伝えないといけないと思ってるように見える。

## 同じものを見てもらおう

「遅れているのを何とかしたいと思ってる」ってみんなに伝えてないなら伝えよう?そして、その理由も伝えよう?

## 弱さも見せよう

それから「何とかしたいと思って、こういう案を考えたんだけど、迷ってる」って素直に相談して一緒に考えよう?きっとそのチームなら大丈夫だよ。

## ちょっと離れたところから

見てたら、チームはそのリーダーたちの気持ちを理解して、どうするのが良いか試行錯誤しだしたみたい。もうちょっと揉めたりしたら面白かったのに、良いチームでちょっとつまらんw

そのふりかえりの改善策って実現可能なのかな?

大変だったプロジェクトの反省会みたいな振り返りとかで、うまくいかなかったことだけを並べて「反省しています!次からはそうならないように、これこれといった対応をしていきたいと思います。」みたいなのをたまに見る。

そういうときに感じるのは「良かったところを知りたいなー」ってのと「そもそもその改善策って、実現可能なのかな?」ってこと。

## 信頼していること

そもそも僕は、全員が全力でプロジェクトを成功させようとしていたこと、良いものを作ろうとしていたことを信頼している。誰も手を抜いていたわけじゃない。

だから、たくさんの良かったことをまず知りたい。この判断は良かったよね。とか、ここは大変だったけどなんとかなったね。とか。

## 課題

そのうえで、思った通りに進まなかったということなので、課題を出していく。例えば、Bが思っていた以上に難しかった。とか。

## それって改善になる?

さて。その課題に対する改善案として「Bが思っていたより難しかったから、Bを先にやっていれば良かった」と出てきたとして、気になるのは「じゃあどうしてそのときはBじゃなくてAを先にやることにしたの?チームとしてそれがベストだと判断したんだよねぇ」ということ。

Aの方が複雑だったから?ふーん。じゃあ、Bを先にやってたら、Aが問題になったんじゃないかな?Aを先にやったのはベストな選択だったんじゃないの?ということは、それは「Aを先にやってリスクを減らすことができた」という良いところだよね。すごいね。

てことは、今回、何かできたことはあったのかな?チームとしてはベストを尽くせたんじゃないかな?

## そのうえで

じゃあ、次にチームがこのプロジェクトをやるとして、そのベストを伸ばすにはどういうことをしていれば良いかな?

「AやBについての知識を普段から学んでおく・・・こと?」

あぁ、それは良さそうだね。毎朝、少し勉強の時間をとって、AとBのことをもっとよく知っておくと良いね。

プロジェクトが始まる前に終わらせておくこと

特に、何か自分たちの未だ知らない新しいことに挑戦する必要があるプロジェクトでは、プロジェクトが始まってからその新しいことを調査し始めると苦労する。

例えば、新しいフレームワークに載せ替えるときに、プロジェクトが始まってから新しいフレームワークの調査を始めたのでは時間がかかりすぎるし、コンテナ化をするというプロジェクトが始まってから、Dockerやk8sを調べ始めたのでは負債の多いシステムになってしまう。

こういった課題に対しては、普段からプロジェクトとは別で学習の時間をとっておくと良いと思ってる。

例えば、現在のプロジェクトでは不要だけど今後必要になりそうなフレームワークに触れておいたり、コンテナを使ってそのメリットやデメリットを実感しておいたりする時間をとっておく。

すると、プロジェクトの開始時点で既に、ある程度の知識があるので、それを実践レベルに持っていく部分に集中できる。

コンテナ化プロジェクトが始まるときには、既にコンテナ化しやすいアーキテクチャーに変更済みだとやりやすいし、欲を言えば既にコンテナがデプロイされていてあとは外部に公開するだけになっていると、良いんだよなぁと思う。

今日は花粉がつらいなー。

MicronautのBean定義のところらへん

今日は娘達とマリオをしたりしながらスキマ時間で、ぼーっとMicronautのドキュメントの3.1-3.5までを読んで手を動かしてみた。

https://docs.micronaut.io/latest/guide/index.html#ioc

ソースはここ。

https://github.com/bufferings/hello-mn/tree/20190301-ioc/src/main/java/hello/mn/ioc

Javaは11を使ってて、Micronautは1.0.4を使ってる。

❯ sdk current java

Using java version 11.0.1-open

❯ sdk current micronaut

Using micronaut version 1.0.4

## 通常のDI

こういう感じでBeanを定義しておいて:

public interface Snack {
  String name();
}

@Singleton
public class Chocolate implements Snack {
  @Override
  public String name() {
    return "Chocolate";
  }
}

こんな感じでDIして使うことができる。他のインジェクション方法も使えるみたいだけど、コンストラクターインジェクションが好きなので、それ以外は僕は使わないと思う:

@Controller("/hello/ioc")
public class HelloIocController {

  private final Snack snack;

  public HelloIocController(Snack snack) {
    this.snack = snack;
  }

  @Get(produces = MediaType.TEXT_PLAIN)
  public String index() {
    return snack.name();
  }
}

実行するとこうなる:

❯ curl localhost:8080/hello/ioc
Chocolate

## Qualifying By Name

Bean定義が複数ある場合は、インジェクトするBeanを名前で指定できる。Engineを実装したV6EngineV8Engineがこんな風に定義されている場合:

public interface Engine {

  int getCylinders();

  String start();
}

@Singleton
public class V6Engine implements Engine {
  int cylinders = 6;

  @Override
  public int getCylinders() {
    return cylinders;
  }

  @Override
  public String start() {
    return "Starting V6";
  }
}

@Singleton
public class V8Engine implements Engine {
  int cylinders = 8;

  @Override
  public int getCylinders() {
    return cylinders;
  }

  @Override
  public String start() {
    return "Starting V8";
  }
}

@Namedアノテーションを使って、こんな風にインジェクトできる:

@Controller("/hello/nq")
public class HelloNamedQualifierController {

  private final Engine v6Engine;

  private final Engine v8Engine;

  public HelloNamedQualifierController(
      @Named("v6") Engine v6Engine,
      @Named("v8") Engine v8Engine) {
    this.v6Engine = v6Engine;
    this.v8Engine = v8Engine;
  }

  @Get(produces = MediaType.TEXT_PLAIN)
  public String index() {
    return v6Engine.start() + ", " + v8Engine.start();
  }
}

実行するとこうなる:

❯ curl localhost:8080/hello/nq
Starting V6, Starting V8

この辺で「名前のルール」が気になって昼間にメモを残しておいた:

bufferings.hatenablog.com

## Qualifying By Annotation

@Named使う代わりに、自分でアノテーション作ることもできるよ、って書いてあって試した。

@Qualifier
@Retention(RUNTIME)
public @interface V6 {
}

@Qualifier
@Retention(RUNTIME)
public @interface V8 {
}

こんな感じで作ると、"V6"とか"V8"のBeanが取得できるみたい。試しにもう一個作ってみた。

@Qualifier
@Retention(RUNTIME)
public @interface V8Engine {
}

"V8Engine"にしたけどこれも動いた。あんまり使うつもりがないから「アノテーション名で判別してるんだろうなー」って雰囲気だけ理解しておいた。こんな風に使える:

@Controller("/hello/aq")
public class HelloAnnotationQualifierController {

  private final Engine v6Engine;

  private final Engine v8Engine;

  private final Engine v8Engine2;

  public HelloAnnotationQualifierController(
      @V6 Engine v6Engine,
      @V8 Engine v8Engine,
      @V8Engine Engine v8Engine2) {
    this.v6Engine = v6Engine;
    this.v8Engine = v8Engine;
    this.v8Engine2 = v8Engine2;
  }

  @Get(produces = MediaType.TEXT_PLAIN)
  public String index() {
    return v6Engine.start() + ", "
        + v8Engine.start() + ", "
        + v8Engine2.start();
  }
}

実行するとこうなる:

❯ curl localhost:8080/hello/aq
Starting V6, Starting V8, Starting V8

まぁ、複数の実装が共通のプレフィックスを持ってる場合とかに便利なのかな?バージョン番号とかでV1V2の2つの実装が沢山ある場合に、このアノテーションを作っておけば便利そうではある。あー、でも、Microserviceって文脈なら、もうそれは別のサービスに分けてルーティングをバージョン分けした方が良さそうか。ま、こういう機能があるんだなってくらいでいっか。

## Primaryアノテーション

複数の実装がある場合に、Primaryアノテーションを使うとそのBeanが優先される。逆の意味のSecondaryもあるみたい。

public interface ColorPicker {
  String color();
}

@Primary
@Singleton
public class Green implements ColorPicker {

  @Override
  public String color() {
    return "green";
  }
}

@Singleton
public class Blue implements ColorPicker {

  @Override
  public String color() {
    return "blue";
  }
}

普通に使うと、PrimaryのGreenがDIされる。

@Controller("/hello/pt")
public class HelloPrimaryTypeController {

  private final ColorPicker colorPicker;

  public HelloPrimaryTypeController(ColorPicker colorPicker) {
    this.colorPicker = colorPicker;
  }

  @Get(produces = MediaType.TEXT_PLAIN)
  public String index() {
    return colorPicker.color();
  }
}

実行するとこうなる:

❯ curl localhost:8080/hello/pt
green

んー。テストのときに上書きしたくてやるぐらい?しか用途を思いつかん。あー、でもあれか、Blueの方は名前を指定すれば利用できるから、通常はGreenを使うけどたまにBlueを使いたいときとかに便利なのか。ふむふむ。

## Container Type

複数のBeanが定義されているときに、IterableとかArrayとかが使えるみたい。あと、Optionalを使うとBeanがない場合にはemptyになるみたい。他にもいくつか対応してるみたい。まぁ、これもあんまり使わんと思うけど一応試しておいた。

@Controller("/hello/ct")
public class HelloContainerTypeController {

  private final List<Engine> engines;

  private final List<ColorPicker> colorPickers;

  public HelloContainerTypeController(
      List<Engine> engines,
      List<ColorPicker> colorPickers) {
    this.engines = engines;
    this.colorPickers = colorPickers;
  }

  @Get(produces = MediaType.TEXT_PLAIN)
  public String index() {
    return engines.stream().map(Engine::start).collect(Collectors.joining(", ")) + ", "
        + colorPickers.stream().map(ColorPicker::color).collect(Collectors.joining(", "));
  }
}

実行するとこうなる:

❯ curl localhost:8080/hello/ct
Starting V8, Starting V6, blue, green

## BeanContext

IoCのコンテナはBeanContextなのかな。実際はそれを継承したApplicationContextがあって、それを実装したDefaultApplicationContextが使われてる。

こんな風にコンストラクターでInjectしてみたら使えた。

@Controller("/hello/bc")
public class HelloBeanContextController {

  private final BeanContext beanContext;

  private final ApplicationContext applicationContext;

  public HelloBeanContextController(
      BeanContext beanContext,
      ApplicationContext applicationContext) {
    this.beanContext = beanContext;
    this.applicationContext = applicationContext;
  }

  @Get(produces = MediaType.TEXT_PLAIN)
  public String index() {
    return beanContext.getBean(Engine.class, Qualifiers.byName("v6")).start() + ", "
        + beanContext.getBean(Engine.class, Qualifiers.byName("v8")).start() + ", "
        + beanContext.getBean(ColorPicker.class).color() + ", "
        + applicationContext.getBean(Engine.class, Qualifiers.byName("v6")).start() + ", "
        + applicationContext.getBean(Engine.class, Qualifiers.byName("v8")).start() + ", "
        + applicationContext.getBean(ColorPicker.class).color();
  }
}

実行するとこうなる:

❯ curl localhost:8080/hello/bc
Starting V6, Starting V8, green, Starting V6, Starting V8, green

どうなんかなー。使うなら、ApplicationContextをDIしといたらいいんかな。

## ということで

3.5までを読みながら触ってみたお話でした。

普段は特に複数Beanを定義することもないだろうから、シンプルにBeanを定義してそれをDIして使う、ってぐらいだと思ってる。しばらく時間が取れなさそうだから、また気が向いたときに続き読もうと思う。

MicronautはどうやってNamedアノテーションでBeanを取得してるんだろう?

最近、なんとなくMicronautを触り始めてる。

micronaut.io

## 気になった

順番にドキュメントを読んでいってて、この部分が気になった。

https://docs.micronaut.io/latest/guide/index.html#qualifiers

import javax.inject.*

interface Engine { 
    int getCylinders()
    String start()
}

@Singleton
class V6Engine implements Engine { 
    int cylinders = 6

    String start() {
        "Starting V6"
    }
}

@Singleton
class V8Engine implements Engine { 
    int cylinders = 8

    String start() {
        "Starting V8"
    }
}

@Singleton
class Vehicle {
    final Engine engine

    @Inject Vehicle(@Named('v8') Engine engine) { 
        this.engine = engine
    }

    String start() {
        engine.start() 
    }
}

Engineの実装として、V6EngineV8Engineが定義されていて、@Namedアノテーションで名前を指定してInjectできるよ、という部分。

この「名前」の部分、どういう動きでv8だけで取得できるんだろう?というのが気になった。v8engineだったらクラス名かなーと思うんだけど。

## とりあえず動かす

サンプルコードはGroovyかな?なので、セミコロンを後ろにつけたりして、手元にあったHelloControllerコンストラクターインジェクションで差し込んでみた。

package hello.world;

import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.*;
import javax.inject.*;

@Controller("/hello")
public class HelloController {

  final Engine engine;

  HelloController(@Named("v8") Engine engine){
    this.engine = engine;
  }

  @Get(produces = MediaType.TEXT_PLAIN)
  public String index() {
    return "Hello World. " + engine.start();
  }
}

interface Engine {
  int getCylinders();
  String start();
}

@Singleton
class V6Engine implements Engine {
  int cylinders = 6;

  @Override
  public int getCylinders() {
    return cylinders;
  }

  @Override
  public String start() {
    return "Starting V6";
  }
}

@Singleton
class V8Engine implements Engine {
  int cylinders = 8;

  @Override
  public int getCylinders() {
    return cylinders;
  }

  @Override
  public String start() {
    return "Starting V8";
  }
}
  • 確かにv8だけで動く
  • v6を指定するとV6Engineになる
  • v8engineでも動く
  • 前方一致なのかな?と思ってv8engにしてみたら動かない

ほほー。

## 実装クラスに名前を指定

@Namedアノテーションをつけると実装クラスに名前をつけることができる。

@Singleton
@Named("sss")
class V8Engine implements Engine {
  int cylinders = 8;
  • sssでInjectできる
  • v8ではInjectできなくなった
  • v8engineでもInjectできなくなった

なるほどー。

## ということで実装を探した

ここだな。

https://github.com/micronaut-projects/micronaut-core/blob/afc3fb377fea37f942c1a251be19b58f9f989b22/inject/src/main/java/io/micronaut/inject/qualifiers/NameQualifier.java#L60-L72

String typeName;
  AnnotationMetadata annotationMetadata = candidate.getAnnotationMetadata();
  // here we resolved the declared Qualifier of the bean
  Optional<String> beanQualifier = annotationMetadata.findDeclaredAnnotation(Named.class).flatMap(namedAnnotationValue -> namedAnnotationValue.getValue(String.class));
  typeName = beanQualifier.orElseGet(() -> {
    if (candidate instanceof NameResolver) {
      Optional<String> resolvedName = ((NameResolver) candidate).resolveName();
      return resolvedName.orElse(candidate.getBeanType().getSimpleName());
    }
    return candidate.getBeanType().getSimpleName();
  });
  return typeName.equalsIgnoreCase(name) || typeName.equalsIgnoreCase(name + beanType.getSimpleName());
}
  • 実装クラスに@Namedがついてる場合は
    • 1-1. 指定した名前が、@Namedvalueと一致すればOK
  • 実装クラスに@Namedがついていない場合は
    • 2-1. 指定した名前が、大文字小文字を無視して実装クラスのSimpleNameと一致すればOK
    • 2-2. 指定した名前 + BeanTypeのSimpleNameが、大文字小文字を無視して実装クラスのSimpleNameと一致すればOK

そっかー。だからv8だけを指定した場合は、2-2で、v8Engineインターフェース名がくっついたものと一致してるってことか。

すっきり。

#scrumosaka スクラムフレームワークを使用する具体的な方法。僕の場合。(後編)

## 前編はこちら

bufferings.hatenablog.com

前編では「1. チーム」と「2. スプリントの内側」の話を書いた。後編は「3. スプリントの外側」と「まとめ」のお話。長くなったー!

## 3. スプリントの外側

お試しスプリントで小さく試してみたときとかは良い感じなんだけど、いざ実際にやり始めてみると「あれー。ここどうしたらいいんだろう?」って悩むことが結構あった*1。そういうのってスプリントの外側の話が多い。その中のひとつが「長いプロジェクト」。

## 3-1. 長いプロジェクト

f:id:bufferings:20190224120241p:plain:w600

絵の大きな円の部分。毎回リリースすることができないような長いプロジェクトの場合はフェーズの切り分け方を考える必要がある。

### フェーズの切り分け方

f:id:bufferings:20190224120419p:plain:w600

右側の付箋みたいなのじゃなくて、左側の網掛けみたいなので進める。最初の頃は、右側みたいに進めたこともあるけど、これだと開発やテストで得た発見や知識を元に設計を改善していくことが難しくてムダが多かったなと思う。短いウォーターフォールみたいな*2

左側の網掛けの意味は、常にすべてのフェーズをやる、ということ。テストや実装からのフィードバックを得て設計をブラッシュアップしたり、実際に動くものを見て方向転換をしたりする。そのフェーズの山はだんだん設計→開発→テストに移っていく。ドキュメントでフェーズをつなぐことがないので、ドキュメントは最終的に揃っていれば良い。

ちなみに、チームの開発とは別で最後にQA(品質保証)と呼ばれるフェーズがある場合もあって、それはQAの専門チームが担当してくれる。開発チームとしては「QAチームにバグを見つけさせない」つもりで自分たちでテストをしておいて、QAチームには最後のチェックをしてもらう。バグが見つかった場合は、QAチーム様・・・ありがとうございます!の気持ち。

そんな風にすべてのフェーズをやるようになると、フェーズが明確に分かれていないことによってスプリントの終わりが曖昧になりやすい。そこで大切になってくるのが「スプリントの終わりを明確に定義する」ということ。つまり、スプリントゴール。

### スプリントゴール

f:id:bufferings:20190224120445p:plain:w600

スプリントゴールは、できるだけ「動くものをステージング環境で動かして、プロダクトオーナーチームに見てもらう」ようにする。動いているものを見てもらうことでフィードバックを得ることができる、というのと、そのゴールを達成できたかどうかが明確に分かる、というのが良い点。

ステージング環境が難しいときは開発用の環境で見せてもいいんだけど、できるだけ本番に近い環境で動かす方が良い。最後の最後で「環境の問題」という落とし穴にはまらないようにするために。

提供する機能は、ひとつずつ前から順番に完成させていくのではなく、ユーザーストーリーマッピングでMVPのスライスを作って、それを上から順番にやっていく方が好き。「作っていったら最後の最後で違和感が出てきた」みたいな状況を減らすことができる。

## 3-2. サービスの運用

僕らはサービス開発だけでなくサービス運用もやってる。サービス運用には、トラブル対応や問い合わせ対応、調査対応などの突発対応が含まれている。

開発だけでなく運用も担当していること自体は、悪くないなと思ってる。というのも、サービス運用のことを考えた開発をすることができるから。これは、実際にサービスの運用をしたことがないとなかなか実感することができない。

ただ、難しいのは、サービスの開発と運用ではサイクルが違うってところ。突発対応が多すぎると、開発のサイクルが乱されて、スプリントゴールの達成が難しくなる。

### 三角形が1次ハンドリング

f:id:bufferings:20190226210418p:plain:w600

三角形の3人が1次ハンドリングを担当する。プロダクトマネージャーがプロダクトよりのことを、プロジェクトマネージャーが組織よりのことを、テックリードが開発よりのことをハンドリングすることが多いかな。お互いに助け合いながら。この時点で、細々としたものはすぐに回答することができる。

そして「これは開発チームが動く方が良い」と判断されたら、開発チームの出番になる。

### 開発チームの出番

f:id:bufferings:20190226210458p:plain:w600

不思議なことに、突発対応は安定して突然やってくる。毎回スプリント内に、ある程度決まった割合で入ってくるのだ。

なので、ポイント見積もりや理想時間見積もりをしている場合は、その実績「前回のスプリントでどれだけのポイント(理想時間)を消化できたか」を使用することで、自然と突発対応の時間が考慮されたものになっている。

それを超えてしまって、プロジェクトの進捗に影響を与えそうなものに関しては、プロダクトオーナーチームに問いかけることになる。「この突発案件の対応をするのであれば、プロジェクトを遅らせるか、スコープを削ることになるが、どちらにするか?」ということを。

開発チームが黙って受け入れて、頑張ってスケジュールをリカバリーするのではなくて、そこはテーブルにのせて全員の問題として相談する。

## 3-3. 他部署とのやりとり

f:id:bufferings:20190226210610p:plain:w600

もう1つ開発のリズムと合わないのが他部署とのやりとり。これも三角形の3人がハンドリングする。

自分のところでは、開発チームは職能横断チームにはなっていなくて、社内の別の専門チームの助けを借りる必要がある。QAチーム、インフラチーム、セキュリティチームなど。なので、それらのチームとのやり取りが必要。

これも別に悪くはないと思ってる。開発チームが全てを見るには範囲が広すぎるし、そこを専門スキルを持った人たちが見てくれるのはとても助かるし安心する*3

ただ、彼らも沢山のアプリケーションチームの相手をしているのもあって、やり取りには時間がかかる。そのチケットを開発チームのタスクとしてスプリントに入れてしまうとスプリントをまたいで残り続けるチケットになってしまって、チームの気が散ってしまう。

なので、これも三角形の3人がハンドリングして、ちょうどチームが必要とするタイミングでスプリントに結果を投げ込むことで開発をスムーズに進めるようにしている。

## 3-4. ディスカバリー

f:id:bufferings:20190226210723p:plain:w600

スプリントとは全く別の時間軸で、プロダクトオーナーチームによってディスカバリーが行われている。アイデアを元にキャンバスを描いたり、ペーパープロトタイプをしたり、ユーザーインタビューをしたり、モックを作ってみたり。そんなことをしながら頻繁に方向転換をして、作りたいものを探っていく。

このとき、プロダクトオーナーチームに含まれているテックリードも重要な役割を果たしている。何かを実験してみたい場合に、技術的にそれが難しいかどうか、どのような方法で実現できるのかどうか、その規模感、などがエンジニアにしか分からないから。

## 3-5. リファインメント

バックログリファインメントは、2つに分かれているなぁ。

### パート1: プロダクトオーナーとテックリードが頻繁に

パート1は、ディスカバリーをしながらプロダクトオーナーとテックリードの2人で行う。そうすることで、プロダクトオーナーがまだふわっと考えているような、開発チームを呼ぶにはまだ早い段階でも、テックリードと相談しながら進めることができる。この段階である程度のアイテムのサイズ感は共有していて、それを元にある程度アイテムを絞り込んでいる。

僕がテックリードしてたときは、プロダクトオーナーと毎日あーだこーだ言いながらFeature DefinitionをReadyにしていってたなぁ。

### パート2: テックリードと開発チームが1スプリントに1回くらい

ある程度方向が固まってきたらテックリードが開発チームを集めて、パート2を始める。プロダクトオーナーは居なくて大丈夫。テックリードはプロダクトオーナーとずっと話をしながらパート1をして認識が揃っているから。みんなからの質問にはテックリードが答えて、プランニングポーカーで見積もりをしていく。

### テックリードの現場感

ちなみに、テックリードはパート1のときでも「これは自分だけで判断するよりも、みんなの意見を今聞いた方が良さそうだな」と思ったときには、いつでも開発チームの力を借りることができる。

テックリードのスキルとしては、チームのみんなに相談しなくても、だいたいみんなの考えと合っているというような現場感が重要になってくる。これによって開発スピードは何倍も変わることになる。

## まとめ

### ふりかえり

ということで、ふりかえってみると、特に図の右側半分の「スプリントの外側」で悩むことが多かったんだよなー。それってどういうことなんだろうなー?と思ったので、その要素を抜き出してみたら、こんな感じ。

f:id:bufferings:20190226223304g:plain

つまり、僕らが悩みながら探してきたのは

  • 真ん中の「計画を持った開発のサイクル」と
  • 上下にある「異なる周波数の様々なイベント」を
  • 「どううまく組み合わせて開発を進めるか」

ということだったんだな。

難しいし面白いのは、これって外にはヒントはあっても答えはなくて、それは自分たちの現場で自分たちの仲間と試行錯誤しながら見つけていくしかないってこと。

### そんなことを考えてたときに

先月開催されたRSGT2019に参加した。特にこの3つのセッションが印象的だった。

confengine.com

confengine.com

confengine.com

この3組が挑戦してることってこういうことだった。

f:id:bufferings:20190226225116p:plain:w600

「どううまく壊して開発を進めるか」。その発想はなかった。・・・でも、考えてみると、それは良さそう。

### ということで僕も2つの実験を始めた

f:id:bufferings:20190226225325p:plain:w600

モブワーク。モブワーク自体というよりも、それによって計画のループを壊せないだろうか?と考え中。この1ヶ月で何回か挑戦してみたところ、良い感触ではあるので、もうしばらく実験してみたい。

f:id:bufferings:20190226225353p:plain:w600

学習セッション。プロジェクトのことを忘れて、今の自分達に役立ちそうなことを業務内の朝の1時間毎日学ぶ時間。これもかなり良い。・・・けど、今はプロジェクトに追われてあんまり取れてないから、時間とるー。

## 最後に

f:id:bufferings:20190226233151p:plain:w300

僕が具体的にやってることの紹介はここまで。で、もしかしたら「いいなー」って思ってくれてるかもしれないし、僕もそこで話を終わりにしておくとキレイかなと思うんだけど、ちゃんと伝えておきたいことがある。

ここまで色んなことを紹介してきたなかで、組織に根付いているのは、僕の勝手な感覚では「2割ぐらい」だと思う。これまで何チームかテックリードとしてリードしてきたし、今は色んなチームのサポートをしたりしてるんだけど、やっぱりその理由までを伝えきれてないとか、日々のプレッシャー、メンバーの入れ替え、マネージャーの方針の違い、などの色んなことが理由で、8割ぐらいはそこに留まらずに流れ落ちていってると感じる。

でも、それでいいかなと思ってる。進んでは戻って、伝えては失われて、そういうことの繰り返しで少しずつ変わっていけばいいかなと。

そもそも、2割も残ってるのってすごいよね。僕が1人であれこれやるよりも、組織としてこの2割の部分って、実感して、染み込んで、強い土台になってるってことで。この土台の上に、ひとつずつまた何かを積み上げていけるんだったら、それってすごいなって。

だから、今、もしあなたが何かを変えようとして、でも全然変えることができなかったり、ほとんど流れ落ちてしまったりして、心が折れそうになったり、悩んだりしてるんだとしたら、そこに残らなかったものじゃなくて、そこにほんの少しだとしても染み込んだものに目を向けると良いんじゃないかなって思う。

それと、このスクラムフェス大阪のようなイベントや、そういったコミュニティに参加すると、同じような気持ちを持った仲間と出会うことができるから良いと思う。

これで、僕のスクラムフェス大阪2019おしまいっ!最高のギャザリングでした!ありがとうございました!

*1:あぁ、今も悩んでるかもしれない。組織の形を考えるという意味で。

*2:とはいえ、長いウォーターフォールをやっていた頃よりはだいぶ良くなった。

*3:残念ながらこの良さを分かっていない人をたまに見かける。

#devkan で喋りましたー「新しいメンバーがチームにやってきた時に行うこと」

devlove-kansai.doorkeeper.jp

「新しいメンバーがチームにやってきた時に行うこと」ってテーマで準備してたら「ペアプロかモブプロやっといたら良さそう」になってしまって「それもなぁ・・・」ってなってたら「そもそもみんなどういうことで困ってるんだろう?」と疑問に思ったので、みんなに教えてもらうことにしました。

出来上がりはこちら。

f:id:bufferings:20190225212500j:plain:w600

DevKanのみんなすごい。やっとむさん直伝のサイレントグルーピング?ってのでみんながもくもくとグルーピングしてくれた!

ドキュメントがないとか、放置されるとか、聞きにくいとかで困った経験があって、だからドキュメントを整備したり、聞きやすい環境を作ったりしといてあげたら良さそうだねーってなって。まぁペアプロとかモブプロとかやってたらドキュメント読んどいて、もなくていいよねーって話。

コミュニケーションの部分の参考までに、自分の考えを共有した。

## 新しい人と仕事するときに僕が気をつけてること

### 話をする

  • あいさつとか
  • 雑談とか
  • うろうろとか
  • 面白がるとか
  • おかしばことか
  • ランチとか
  • 飲み会は好きだけど、飲み会に頼るのは好きじゃない。業務時間中にジュースで良い。

### 否定しない

  • 大切にしているものを壊さない
  • 現状や過去をバカにしない
  • 考えや努力を否定しない
  • 「そう感じている」という事実を否定しない

### 押し付けない

  • 正しさを押し付けない
  • 自分を相手に重ねない
  • 期待を押し付けない
  • 現状を受け入れる
  • 相手が実感していないものを押し付けない
  • 相手の気持ちを想像しない。確認する。

### 問題を見る

  • 誰かのせいにしない
  • 仮想の敵を作ったら楽だけど良くない
  • 立場の違う人を悪者にしがち(マネージャーとか)
  • 顔の見えない人を悪者にしがち(他部署とか)

### 何度も伝える

  • 言葉で何度も伝える
    • 特にポジティブなことは意識的に伝える
  • 態度で何度も伝える
    • コードレビューとか
    • 悩んでるときの相談とか

### 役に立つ

  • ちょっと困ってるところを手助けする
  • スキルがどの程度かを知ってもらう
  • あせらない

## Lofty Goals

チームのビジョンを共有するための「Lofty Goals」についても触れたけど、詳細はこのエントリーに書いてる。

bufferings.hatenablog.com

## プロの無職

ダイアログのときに「忙しすぎる人は声をかけづらくなってしまうよねぇ」って話が出てきて「そういうときは id:m_seki の『プロの無職』って役割良いよー」って紹介しといた。

## 未来会議

部署全体のビジョンを考えるのってどうするのがいい?って話があったので未来会議を紹介した。

## 僕得