宮水の日記

宮水の日記

主に書評や資格取得について記事を書いています。

「オプティミストはなぜ成功するか」を読みました

こんにちは。宮水です!

今回は「オプティミストはなぜ成功するか」という本を読みました。
オプティミストとは、「楽観主義者」という意味です。
自己啓発本はどれも似たような内容なのであんまり好きではなくなっていたのですが、こちらの本は非常に納得させられる内容だったのでブログにしました。
コロナ禍もあって私なりに最近色々と落ち込むことがあり、その際に会社の方にオススメしていただいたので読みました。

楽観主義を身につけるということは、世の中をむやみに明るく見ることではなく否定的でない考え方を学ぶことだ

この本のざっくりまとめ

第1部 オプティミズムとは何か

オプティミストとは、「楽観主義者」のことです。
第1部では、著者がどうやって「楽観主義者」の研究に行き着いたかが書いてあります。
最初は「人はどうして無気力になるのか?」と研究していたのですが、無気力になる人がいる一方、一定数「何があっても絶対に諦めない人」がいることに気がつき、研究を進めていきます。

第2部 オプティミズムが持つ力

楽観主義者の人の仕事ぶりや、楽観主義者の子供達の成績を調査したら、やっぱりオプティミストの方が成績がよかったねって話です。
(この章は面白くなくて途中で読むのやめました 笑)

変身―ペシミストからオプティミスト

では、どのようにしてオプティミストの考え方を身に付けるのか?その具体的な方法が書いてあります。
この章が、根性論が一切なくて素晴らしいです。

落ち込むことがあったときにどう自分に説明するか

「楽観主義者」であるオプティミストの反対は、「悲観主義者」ペシミストと言います。
楽観主義者と悲観主義者は色々と思考が異なります。

永続的 / 一時的

悲観主義者は、何か悪いことが起きたときその悪い出来事が永続的に感じてしまいますが、楽観主義者は一時的なものだと考えます。
「私はもう立ち直れない」(悲観的)/「私は今疲れている」(楽観的)

良いことが起こったときは逆です。
「今日はついている」(悲観的)/「私はいつもついている」(楽観的)

特定 / 普遍的

悲観主義者は、何か悪いことが起きたときその悪い出来事が普遍的なものに感じてしまいますが、楽観主義者は特定のものだと考えます。
「先生は皆不公平だ」(悲観的)/「〇〇先生は不公平だ」(楽観的)
「不愉快だ」(悲観的)/「彼は不愉快なやつだ」(楽観的)

良いことが起こったときは逆です。
「私は数学がよくできる」(悲観的)/「私はよくできる」(楽観的)

外交的 / 内向的

悲観主義者は、何か悪いことが起きたときその悪い出来事が内向的なものに感じてしまいますが、楽観主義者は外交的なものだと考えます。
「私にはポーカーの才能がない」(私が原因、悲観的)/「私はポーカーでついていない」(ポーカーのせいにする笑 楽観的)

良いことが起こったときは逆です。
「チームメートが活躍してくれたおかげだ」(悲観的)/「私が活躍したおかげだ」(楽観的)

憂鬱なときの、男女の思考の違い

女性は男性の2倍もうつ病の患者数が多いそうです。

なぜかというと、女性の方が「反芻」を起こす傾向にあるからだそうです。
どういうことかというと、女性は鬱状態になったとき「なぜ自分がうつ病になったのか」を考えますが、一方で男性は運動をしたり仕事に打ち込んだり、「行動」によって気分を紛らわせる傾向にあります。

男性は、落ち込んだときに気分転換によって負の感情を断ち切るのが上手で、女性は悶々と考え込んでしまう傾向にあり、それが女性にうつが多い原因ではないかと言われているそうです。男性でも、よくよく考え込んでしまう人はいらっしゃるのではないでしょうか?

困ったことがあったときの考え方のクセを直す

どのようにして楽観主義を身に着けるのか?ここでは一つだけ紹介します。
以下のフレームワークを使います。

困ったこと:
思い込み:
結果:
反論:
元気付け:

悲観主義者は「結果」の部分で終わってしまうので、そうではなくてその思い込みに対する反論と自分を元気づけるところまで考えます。
たとえば私だったらこんな感じ。

困ったこと: 最近仕事内容が全く理解できない。
思い込み: 〇〇さんは同じくらいの歳のときにもっとうまくやってるのに、私はなんてバカなんだろう。この仕事は向いてないんじゃないか。
結果: もう辞めてしまった方が周りの人や自分のためなんじゃないかと思い、退職を検討する。
反論: 私は現に、3年間もエンジニアを続けている。それに今回のプロジェクトの構成は初めてやるものだし、わからないことが多くて当然じゃないか。
今までだって、初めて触る技術があっても勉強して少しづつ克服してきたし、きっと今回も時間が経てばそのうち理解しているだろう。
元気付け: ここで諦めたら、もっと後悔することになると思う。わからないことをちゃんと整理して、理解することに集中しよう!

みたいな感じです。自分で書いててめっちゃポジティブな気持ちになってきました。

オプティミストが正義って訳でもない

オプティミストが正義!みたいな書き方をしてきたんですが、いつでもそういう訳ではありません。
楽観的過ぎると慎重にならなさ過ぎたり、重大なことを先延ばしにしてしまったり、わがままになってしまったり、悪い面もあります。
悲観主義者は楽観主義者よりも現実を把握するのが得意ですし、安全管理や設計、契約交渉などには悲観的な観点も必要です。

最後に

今までも、結論だけ似たような自己啓発本を読んできました。
「人や環境は変えられなくても、自分は変えられるよ!」とか、「自分が変われば人生が変わるよ!」とか。そんなのには正直うんざりでした。
でも、この本はとても納得させられる内容が多く、さらに実践的で非常によかったです。

最近コロナ禍もあって、普段ならどうってことない試練が乗り越えられず、憂鬱な気分が続いていました。
「〇〇だし、エンジニアなんて無理だったんだ、もうこれ以上はコード書けない...」なんて思い込みは、もろに「悲観主義者の永続的/普遍的/内向的」の三拍子揃ってました。この本を通して、自分が落ち込んでるときの考え方のクセを冷静に見つめ直すことができました。

自分が自分のことを否定すると、そのネガティブさが周りの人にも伝わってしまって何もかもがうまくいかなくなるような気持ちになります。
何事もバランスですが、きっとこの「楽観主義者」の考え方を実践していれば、人生がもっと楽に、楽しく思える場面が増えると思いました٩( 'ω' )و

ここまで読んでくださりありがとうございました!

Element implicitly has an 'any' type because expression of type '"test1"' can't be used to index type '{}'. Property 'test1' does not exist on type '{}'.ts(7053)

こんな感じのテストを書こうとしていました。

//     { test1: 'test1', 'test2.title': 'test2' } みたいなデータが返ってくる関数をimport
import messages from 'translations/messages';

describe('messeages', () => {
  it('return flattened ja.json data', () => {
    expect(messages['test1']).toEqual('test1');
    expect(messages['test2.title']).toEqual('test2');
  });
});

するとこんなエラーが出ました。
お前がimportしたオブジェクトの中身がなんなのかよくわからんぞ的なことが書いてあるんだと思います。

Element implicitly has an 'any' type because expression of type '"test1"' can't be used to index type '{}'. Property 'test1' does not exist on type '{}'.ts(7053)


こうしたら解決しました。
オブジェクトが持っているkeyの型を定義してあげる必要がありました。

import messages from 'translations/messages';

const test_messages: { [key: string]: any } = messages; // ここをプラス

describe('messeages', () => {
  it('return flattened ja.json data', () => {
    expect(test_messages['test1']).toEqual('test1');
    expect(test_messages['test2']).toEqual('test2');
  });
});
<||

自動生成されたファイルのESLintのエラーでハマったのでメモ

TypeScriptとReactの環境構築中

eslintの設定をしていたら、自動生成されたファイルのlintエラーにめっちゃハマった。

5:1 warning Missing return type on function @typescript-eslint/explicit-module-boundary-types

App.tsx

import React from 'react';
import logo from './logo.svg';
import './App.css';

//ここをfunctionをやめて型をつけた
const App: React.FunctionComponent = () => {
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.tsx</code> and save to reload.
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
      </header>
    </div>
  );
};

export default App;

136:8 warning Missing return type on function @typescript-eslint/explicit-module-boundary-types

140:9 error Promises must be handled appropriately or explicitly marked as ignored with the `void` operator @typescript-eslint/no-floating-promises

143:23 error Unsafe member access .message on an any value @typescript-eslint/no-unsafe-member-access

serviceWorker.ts

type Error = {
  message: string;
};

//   warning  Missing return type on function @typescript-eslint/explicit-module-boundary-types
// functionからconstの書き方に変えた
export const unregister = (): void => {
  if ('serviceWorker' in navigator) {
    navigator.serviceWorker.ready
      .then((registration) => {
       //  error    Promises must be handled appropriately or explicitly marked as ignored with the `void` operator  @typescript-eslint/no-floating-promises
  // 何も指定しないと返り値が any になってしまうので何か明示しましょうねというエラー(らしい)
        void registration.unregister();
      })
       // テキトーに型をつけてあげた
      .catch((error: Error) => {
       // error    Unsafe member access .message on an any value @typescript-eslint/no-unsafe-member-access
       // テキトーに型をつけてあげた
        console.error(error.message);
      });
  }
};

6:9 error Unsafe assignment of an any value @typescript-eslint/no-unsafe-assignment

7:9 error Unsafe assignment of an any value @typescript-eslint/no-unsafe-assignment

7:23 error Unsafe call of an any typed value @typescript-eslint/no-unsafe-call

全体的にVSCodeで宣言先をみて型をつけてあげた。

// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment にしている部分は、試行錯誤した結果ライブラリの型はどうしようもなかったからこうした。

App.test.tsx

import React from 'react';
import { render } from '@testing-library/react';
import App from './App';

interface getByTextFunc {
  getByText: (arg1: RegExp) => string;
}

test('renders learn react link', () => {
  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
  const { getByText }: getByTextFunc = render(<App />);
  const linkElement: string = getByText(/learn react/i);
  expect(linkElement).toBeInTheDocument();
});

雑ですみません\( ˆoˆ )/

「アジャイルサムライ達人開発者への道」を読みました。

今回は、「アジャイルサムライ達人開発者への道」を読みました。

なぜ読んだのか
  • EMにおすすめされたので!
  • スクラムの元となるアジャイルの考え方について楽しく学びたかったから。
対象読者
  • どんな人でも
  • エンジニア初級者にとっては、プロジェクトを進めるにあたって持っておきたい心構えを非常に多く学ぶことができると思います。
  • エンジニア中級者・上級者にとっては、初心に返るのにとてもいいと思います。
この本で学べること
  • アジャイルプロジェクトの準備とキックオフをうまくやる方法。プロジェクトの意義や目的をはっきりさせ、誰のために何をするのか明確にする。
  • 要求を集めて、見積もり、計画を立てる方法。それもわかりやすく、風通しよく、誠実なやり方で。
  • ものすごい勢いで成果を上げていくための手法。プロジェクトを手入れの行き届いた機械のようにして、質の高いリリース可能なコードを生み出し続けられるようになるためにはどうすればいいか。

序章

アジャイル」はフレームワークであり、心構えであり、ソフトウェアを無駄なく、早く届ける手法であるとのこと。

第1部 「アジャイル」入門

第1部では、アジャイルなソフトウェア開発の全体像と、アジャイルチームがどう機能するかを説明します。

第1章 ざっくりわかるアジャイル開発
  • お客さんにとって「価値ある成果」とは何か

それは「毎週動くソフトウェアを届ける」こと

  • 毎週価値ある成果を届けるために大切なこと

①大きな問題は小さくする
 毎週成果を届けられるように。
②本当に大事なことに集中して、それ以外のことは忘れる。
 ドキュメントや実施計画書はあくまでも動くソフトウェアを補完するものでしかない。
③ちゃんと動くソフトウェアを届ける
 そのために、こまめにテストする
④定期的にお客さんからフィードバックを求める。
 間違っていたら軌道修正するために。
⑤必要とあらば進路を変える。
 プロジェクトではいろんなことが起こるので、計画を柔軟に変更する。
⑥成果責任を果たす
 彼らの資金の使い道を示すことができたら、成果責任を果たしたことになる。

  • プロジェクトの3つの真実

1. プロジェクト開始時点に全ての要求を集めることはできない
2. 集めたところで、要求は必ずと言っていいほど変わる
3. やるべきことはいつだって与えられた時間と資金よりも多い

第2章 アジャイルチームのご紹介

第2章は、「アジャイルなチームとはどんなチームか」についてです。

アジャイルチームは肩書きも役割も関係なく、分析、設計、実装、テストといった開発工程が途切れることなく続くということは良く知られていると思います。
でもそれ以上に、"責任"という言葉がたくさん出てきました。プロジェクトを他人事とせず、自分のこととして、責任を持って成果を出していける人。改善案を出していける人。そんな人がアジャイルなチームには必要なんですね。

第2部

第2部は、便利なツールやフレームワークの紹介です。

第3章 みんなをバスに乗せる

プロジェクトが失敗する理由は、主にみんなのゴールやビジョンがあってないから。
それを合わせるために、インセプションデッキというものを使用する。

第4章 全体像を捉える

さらにビジョンを明確にしていくために、エレベータピッチやパッケージデザイン、やらないことリストなどを作っていきます。コラムにあった、「エンジニアが実際の現場に行ってその地域にあった車を作った」といういい話も印象的でした。

第5章 具現化させる

3章と4章で「なぜここにいるのか」ビジョンのすり合わせができたので、5章では「どうやって実現させるか」をみていきます。

  • リスクと向き合う

実現させるにあたって、まずはじめにやることは、「リスクとの向き合い方」です。
チーム内でリスクについて認識合わせを行い、予め手を打っておくことです。

  • 荒ぶる四天王(時間、予算、品質、スコープ)との向き合い方

どれも最優先にはできないので、スコープを諦めてもらう場合があること、優先順位が同じになるものを作らないこと。
スコープ設定や計画に何ヶ月も時間をかける必要はないことなどが書かれていました。

第3部

アジャイルな計画づくりの基礎を扱います。ユーザーストーリー、見積もり、それから現実に合わせて変えられる計画の立て方を解説します。

第6章 ユーザーストーリーを集める

6章は、ユーザーストーリーについてでした。

  • 文章よりface-to-face

- ユーザーストーリーはお客さんにわかりやすいように
データベースにコネクションプールを導入する→現状の10秒かかるページの読み込み速度を2秒まで縮める

  • 制約をつける

ウェブサイトはサクサク表示する→サイトのページはどれも2秒以内に読み込めること

第7章 見積もり:あてずっぽうの奥義

7章は見積もりについてでした。

  • 見積もりで欲しい答えは一つ

このプロジェクトをやり遂げられるのか?

  • 仕事のサイズがどれくらいの大きさなのか

基準より大きいか小さいか「相対サイズで見積もる」のがアジャイル見積もりと計画作りの真髄。

  • プランニングポーカー

> 1人の専門家より、複数の素人の平均の方が正しい(!)という研究結果もある。

やり方:
1 顧客がストーリーを読み上げる
2 開発チームが見積もる
3 開発チームで議論を重ねる
4 再び開発チームが見積もる

第8章 アジャイルな計画づくり:現実と向き合う

8章は、現実と向き合う方法です。

  • 時間が足りないときは

いつまでも最初の計画に執着しない。やることを減らすのがアジャイルチーム。

  • マスターストーリーリスト

プロジェクトでこなすべきToDoリストのこと

  • リリースを定義する

チーム内でリリースの定義を決めておく

第4部

プロジェクト運営。立てた計画を、お客様が実際に使えるソフトウェアとして具現化するための作戦を学ぶ。

第9章 イテレーションの運営:実現させる
  • テストは開発と一緒

 プロジェクトの最後にならないと全体として動作するかわからない状況に甘んじない

  • ユーザーストーリーの詳細は、必要な分だけを、必要なときに分析する
  • カンバン

 プロジェクトの進行状態を"誰でも"把握できるツール。

  • ”価値のあるプロジェクト”

 分析、開発、テストといった作業を一つにまとめあげてこそ。

第10章 アジャイルな意思疎通の作戦
  • ストーリー計画ミーティング

 これから取り組むストーリーの準備が整っているか、顧客と一緒にストーリーの準備が整っているか確かめる。開発チームが見積もりの数値を確認したりするのもこのタイミング。

 開発チームと顧客が一緒になって次回のイテレーションの作業を計画する。ベロシティを確認し、次に取り組むストーリーを整理する。

 昨日やったこと、今日やること、困っていることを毎日チームに共有する。10分くらいに収めるのがコツ。

  • 建設的にフィードバックするコツは"だけど" と言う言葉を避けること。
第11章 現場の状況を目に見えるようにする
  • 状況を見えるようにしておくには

 ストーリーボード、リリースボード、ベロシティとバーンダウンチャート、インセプションデッキなどがあればOK

  • 共通の言語

 プロジェクトで使う言葉を共有する。開発チームも顧客もわかる言葉を使う。
「エリック・エヴァンスのドメイン駆動設計」を読むこと。

第5部 アジャイルなプログラミング

第12章 ユニットテスト:動くことがわかる
  • ユニットテストがあれば、プロダクトに変更を加えても期待どおりであることを確認することができる。
  • 変更を加えるたびに手動でシステム全体のリグレッションテストを実行する手間が省ける。
第13章 リファクタリング:技術的負債の返済
  • 技術的負債は気づいたら背負っている。そうなる前にこまめにリファクタリングする習慣をつけておく。
第14章 テスト駆動開発

 ルール1:失敗するテストを一つ書くまでは新しいコードを一切書かない。
 ルール2:「危なっかしいところ」を全てテストする

  • 既に実装されたコードがあると考えながら、先にテストを書くと良い。
第15章 継続的インテグレーション:リリースに備える
  • ビルド時間は10分以内に留めるのが望ましい。
  • ビルドを大切にすること。ソフトウェアはいつも動くようにしておこう。
  • リリースに備えるとは、心構えのこと。今日書いたコードは今日テストしてリポジトリに統合する。バグがあれば即座に直す。「あとで直す」と先送りにしてはダメ。

感想

ずっと読んでみたかった名著、アジャイル・サムライ読み終わりました!

f:id:kattyan53:20200824200920p:plain
f:id:kattyan53:20200824203534p:plain

プロダクト開発の本質を突きながら、所々ユーモアがあって楽しく読み終わることができました。いやー... 2年前に読みたかったです。
先日「アジャイルな見積もりと計画づくり」という本を読んだのですが、それよりももっと"概念"というか、抽象的なアジャイルのお話でした。

エンジニアになったばかりの人は必ず読んでおいた方がいい本だと思います。

「もっと細かいことわからないと見積もりできないよ!」
「時間がないからリファクタリングは後にしよう」
「テストも後にしよう...」
「CIってなに?よくわからないけど、動いてるからヨシ」
「え!?そんな機能あるなら早くいってよ!」
「このコード、どっから変更したらいいんだ...」

全部私が今までの開発で通ってきた失敗でした... orz
もし、私が2年前にこの本を読んで多少なりともアジャイルの考え方を理解していたなら、もう少し良いアプローチ方法や問題への対処法を考えらえたはずです。

それにしても、アジャイルな考え方って素敵ですよね。この世の仕事って、なかなかアジャイルな考え方で進められる内容の仕事って少ないと思います。
これからどんな新しい機能ができて、どんなコードにしておけばいいかなんて誰にもわからない。
そんなこれから起こる未来にできるだけ対処していくための先人たちの知恵って、めちゃくちゃかっこいいですね。

色々書いてありましたが、一番大切なことは「顧客にとって本当に価値のあるプロダクトを安定して届ける」ということですね。
いろんな事情があると、だんだんと忘れがちになってきますが、このマインド(User Firstな気持ち)を忘れずにこれからも開発に取り組んでいきたいと思います。

「SCRUM BOOTCAMP THE BOOK」を読みました

みなさんこんにちは!宮水です。
今回は、SCRUM BOOTCAMP THE BOOKを読みました。

なぜ読んだのか

以前、スクラムの考え方の元となっている「アジャイル」については以下の本で勉強し、"計画づくり"が大切だということを学びました。
アジャイルな見積もりと計画づくり - 宮水の日記

ベースの考え方を学んだので、続いてアジャイル開発手法の1つである「スクラム」について学んで行こうと思いました。

対象読者

この本で学べること

  • 基礎編では、スクラムの全体像と決められているルールについて学べます。
  • 実践編では、架空の開発現場を題材に開始時から時系列に沿ってスクラムでどう進めていくのか説明します。

注意すること

  • この本でスクラムの全てがわかるわけではないし、書かれていることを鵜呑みにしない。
  • 自分たちの現場ならどうするか考えながら読む。

スクラムってなに?--基礎編--

出てきた用語だけまとめました。
いつも使用している用語と少し異なるものも時々ありましたが、普段からやっている会議や制作物はこういう目的があったのか!と勉強になりました。
本には、もっと詳しく「イベント毎のロールの役割」や「成果物の役割」が書いてあります。

アジャイル開発とは

アジャイル開発とは、似たような開発手法に共通した価値観と行動原則のこと。

スクラムとは

アジャイル開発手法の1つ。
5つのイベント、3つのロール、3つの制作物など最低限のルールのセットで構成されている。
ルールを実際どのように適用していくかは、自分たちで決める。

5つのイベント

①スプリント ... 計画を実行する単位。短く区切って繰り返す
②スプリントプランニング...スプリントで何を達成するかPOから聞く。開発チームはどのように実剣するか計画を立てる。
③デイリースクラム ... 昨日やったこと、今日やること、困っていることを共有する。
④スプリントレビュー ... スプリントで完成したものをデモする。
⑤スプリントレトロスペクティブ ... うまくいったこと、今後の改善点、アクションプランを作る。

3つのロール

①プロダクトオーナー ... プロダクトのWhatを担当する
②開発チーム ... プロダクトのHowを担当する
スクラムマスター ... スクラムの仕組みがうまく回るようにする人。

3つの制作物

①プロダクトバックログ... 実現したいことをリストにして優先順位をつける
②スプリントバックログ ... プロダクトバックログを具体的な作業に分割する
③インクリメント ... スプリント終了時点で完成していて、正常に動作しているものの単位。完成の定義は、チームで決める。

どうやればうまくいくの?--実践編--

実践編では、やりたそうにしていたからという理由だけでスクラムマスタに任命された「ボク君」や、営業部から移籍してきた「キミちゃん」などの登場人物と一緒にスクラムのやり方を学んでいきます。

Scene 1 プロダクトオーナーは誰だ

スクラムには3つのロールがあります。
プロダクトオーナーは、「何のために何をどういう順番で作るか考える人」
開発チームは「POが実現したいことを実際に作る人」
スクラムマスタは「POと開発チームが開発をうまく進められるようにする人」

ロールは単なる目印であり、誰がリーダーとか偉いとかないというのが印象的でした。
誰がどのロールに適任かどうかは、「ロールが求めていることに熱心に取り組んでくれる人」が理想だそうです。

弊社にはスーパーPOがたくさんいるのですが、みなさん営業出身で、ユーザーファーストを具現化したみたいな方が多くいるので、いつも勉強になっています。

Scene 2 どこに進んでいくの?

この章では、「どういうことを実現するのか」「絶対に達成したいことは何か」を決めるためのフレームワークインセプションデッキ」についての紹介です。

ビジネス上のゴールについての質問「エレベータピッチ」とミッションについての質問「我々はなぜここにいるのか」のスライドを作りましょうという話でした。

Scene 3 いつ頃終わるんだい?

この章では、”見通しの立て方”について解説されていました。
プロダクトバックログというものを使って、欲しい機能を列挙します。
優先順位などの開発順序もスクラムチームで考えます。

Scene 4 正確に見積もれません?!

4章は、見積もりについてです。スクラムチームは、「作業の量」で見積もります。
基準となるタスクを設け、それより大きいか?小さいか?でタスクの作業量を見積もっていきます。

Scene 5 僕なんかでいいですか?

5章は"正確な見積もりをする工夫"についてです。

曖昧なことを確実にしようとして膨大な時間を費やしても確実なものにはならない。スクラムでは推測より確実なものに時間を使う。
という文が印象的でした。

見積もりポーカーの紹介がありました。
見積もりポーカーでは、開発者間の認識合わせに役立ちます。認識のズレが数字のズレとして現れます。
見積もりポーカーは数字を合わせることではなく、対話することに意味があります。

Scene 6 いつ何が手に入るのか?

6章では、ベロシティについて解説されていました。
ベロシティは、1スプリントに消化できたポイントのことで、決定するものではなく計測を続けていくものです。

Scene 7 ちゃんと計画できたかな?

7章は、スプリントプランニングについてです。
前回のベロシティを考慮して、今回のスプリントで実現できそうなタスクを積んでいく作業のことです。

どの段階で「完成した」と言えるのかどうかチームで合意を取っておくこと、「これなら確実に達成できるぞ」と自信が持てるくらい具体的で詳細な計画を作るのがポイントです。

Scene 8 スプリントは順調かな?

8章はデイリースクラムについてです。
一日1回、同じ時間に昨日やったこと、今日やったこと、困っていることを共有します。

デイリースクラムは進捗共有会ではなく、問題があればすぐにスプリントの残りの進め方を見直したり、何か対策したりするためにあります。
見積もりも、予想なので何か問題があった場合必要に応じて更新するべきです。

Scene 9 これって間に合うのかな?

スプリントの中で問題になりそうなことを見つけたり、全員のタスクの状況を把握するためにタスクボードを使う。
それぞれのタスクが未着手、着手、完了などそれに合わせて貼っておきます。

Scene 10 だいたい終わってまーす!!

「要望通りに作った」というのは人によって異なることがあるので、実際に出来上がったものをデモして目で見ることが大切です。

上述したように、ここでも「完成の定義」が大切になってきます。
みんなで決めた完成の定義を満たしているかどうか、デモによって明らかにします。

Scene 11 もう1日あれば...

タスクを積んで、スプリント内に終わらず、あと1日あれば終わるんだけど...というケースはよくあります。

そういった場合でも、スクラムではスプリントの期間を変えたりしません。理由は、スクラムチームの実力を正しく測り、納期を予測しやすくするためです。

Scene 12 少し早く終わったぞ!

逆に、予定していたタスクがスプリント期間が終わる前に消化できたとします。
その場合は、次に予定しているタスクをプロダクトバックログから取り、開発を進めていきます。
そのために、プロダクトバックログは開発順・優先順位が高い順に並んでいます。

Scene 13 全員揃ってないけど...

スクラムには、3つのイベントがあります。
スクラムチームは、全員がイベントの目的を理解し、当事者意識を持って"自分達だけで"イベントを進められるようにする必要があります。

スクラムがうまく進んでいないうちは、だんだんイベントに出席しなくなったり、垂れてくることもありますが、そのためにスクラムマスタがいます。スクラムマスタは、スクラムチームの課題に気づかせてあげたり、課題に対してアドバイスをしたりします。

Scene 14 もっと早くできないの?

ベロシティが上がっていると、良い兆候に見えます。
しかし、安定しているベロシティがいいベロシティです。なぜかというと、見積もりもタスクの消化もうまくいっている証拠だからです。

Scene 15 え? POがいないの!?

POには、必ずイベントに参加してもらいましょう。
特に、出来上がったものは必ずPOにレビューしてもらい、フィードバックを得ましょう。
スクラムマスタが、POの支援をしても良いです。

Scene 16 うまく伝わってるのかな?

ロールが違うと、実現したいもののアプローチも違います。
POはユーザー目線が、開発者はどうやって実現するかを考えるのが得意だからです。

開発者に、どういったものを作って欲しいのかうまく伝えるのに、ユーザーストーリーというものがあります。

Scene 17 何かがおかしいぞ!?

スクラムを進めていく上で、チームが何か問題を抱えている気がする...という場合。
本書では、みんながプランニングで決めた期限を守るためにものすごく残業しているとか、完成しそうにないことに薄々気づいているけど、誰も気に留めないなどが問題の兆候としてありました。

そこで、スクラムマスタが「本当に大丈夫か」声かけをしてあげます。問題に気づかせてあげるのも、スクラムマスタの重要な役割です。
そうすると「前に書いたコードが汚すぎて修正が大変」という具体的な問題点が浮かび上がってきました。

Scene 18 すぐに解決できないよ...

スクラムチームは、問題になりそうなことやうまくいってないことに素早く対処して開発を進めていきます。
問題が起きたら、早めに対処するために、タスクボードに問題に対するDoToリストを作り、優先順位をつけて片付けていく方法があります。

Scene 19 今後のことがわからない!?

19章では、プロダクトバックログのお手入れについてです。
プロダクトバックログでは、最初に取り決めた機能や、スプリントレビューでレビューされた意見や、バグ修正など色々なタスクを貯めておくところですが、定期的にお手入れしないと、優先順位がわからなくなってしまいます。

最初のうちは、バックログを定期的にお手入れするイベントを用意すると良いようです。

Scene 20 本当に着手してもいいのかな?

20章は、手戻りを少なくする方法についてです。

作りおえて、スプリントレビューのときに「何か思っていたのと違う!」となってしまっては、もったいないです。そうならないためにも、タスクに着手する前から、仕様を明確にし、実現したいことを深く理解するべきです。

受託で働いていたときは「とりあえず動くものを作らないとお客さんもイメージが湧かない」ということがありました。そういうときは、紙でもパワーポイントでもいいので、プロトタイプを作ると良いそうです。

Scene 21 あれ!? 間に合わない...

ゴールは常に動いていくもの。
リリースが間に合わない場合、品質・予算・期間・スコープの4つのうち、期間の調整かスコープの調整を行おう。

「どう実現するか」考え直すのもスコープの調整といえます。

Scene 22 この作業は苦手です...

22章は、スキルマップについてです。
スクラムチームは自分たちで問題を解決できるし、自分たちで次にやるべきことを見つけられる独立したチームでありますが、個人個人は得手不得手があります。
そこで、スキルマップを作って、開発チームの得意なこと、苦手なことを把握し、チームで協力して開発を進めていくのがいいスクラムチームです。

Scene 23 それぐらいはできるよね?

スプリントゴールの達成へのコミットメントを表明するのは、開発チームであり、他の人が口を挟んではいけません。
一方で開発チームは、責任を持って約束できるようになることが大切です。

Scene 24 やり残したことはないかい?

24章は、リリースについてです。
以前みんなで決めた完成の定義を満たしていても、リリースの定義を満たしてなければリリースはできません。

そこで、リリーススプリントといって、リリースのために必要なタスクを洗い出すためのスプリントがあります。ただし、本来はリリーススプリントがなくてもいいようにするのが理想です。

Scene 25 ここからが始まりさ!?

実際の現場はもっと複雑で大変です。
スクラムを導入したからといって、うまくいくわけではありません。
スクラムは、

  • 開発でうまくいっていないことを特定しやすい
  • 問題を解消する機会がある
  • うまく進めるためにやり方を変えられる機会がある
  • 多少やり方を変えても、影響が少ないようになっている

のが特徴です。これを活用できるかどうかは、スクラムチーム次第です。

感想

今回は、SCRUM BOOTCAMP THE BOOKを読みました。
以前「アジャイルな見積もりと計画づくり」を読んでいたものの、スクラムについて絞った本を読んだのは初めてでした。
上記の本より、少し解像度高くスクラムの用語や進め方の基本の型を学ぶことができて、よかったです。

現在私はスクラムで開発をしているチームにいるのですが、すごく良いスクラムチームに所属させていただいているんだなと改めて思いました。
振り返りのときは毎回よかったことも良くなかったこともしっかり振り返られているし、毎スプリントで改善点を出せたり、このまま良いスクラムチームを継続できるように私も頑張りたいです。

本書には、スクラムで困ることあるあるが漫画も交えてストーリー形式で書いてあってわかりやすくて面白かったです。
場面(Scene)ごとに並べてあるので、スクラムを進めていて「困ったな?」と思ったら読み返すと良さそうです。
時々わからなくなったときに、初心に返るためにまた適宜読み直したいと思います。

以上です。ここまで読んでくださりありがとうございました。

Typescript exercises やってみた 8問目

みなさんこんにちは。宮水です。
今日は、TypeScript エクササイズの8に取り組んでみました。

こちらのリポジトリをforkして、cloneして取り組みます。
rootディレクトリで、yarn installしてから問題文にあるRun this exerciseのコマンドを叩くと、答え合わせができます。
github.com

本日の問題

/*

Intro:

    PowerUsers idea was bad. Once those users got
    extended permissions, they started bullying others
    and we lost a lot of great users.
    As a response we spent all the remaining money
    on the marketing and got even more users.
    We need to start preparing to move everything to a
    real database. For now we just do some mocks.

    The server API format was decided to be the following:

    In case of success: { status: 'success', data: RESPONSE_DATA }
    In case of error: { status: 'error', error: ERROR_MESSAGE }

    The API engineer started creating types for this API and
    quickly figured out that the amount of types needs to be
    created is too big.

 PowerUsersのアイデアはダメでした。
 それらのユーザーが拡張された権限を取得すると、他のユーザーをいじめ始め、多くの優れたユーザーを失いました。
 対して、残りのすべてのお金をマーケティングに費やし、さらに多くのユーザーを獲得しました。
 すべてを実際のデータベースに移動する準備を始める必要があります。とりあえず、モックをいくつかやってみます。
 
    成功した場合: { status: 'success', data: RESPONSE_DATA }
    エラーの場合: { status: 'error', error: ERROR_MESSAGE }

  APIエンジニアは、このAPIのタイプの作成を開始し、作成する必要があるタイプの数が多すぎることをすぐに理解しました。

Exercise:

    Remove UsersApiResponse and AdminsApiResponse types
    and use generics in order to specify API response formats
    for each of the functions.

 UserのAPI応答とAdminのApiResponseタイプを削除し、ジェネリックを使用して、各関数のAPI応答形式を指定します。

Run:

    npm run 8

    - OR -

    yarn -s 8

*/

interface User {
    type: 'user';
    name: string;
    age: number;
    occupation: string;
}

interface Admin {
    type: 'admin';
    name: string;
    age: number;
    role: string;
}

type Person = User | Admin;

const admins: Admin[] = [
    { type: 'admin', name: 'Jane Doe', age: 32, role: 'Administrator' },
    { type: 'admin', name: 'Bruce Willis', age: 64, role: 'World saver' }
];

const users: User[] = [
    { type: 'user', name: 'Max Mustermann', age: 25, occupation: 'Chimney sweep' },
    { type: 'user', name: 'Kate Müller', age: 23, occupation: 'Astronaut' }
];

type AdminsApiResponse = (
    {
        status: 'success';
        data: Admin[];
    } |
    {
        status: 'error';
        error: string;
    }
);

function requestAdmins(callback: (response: AdminsApiResponse) => void) {
    callback({
        status: 'success',
        data: admins
    });
}

type UsersApiResponse = (
    {
        status: 'success';
        data: User[];
    } |
    {
        status: 'error';
        error: string;
    }
);

function requestUsers(callback: (response: UsersApiResponse) => void) {
    callback({
        status: 'success',
        data: users
    });
}

function requestCurrentServerTime(callback: (response: unknown) => void) {
    callback({
        status: 'success',
        data: Date.now()
    });
}

function requestCoffeeMachineQueueLength(callback: (response: unknown) => void) {
    callback({
        status: 'error',
        error: 'Numeric value has exceeded Number.MAX_SAFE_INTEGER.'
    });
}

function logPerson(person: Person) {
    console.log(
        ` - ${chalk.green(person.name)}, ${person.age}, ${person.type === 'admin' ? person.role : person.occupation}`
    );
}

function startTheApp(callback: (error: Error | null) => void) {
    requestAdmins((adminsResponse) => {
        console.log(chalk.yellow('Admins:'));
        if (adminsResponse.status === 'success') {
            adminsResponse.data.forEach(logPerson);
        } else {
            return callback(new Error(adminsResponse.error));
        }

        console.log();

        requestUsers((usersResponse) => {
            console.log(chalk.yellow('Users:'));
            if (usersResponse.status === 'success') {
                usersResponse.data.forEach(logPerson);
            } else {
                return callback(new Error(usersResponse.error));
            }

            console.log();

            requestCurrentServerTime((serverTimeResponse) => {
                console.log(chalk.yellow('Server time:'));
                if (serverTimeResponse.status === 'success') {
                    console.log(`   ${new Date(serverTimeResponse.data).toLocaleString()}`);
                } else {
                    return callback(new Error(serverTimeResponse.error));
                }

                console.log();

                requestCoffeeMachineQueueLength((coffeeMachineQueueLengthResponse) => {
                    console.log(chalk.yellow('Coffee machine queue length:'));
                    if (coffeeMachineQueueLengthResponse.status === 'success') {
                        console.log(`   ${coffeeMachineQueueLengthResponse.data}`);
                    } else {
                        return callback(new Error(coffeeMachineQueueLengthResponse.error));
                    }

                    callback(null);
                });
            });
        });
    });
}

startTheApp((e: Error | null) => {
    console.log();
    if (e) {
        console.log(`Error: "${e.message}", but it's fine, sometimes errors are inevitable.`)
    } else {
        console.log('Success!');
    }
});

// In case if you are stuck:
// https://www.typescriptlang.org/docs/handbook/generics.html

答え

以下の5行を関数の上ら辺に追加しました。

type ApiResponse<T> = { status: 'success'; data: T; } | { status: 'error'; error: string; };

type ApiResponseOfAdmin = ApiResponse<Admin[]>;
type ApiResponseOfUser = ApiResponse<User[]>;

type CurrentServerTimeApi = ApiResponse<number>;
type CoffeeMachineQueueLengthApi = ApiResponse<number>;

感想

今回も問題を理解するのにめっちゃ時間かかったんですけど、とにかく`requestCurrentServerTime`関数と`requestCoffeeMachineQueueLength`関数
のunknown型をAdmin用でもUser用でもいいようにジェネリックを使って対応させてくださいという問題でした。

ジェネリックについては、以下のやめ太郎さんの記事が大変わかりやすいです。
4歳娘「パパ、具体的な名前をつけないで?」 - Qiita

今回は、Tの部分がAdminでもUserでもnumberでもOKな、ApiResponseという型を作りました。

type ApiResponse<T> = { status: 'success'; data: T; } | { status: 'error'; error: string; }

ApiResponseOfAdminとか命名イケテなさすぎますねwwww 英語頑張ります😭
ふぅー8問目もめちゃくちゃ難しかった😭 以上です!

Typescript exercises やってみた 7問目

みなさんこんにちは。宮水です。
今日は、TypeScript エクササイズの7に取り組んでみました。
翻訳はGoogle翻訳にぶち込みました。

こちらのリポジトリをforkして、cloneして取り組みます。
rootディレクトリで、yarn installしてから問題文にあるRun this exerciseのコマンドを叩くと、答え合わせができます。すごい!!
github.com

本日の問題

import chalk from 'chalk';

/*

Intro:

    Project grew and we ended up in a situation with
    some users starting to have more influence.
    Therefore, we decided to create a new person type
    called PowerUser which is supposed to combine
    everything User and Admin have.

  プロジェクトが成長し、一部のユーザーがより大きな影響力を持つようになる状況になりました。
  そのため、UserとAdminが持つすべてのものを組み合わせることになっているPowerUserという新しい人物タイプを作成することにしました。

Higher difficulty exercise:

    Define type PowerUser which should have all fields
    from both User and Admin (except for type),
    and also have type 'powerUser' without duplicating
    all the fields in the code.

  UserとAdminの両方からのすべてのフィールドが必要なタイプPowerUserを定義します(typeを除く)。
  また、コード内のすべてのフィールドを複製することなく、タイプ 'powerUser'を持ちます。

Run:

    npm run 7

    - OR -

    yarn -s 7

*/

interface User {
    type: 'user';
    name: string;
    age: number;
    occupation: string;
}

interface Admin {
    type: 'admin';
    name: string;
    age: number;
    role: string;
}

type PowerUser = unknown;

type Person = User | Admin | PowerUser;

const persons: Person[] = [
    { type: 'user', name: 'Max Mustermann', age: 25, occupation: 'Chimney sweep' },
    { type: 'admin', name: 'Jane Doe', age: 32, role: 'Administrator' },
    { type: 'user', name: 'Kate Müller', age: 23, occupation: 'Astronaut' },
    { type: 'admin', name: 'Bruce Willis', age: 64, role: 'World saver' },
    {
        type: 'powerUser',
        name: 'Nikki Stone',
        age: 45,
        role: 'Moderator',
        occupation: 'Cat groomer'
    }
];

function isAdmin(person: Person): person is Admin {
    return person.type === 'admin';
}

function isUser(person: Person): person is User {
    return person.type === 'user';
}

function isPowerUser(person: Person): person is PowerUser {
    return person.type === 'powerUser';
}

function logPerson(person: Person) {
    let additionalInformation: string = '';
    if (isAdmin(person)) {
        additionalInformation = person.role;
    }
    if (isUser(person)) {
        additionalInformation = person.occupation;
    }
    if (isPowerUser(person)) {
        additionalInformation = `${person.role}, ${person.occupation}`;
    }
    console.log(`${chalk.green(person.name)}, ${person.age}, ${additionalInformation}`);
}

console.log(chalk.yellow('Admins:'));
persons.filter(isAdmin).forEach(logPerson);

console.log();

console.log(chalk.yellow('Users:'));
persons.filter(isUser).forEach(logPerson);

console.log();

console.log(chalk.yellow('Power users:'));
persons.filter(isPowerUser).forEach(logPerson);

// In case if you are stuck:
// https://www.typescriptlang.org/docs/handbook/utility-types.html

答え

まず問題の意味を読み解くのに時間がかかりました😇
interface powerUserは作らずに、UserもAdminの特性をもつpowerUser型を作りましょうという意味でした。
つまり、UserだけがもつoccupationもAdminだけがもつroleも持ちます。

  // ココニカイテアッタヨ
    {
        type: 'powerUser',
        name: 'Nikki Stone',
        age: 45,
        role: 'Moderator',
        occupation: 'Cat groomer'
    }

今回のヒントはこちらです。
TypeScript: Handbook - Utility Types

TypeScript provides several utility types to facilitate common type transformations. These utilities are available globally.

ユーティリティ型は、一般的な型変換を容易にしてくれるTypeScriptが用意してくれた型のことだそうです。

今回は、Omitだけ馴染みがあったので、Omitを使いました。

Userのtypeを取り除く & Adminのtypeを取り除く & type: powerUserを追加するという意味の型を作りました。

type PowerUser = Omit<User, 'type'> & Omit<Admin, 'type'> & {type: 'powerUser'};

多分いい感じだと思います。
f:id:kattyan53:20200805084901p:plain

他に回答あったらご教示いただけると嬉しいです!以上です!