心的な負傷からの回復・ケアについて
目的・動機
- 個人的には,過去の不快な出来事について,過去のこととなったにもかかわらず思い出されてしまうことをなくしたい
- 「傷つかないようにするために」というテーマの文献は(質を問わなければ)多く出てくるが,一方で「傷ついた後の回復」について考えられた文献がなかなか出てこない.情報を整理したい
- この情報の偏りそのものも,けっこうストレスに感じることが多い.それも解消したい
- 「傷ついた後の回復」について理解することで,偏りを回避できる検索方法の理解もついでに期待できると思う
- この情報の偏りそのものも,けっこうストレスに感じることが多い.それも解消したい
- おそらくはこういった人の目に届く情報の偏りが故と思うが,傷ついた後の人に対して「その出来事についてあなたは恐怖を感じるべきではなかった(または別の捉え方があったんじゃないの)」みたいな的外れなことを言っているのを,見たり聞いたりしてしまうことがある.そのときに,それは違うのだと,明確に棄却できる状態でありたい(必要なら説明したり文献を紹介したりできるようにする)
- 以下に示す文献にもあるが,重要なのはその出来事をどう捉えるべきだったかではなく,その出来事とは関係がない/関係が薄いことまで恐怖に感じてしまう状況を改善することにある(過去の出来事そのものと想起した記憶は異なるなど)
- なぜその出来事に恐怖を感じたかは最終的には理解することになるが,それは加害者の視点での理解では断じてなく,被害者の視点での理解・整理
- 傷ついた側に,傷つけた側の考え・視点を押し付けようとして傷をえぐるのは,本当に酷いこと
調査内容
- ハラスメント被害者の心理的回復について,小西聖子先生の講演内容が文献となっていたので,まずはそれを元に心理的回復について概観を見た
- 上の文献で,治療法が紹介されていた.特に使われているというPE法について,概要の記載がある文献を閲読した
概要
- 恐怖条件付けの再学習が必要
- 過去の出来事そのものと記憶の想起が異なるものだということを理解する
- 過去の出来事とは異なり,振り返りは実際にその時脅かされるわけではない
- まずは症状を安定させる
- 今の状況の理解,薬物治療など
- 準備が整ったら,トラウマ記憶を整理して,恐怖条件の再学習をする
- 症状が安定しているという前提の上で,今の(過去の出来事とは異なる)出来事や振り返りについて,回避をしないようにしていく
- 回復の目安
- 未来のことも,どういう行動をしたら何が起こるか,加害者と遭遇する可能性があるか考えられる
- 確率が低いこと,遭遇したらどうすればいいか,そういうことを考えることがとても大事
- 心配するのではなく考えるということが回復につながる
- 過去のその記憶の時点にしか生きていない状態から外れて,ゆっくり考えることができることが,臨床的な回復の目安
- 未来のことも,どういう行動をしたら何が起こるか,加害者と遭遇する可能性があるか考えられる
- 加害の視点で捉えてはいけない
- ハラスメントは,アサーティブに対応するなどして回避できるものではない(被害者には選択の余地がない)
- 構造的な問題は上が解決するしかない
考察
- 被害者の視点での,過去の出来事,認知の整理が必要
- 加害者の視点から話されているものが多い
- それらは決して被害者の助けにはならない
- 段階を踏む必要があることを,いきなりやってしまうようなケースは避けるべき
- 状態を理解したうえで,状態に合わせて対応する必要がある
- 過去の出来事そのものと記憶の想起が異なると理解することは,本当に重要
- 想起では実際にそのとき害される恐れはない
- 善意があってもリソースがないときは,良い治療者を紹介するのがよい
- かじった程度の知識で対応できる問題ではなかなかない
- ただし,適切な対応ができない治療者も多くいると思われるので,紹介するなら選定は慎重にする必要がある
課題
- 被引用文献など関係する文献を探す
- 病理の理解
- biologicalな説明
- 学習心理学的な側面からの恐怖条件付け
- リラックスの方法とか呼吸を整える方法とか具体的な方法を見る
- PE法のワークブックとか実践的な部分を学ぶ
参考文献
ハラスメント被害者の心理的回復
- ハラスメント被害の問題は,その後に職場に復帰する,あるいは精神的痛手から回復するというところまで視野に入れる必要
- しかし,この点が軽んじられていたり,また非常に難しい問題でもある
- 医学的,臨床心理学的な背景から事後的な回復に焦点を当てる
- 小西先生の立場
- 臨床側からでしか明確に見えてこない2点
- Point 1. 暴力の心理的影響は,加害者や第三者としての周囲が考えるよりも,ずっと大きいことが多い
- 共感性の不足は,特別の人にだけ生じるわけではない
- 非対称性が,被害と加害の中には本質的に含まれている
- 被害を受けた人がどういう風に感じていて,どういうところに苦痛を感じているかに共感していくことは,技量が必要ということを知る必要がある
- トラウマの分野では,予期せぬ突然の出来事というのは,すごく悪影響を与える
- トラウマとしての大きさのインパクトを増す
- 被害者には選択肢もなく,予期もできないという点で,対称でない
- Point 2. 被害からの回復には周囲が想像するより長い時間がかかる
- 加害行為が止まれば終わるものではない
- 骨折のimage
- 外からの力が,衝撃が心にかかって負担となる
- 治療の仕方が良くないと,元と同じ機能は持てないこともある
- supportが必要
- 二次被害(その後のストレス)
- 多くの人が学校や会社にいけなくなっている
- 年単位の回復経過となる
- 症状
- 振り返れない,話せない,検討などましてできない
- 交渉や,相手に意見が伝わることが怖くなる
- 身体症状や感情のコントロールが悪くなる
- 感情が麻痺
- 合理的に思考している部分と,恐怖で考えることのできない部分が共存する
- 精神医学的に診断をつけるなら,トラウマおよびストレス関連疾患と診断することが多い
- 診断はつかないけれども苦痛はあるという人もいる
- こういった複雑さが被害者の回復を見えにくくしている
- 臨床の専門家としての支援の2つの柱
- ハラスメントの構造を見極め,相談者の人権を守る.自己決定を尊重し,職場との対応や裁判に可能な範囲で協力する
- 被害者の何が苦痛を与えているのか,あるいは,どういうところで引っかかっているのかということを理解する必要
- 何をしたいのか,どういうふうにやりたいのかを知る必要
- トラウマ/ストレスによって生じた心理的反応(時には診断名のつく障害)を軽減し治療し,日常の生活を取り戻す
- ハラスメントの構造を見極め,相談者の人権を守る.自己決定を尊重し,職場との対応や裁判に可能な範囲で協力する
- 事例1.セクシャルハラスメント
- 最初は,本人の感情や経験,時にはそれを感じないこととか覚えていないこと,を批判せずによく聞く
- ここのところで治療者が平気で批判したりして医者からの二次被害になることもある
- 多くの人の場合,自責感が強い
- 誤解の多いところ
- 被害を訴えていても,こういう体験をした人の多くが合理的とは言えない自責感を強く感じている
- 表面だけではないリアルな状況に本当に理解し共感をすることが必要
- 知識が必要
- PTSDについて(例)
- トラウマ記憶や,それを思い出させるものへの回避が非常に強かった
- flashback, 事件のことがそのまま頭によみがえってきて,非常に苦痛
- 恥辱感や無力感が強く思い出されて苦痛なため,それを回避する傾向が強くなっていた
- 侵入症状,回避症状,認知や感情のネガティブな変化,覚醒の亢進というだいたいこの4つが症状
- 1か月以上持続する
- これらに,トラウマ体験と,重要な機能が障害されているという条件で,PTSDと診断される
- トラウマのreminderの回避のために日常生活が困難
- たいがいは電車に乗ることができなくなる
- 道を1人で歩くことが怖くなる
- 電話ができなくなる
- 合理的でない自責の念
- 訴えたことにより相手から攻撃的な感情を向けられることの恐怖
- 侵入
- 記憶が勝手に向こうから,自分の意思と関係なく繰り返しよみがえってくるというような症状
- かなり苦痛を伴う
- 身体的反応が起きたり,心理的な反応が起こる
- かなり苦痛を伴う
- 記憶が勝手に向こうから,自分の意思と関係なく繰り返しよみがえってくるというような症状
- 回避
- 考えない,刺激のそばによらない
- 日常生活を困難にする
- 考えない,刺激のそばによらない
- 覚醒
- いつも緊張
- 不眠,ちょっとした物音にすごく驚いたりというような状態
- 自責感,自分を壊したいというような行動,いらだちなど
- トラウマ記憶や,それを思い出させるものへの回避が非常に強かった
- ケアの方法
- まずは症状の安定化
- 心理教育でよく使う言葉,今その人がどういう状態にあるかを話す
- 薬物治療
- relaxationの方法の教授
- まだトラウマに切り込むことはできない
- 少しずつ今の状況を整理していく
- 共感的でない人にかかると,都合のいいとこだけ怠けてるんじゃないのというようなことを言われかねない状況
- 自分でもちゃんと復帰したいんだけどできないのだっていうことを,ちゃんとわかる,理解するということがとても大事
- このようなことで,だんだんと自分の症状を理解して話せるようになる
- PTSDだということが理解されてきたところで,PTSDの認知行動療法を入れる
- 出来事が考えられると,次は裁判に出ていく
- 診断書が必要になったり,相手の出してきた意見というのを読んだり,それについて反論しなくてはいけないことも多く,被害者にはとてもとても大変な作業
- 相手に罰を与えるためには裁判くらいしかないので,どういうふうなことができるようにならなくちゃいけないかを話し合い,準備することもある
- 裁判への心理的な対応も必要になる
- 事件後2年ほどたった頃に,外出や被害に関係あるところから少し離れた場所に行けるようになった
- 過去のことについて考えられるようになり,アルバイトもできるようになった
- 2年5か月ほどたって,復帰を勧められて復帰したが,被害に関係あるところの近くへの用事があった時に突然flashback
- → またいけなくなる
- なぜそうなったか話し合い
- 自分のことを思ってくれている人の話は聞かなくてはいけないと,従順に思ってしまったことが良くなかったということを確認
- 一時的に具合が悪くなったが,いったん治療した後なので,割と早く症状が軽減
- さらに自分の行動についてよくコントロールできるようになり,4年後に大学を辞めることを選択して就職し,普通に勤務,自活できるようになった
- 被害者から見ると,本当にそれぞれのときにかなりできることは精一杯している
- ただ,時間がすごくかかっている
- 全部が良くなるまで待つと,何年もかかってしまうことも多い
- 回復の目安
- 全部ネガティブではなくなってくる
- 小さい楽しみが出てくる
- この回復の目安がとても重要
- トラウマ体験となったようなこと,過去のことを,ただ恐怖するのではなく,そのことを十分に考えることができる,頭にとどめておいて分析できる
- 未来のことも,どういう行動をしたら何が起こるか,加害者と遭遇する可能性があるか考えられる
- 確率が低いこと,遭遇したらどうすればいいか,そういうことを考えることがとても大事
- 心配するのではなく考えるということが回復につながる
- 過去のその記憶の時点にしか生きていない状態から外れて,ゆっくり考えることができることが,臨床的な回復の目安
- 科学的エビデンスのある治療法の第一選択は,PTSDの場合は,薬物より認知行動療法
- PE(Prolonged exposure)法, CPT(Cognitive processing therapy)
- いずれの治療法も一定の効果はある,特に認知行動療法が一番効果量が大きい
- ただし,薬はなかなか難しい.成績が良くないものもある
- → 放っておくのが一番いけない
- まずは症状の安定化
- 最初は,本人の感情や経験,時にはそれを感じないこととか覚えていないこと,を批判せずによく聞く
- 事例2.パワーハラスメント
- 事例3.セクシャルハラスメント
- 優秀な一方で発達障害,traitsがあるということで,男性なら変わり者で済むようなことが,女性のケースではさまざまな情緒的機能を満たすように暗に要求されることが多い
- そのことで破綻をきたすことがある
- 脆弱性から生じるハラスメント
- 回復していないトラウマがあることで,対人関係が上手くいかず,傷つきやすく,再被害に遭いやすいということもある
- 発症のリスク要因
- 事前の精神障害があるようなときは,ハラスメントに弱くなる
- それはそれとしてハラスメントはハラスメント
- リスク要因が発症に促進的に関わってくる点と切り分けられるものではない
- 脆弱性がトラウマやストレスに関連する精神障害発症の一定のリスク要因となる
- 発症しやすく,症状が重くなる
- PTSDの発症のリスク要因
- 単純に女性であるということ
- 事前のトラウマ歴がある
- 乖離症状が起きたりする
- 何らかの精神障害や,通院歴の既往がある
- 事後の発症リスク要因として,ソーシャルサポートがないこと
- 孤立は非常に良くない
- 発達障害があるような方の場合は,治療しても回復がなかなか上手くいかないこともある
- 脆弱性はリスクを背負いこみやすいが,ハラスメントの加害性は当然ある
- 加害は加害
- 優秀な一方で発達障害,traitsがあるということで,男性なら変わり者で済むようなことが,女性のケースではさまざまな情緒的機能を満たすように暗に要求されることが多い
- Point 1. 暴力の心理的影響は,加害者や第三者としての周囲が考えるよりも,ずっと大きいことが多い
- 指定討論
- 学校の特殊性
- 学生はclientでありながらパワーがない
- 異動がない
- 法的なサポート
- 操作そのものが加害の視点で組み立てられている
- 被害者の視点を謙虚に聞く必要
- 治療しなくちゃ説明できないくらいショックを受けていることを,誰も知らずに整合性がないといわれるのは酷いこと
- 記憶の想起は記憶の改変と同等というのが学術上の1つの答え
- 1つの事実に対して正直に話すか嘘をつくかという捜査の前提自体が間違っている
- 治療しても記憶は変わってしまうが,話す権利を奪われるよりは治療した方が良い
- どういうふうに話せないかということを社会に知ってもらいたい
- 発達障害について
- 本人が自分の状態を分かって治していく,コントロールしていかなくては,リスクが下がらない
- 学校の特殊性
- 質疑応答
PTSD(心的外傷後ストレス障害)の認知行動療法マニュアル(治療者用) PE法のprotocol(概要)
- 以下の書籍の概要となっている.
はじめに
- 理論的基礎
- PE(Prolonged_exposure)は,不安障害のためのエクスポージャー療法の長い歴史と,PTSDの情動処理理論(Emotional_Processing_Theory)に基づく
- Emotional_Processing_Theoryによれば,恐怖は1つの認知構造として記憶の中に表現される
- 恐怖刺激,恐怖反応,刺激に関連した意味,さらにこれらへの反応が含まれる
- PTSDの背景にある恐怖構造の特徴
- 第一に危険であると誤解されている刺激が非常に多く存在すること
- 第二にそうした刺激がPTSD症状に見られるような生理学的な覚醒の亢進と行動的な回避反応と結びついていること
- 2つの広義な否定的な認知(世界はすべて危険,対処するには自分はまったく無力)が,さらにPTSD症状を悪化させて,誤った認知を一層強化する
- PTSDの自然回復と慢性化
- 回復の経緯(自然回復の場合)
- 日常生活の情動処理の結果
- トラウマ記憶が何度も賦活され,トラウマに関連した考えや感情が呼び起され,それをほかの人間と話し合い,トラウマを思い出させるような状況に向き合うことで,否定的な認知(世界は危険であるとか,自分は無力であるとか)が修正される
- 助けになってくれるものを相手にトラウマの出来事について話したり考えることで,事件の記憶の筋道が整理し直される
- PTSDの慢性化は,トラウマの想起刺激を極度に回避したためにトラウマ記憶が適切に処理されなかったために起こる
- → 治療では,情動処理を促進する必要
- PEでは,自然回復のときと同じく恐怖構造が十分に賦活されると考えられている
- 想像および現実エクスポージャーを通じてそのような賦活が生じることで,患者はトラウマに関連した思考,イメージ,状況に意図的に向き合い,自分と世界についての感じ方が不適切であったことを学ぶことができる
- 認知や行動に負の強化が加えられていることでPTSDは維持されているが,PEはトラウマの記憶や想起刺激への直面化を通じてこのような強化を減少させる
- さらに馴化が生じると,回避以外に苦痛をやわらげる方法はないという思い込みは否定される
- 想像および現実エクスポージャーによって,患者はトラウマ的な出来事と,それに似てはいるが危険ではない出来事の区別ができるようになる
- 再び被害を受けるわけではないことが理解されるようになる
- 被害の出来事について考えることは危険ではないと気づき,過去と現在が区別できるようになる
- 繰り返しトラウマ記憶の扉を開いて物語ることで,出来事の記憶の中には様々な別の要素があることに気づき,必ずしも世界が危険だとか自分は無力であると信じる必要はないことが正確に判断できるようになる
- 回復の経緯(自然回復の場合)
- PE全体の概観(手続き)
- 適応
- 一般的には,少なくともトラウマ被害から1か月が経過し,それでもなお強いPTSD症状が持続している患者が適応となる
- トラウマのタイプは不問
- 精神病性障害の診断がある者は,適切な薬物療法によって状態が安定している場合には治療が行われてきたが,体系的な研究はされていない
- 現在もまだ被害に遭うリスクが高い場合,PEは有効であるが,まずは安全を確保し,危険な状況から引き離すことを介入の中心とすべき
- 安全がすべてに優先する
- トラウマの明確な記憶がない,もしくは記憶が不十分である場合に,患者にトラウマを思い出させたり,記憶を取り戻させたりするための手段としてPEを用いてはならない
- 以下の問題のある患者には,実施は可能だが要検討
- 薬物・アルコールの乱用や依存
- 危険な住環境・労働環境
- 第Ⅱ軸障害の存在
- 重度の解離症状
- 罪悪感や恥辱間が顕著なPTSDの場合
- 評価
- 対人暴力の被害者を治療する際の留意点
- 被害者が故意による他人からの重大なトラウマを体験したことを忘れてはならない
- 極度の恐怖を体験し,誰も信頼できないという悲観的な世界観を持っているはず
- 患者から信頼されて被害体験の情動処理を助けていくためには,強力な治療同盟を結ぶことが不可欠
- 被害者が故意による他人からの重大なトラウマを体験したことを忘れてはならない
- 治療の基礎
- 恐怖を引き起こす状況と向き合うことは非常に難しい
- 恐怖を上手に克服するためには,以下の点に留意して治療の土台を築き,患者を助けることが必要
- トラウマ被害者の治療の難しさ
- 患者によっては,PEが終わった後で,必要に応じて別の援助手段の紹介も重要
- PEは人生の問題の一部分を扱っているに過ぎない
- 治療への動機づけ
- 恐怖のために避けてきた記憶やその想起刺激に向き合うことは大変難しい
- 20%-30%の患者が途中で脱落する
- 回避はPTSDの症状の一部なので,治療そのものを回避したいと思うことがある
- 治療を開始した直後や,治療を回避したいという葛藤が生じたときには,常に回避について話し合い,治療への動機づけを高める
- 被害前の生活を覚えている場合には,被害前後の生活を比較することで,患者がPEによってどのように変わりたいのか,どのような生活を取り戻したいのかを具体的に明らかにするのも良い
- 恐怖のために避けてきた記憶やその想起刺激に向き合うことは大変難しい
- 治療者自身のケア
セッションの進め方(抜粋)
- 治療内容の説明
- 情報収集
- 標的となるトラウマを定める
- SUDS: Subjective Units of Discomfort Score
- 現実エクスポージャーを終えることができたら,その経験を踏まえ,記憶に対するエクスポージャーへと導入する
- 想像エクスポージャーの治療原理
- 記憶を処理し,整理する
- トラウマの出来事を「思い出す」ことと,「再びトラウマ被害を受けること」の区別を促進すること
- 馴化を促進すること
- トラウマの出来事とそれに類似する事柄の区別をさせること
- 自己制御とコントロールの感覚を強化すること
- 想像エクスポージャーの実施
- 45-60分間,中断を入れずに患者にトラウマ記憶を語るように依頼する
- 時間が余ったら同じ話を反復させる
- 話すことそれ自体に対する否定的な認知が生じているので,患者を勇気づけ,話したいという事実に基づいて患者の自主性を賞賛し,記憶と現実の危険が違うことに注意を向ける
- 5分ごとにSUDSについて尋ねる
- 感情のレベルの調整のために,開眼-閉眼,過去形-現在形などの使い分けを指示する
- 促しや指示的なコメントで治療を順調に進める
- 患者を支持するために,共感的なコメント及び想起の作業を評価するようなコメントを与える
- 記憶への感情的なかかわりを強めるために,感覚(体感,視覚,嗅覚,聴覚など),思考,などについて簡単なopen questionを用いる.治療者との応答にならないように注意
- 45-60分間,中断を入れずに患者にトラウマ記憶を語るように依頼する
- 想像エクスポージャーでトラウマ記憶の処理を促す方法
- 想像エクスポージャーの処理
- つらい記憶と向き合う患者の勇気と能力を認め,誉め,励ます
- 必要に応じて指示を与え,落ち着かせる
- 今終えたばかりの,想像の中のトラウマ体験に立ち戻るという治療についての,患者の考え,感情を話してもらう
- トラウマの最中とその後の患者の反応や行動が,誰にでも起こり得ることを理解させる
- セッション中または,全体を通して観察された馴化についてコメントする(十分でなかった場合は今後の見通しを与える)
- 思い出された記憶についての考えや感情の変化,あるいは治療者が観察したことを取り上げる
- PTSDを長引かせる原因となるような考えや信念への自覚が深まった場合には,そのことを取り上げる
- 患者が考えていることを自由に話してもらう.治療者の見解を伝えることは控える
- リスクへの対応
- 治療室の中では治療者がいて安全であること,それは記憶であり,今起こっているわけではないことを言葉で話して思い出させる
- 中間セッション
- 最終セッション
患者に応じた治療修正
- 期待したほどに回復しない原因
- 回避
- 情動的関わりが不足または過剰
- 苦痛の情動に耐えられない
- 苦痛以外の否定的な情動(たとえば怒り)にとらわれていて,そのために改善が妨げられている
- 治療モデルの重要性
- 恐怖構造に近づく
- 修正的な情報を提供する
- 恐怖構造の過剰で非現実的な側面をやわらげるような「安全な経験」を提供する
- 現実エクスポージャーの修正
- 階層表の場面をさらに細かい不安の段階に応じて設定し直すとよい
- 時間帯や場所など
- 現実エクスポージャーの最中に実際に何をしているか詳しく見る
- 無意識の回避がないか確かめる
- 階層表の場面をさらに細かい不安の段階に応じて設定し直すとよい
- 想像エクスポージャーの修正
- 患者の興奮または苦痛のレベルを加減する
- under_engagiment
- 短い質問
- 質問しすぎないようにする
- 治療原理を話し合う
- 想起と実際の被害の違いを確認する
- over_engagiment
- 解離している
- 感情に圧倒されている
- 苦痛をコントロールしながら現在という時間に足をつけて踏みとどまるようにすることと,今は安全な治療室にいることを理解したうえで,トラウマ記憶の一部でもよいから話せるように助けることが目的
- 開眼,過去形にしたり,治療者からの発言を増やすことも役に立つ
- 患者に共感し,努力を認めて誉めるような短いコメントによって,患者をそのまま記憶にとどまらせるようにする
- 患者がトラウマを乗り越えて進んできたことや,トラウマ記憶を物語るときは片方の足だけを過去に入れればよく,もう片方の足は現在の治療室に置いたままで良いことを,思い出させるのも役に立つ
- 声に出して語るのではなく,書いてもらう方法もある
- 自分が支えられ,現在に足をつけていると感じてもらうためには,治療者は何をすればよいのかを患者に聞くことも推奨される
『ビヨンド ソフトウェア アーキテクチャ』学習メモ
前書き
- solutionをうまく構築し維持するという目的
- business 課題を理解し,受け入れていく
- 幅広いbusiness上の課題とarchitecture上の選択との関係について議論する
- 誰かに価値をもたらし,持続的に利益を上げる
- techniqueとbusinessの相互関係をmanagement
Ch01 software_architecture
- software_architectureの定義
- important module, process, data, structure, component間の正確な相互関係を積み上げたもの
- どのように拡張できる・されるべきか,どのようなtechnologyに依存しているかも含まれる
- systemの正確なcapabilityと適応性が決まる
- → systemの構築や改修のための計画が立てられるようになる
- 全体的な観点でみることがimportant
- important module, process, data, structure, component間の正確な相互関係を積み上げたもの
- software_architectureに関する一考
- 上記の定義と異なり,「人間」や「business」に焦点を合わせる.
- subsystemは依存性を管理するために設計される
- developers groups間の依存関係を管理しやすくすることを優先
- subsystemは人間のやる気や願望に応じて設計される
- architectureの構築の始まりはやる気や願望の考慮が必要
- column: 全体感を生み出すsubsystemの設計
- 自分の仕事に「全体性」や「網羅性」を感じたいという強い願望
- より上位のarchitectureへの屈服
- 問題領域のフォースのなすがままになることはbestではない
- 自身の経験からの影響も重要
- 美は見る者の目に宿る
- 設計上の決定に関する美学から一歩引いて考えられるdeveloperはごくわずかしかいない
- なぜsoftware_architectureが重要なのか
- 良いarchitectureが長期的な成功には欠かせない要素のため
- 寿命
- stability
- architecture levelで安定していれば,基本部分の手戻りは最小限で済むことが補償される
- → 全体として実装コストは減少する
- architecture levelで安定していれば,基本部分の手戻りは最小限で済むことが補償される
- 変更の度合いと性質
- architectureにより,systemに生じる変更の性質が左右される
- 変更や機能追加が容易 → architectureが優れている
- architectureにより,systemに生じる変更の性質が左右される
- 採算性
- technical architectureの採算性は,architecture自体とほとんど無関係
- marketing, 流通, brandingなどが影響
- 簡潔で洗練されたarchitectureの方が採算性の高いsolutionの基盤には向いている
- technical architectureの採算性は,architecture自体とほとんど無関係
- 社会的構造
- good architectureは構築しているteamにも影響を与える
- どのようなprogramming languageを選んでも,それを使えるようにdevelopment_teamを作っていくしかない
- architectureはdevelopment_teamよりも長生き
- column: architectureを置き換えるのにどのくらい工数がかかるか
- 今のteamの半分の人数で1年以内に以降ができそうなら,新しいarchitectureへの置き換えを真剣に検討した方が良い
- teamを分割し,旧systemの維持と新systemの構築をそれぞれで担うことで,costの増加を最小限に抑えられる
- 旧architectureにかけたコスト全体の40%~60%はかかる
- 今のteamの半分の人数で1年以内に以降ができそうなら,新しいarchitectureへの置き換えを真剣に検討した方が良い
- boundaryを定める
- architectureが最終的に成功するうえで極めて重要
- business固有の要求を満たすため技術的な境界を構築する
- 持続可能で圧倒的な優位性
- most important
- good architectureならば,技術的に洗練されていて,真似もしにくく,競合に対する持続的な優位性を得られる
- good architectureならば,usabilityや性能面などにおいてもadvantageを得られるという点もある
- architectureの構築
- 振り返りの分析を通じて,deliveryされた現実に合わせて当初の設計を更新し,architectureを説明できるようになる
- user/developer両方が制限を理解し管理することで,architectureのcapabilityについての自信を深めていく
- 本物のfeedbackを受け取ることと,それに対して成功に欠かせない変更を行うことを約束することが特徴
- architectureの最適解を見つけるために代替architectureを探すことは現実的ではない
- 金銭的余裕がない
- team_memberの選定時に,十分に経験を積んだ人を選び,代替案の調査をしなくても正しい決定ができるようにすることで対応する
- 問題の性質や周囲の環境
- 実用的な意味でのarchitectureの選択肢が限られる
- 標準的なarchitectureを選択することになる
- より小規模でより特化したものを調査することは,役立つことが多い
- 実際にsystemを使用しないとわからないarchitectureの良さ
- このprocessには長い時間がかかる
- 金銭的余裕がない
- column: 失敗しそうなことに気づくのはいつか
- さまざまなarchitectureの経験を積むことで,実装する前に,大きな失敗をしそうな選択肢がわかるようになる
- pattern, architecture
- 上記のforceが絡み合うため,初期のarchitectureを構築する際には,完全に実用的なapproachをとらなければならなくなる
- software_pattern: あらかじめかみ砕かれた知識
- architecture_pattern: software_systemの基本的な構造を表現
- まず,architectureの技術的側面を扱う
- subsystemについて説明し,それぞれの責務を定義し,特定の問題を解くためにどうやって相互作用するのかを明らかにする
- 問題の特定の側面に的を絞れば,architecture_patternによって,architectureの種類や形式が正しいかどうか判断できる
- まず,architectureの技術的側面を扱う
- software_architectureを構築する際の実用的なapproachは,文書化されたあらゆるpatternを調査し,状況に合うものを選択すること
- 必要に応じてarchitectureをテーラリング(仕立て直し)
- 最終的には,それを動作するsystemの中で実現する
- systemとarchitecture_patternのgapを埋める
- たとえば,あらゆるsoftware_architectureは,適切に設計されたinstall/upgrade process,上手く構築された設定ファイルによる恩恵を享受できる
- これらのapproachをすべて駆使して,成功するsolutionを構築し維持することが目標
- architectureの進化と成熟: featureをとるかcapabilityをとるか
- clientの要望するfeatureや自分たちが望むfeatureと,それをサポートするために必要なcapabilityとの相互作用は,時とともにarchitectureがどのように進化していくかを説明する
- この相互作用は,technologyの継続的な進化とともに起こる
- clientからのfeedbackは,自分たちの役に立ちそうなtechnologyを見つけたことに起因することがあり得る
- development_teamがtechnologyを見つけることもある
- この相互作用は,technologyの継続的な進化とともに起こる
- feature(または機能): productが何をするのか・何をするべきなのかを規定する
- 要求されたfeature全体によって,productの要件が規定される
- usecaseとしてfeatureを記述すると,featureを特定のcontextに当てはめて表現できる
- 最も役に立つcontextは,usecase中のactorの目的に基づくもの
- actorの目的が分かり,systemでその目的を達成できるか分かれば,product managementが価値のある提案をするために必要なデータが手に入る
- この提案が,business model, license model. price modelの根拠になる
- featureはmarketingによってはっきりと優先順位づけされているときが1番管理しやすい
- feature間の技術的な依存関係がdevelopment_teamによってきれいに整理されているときが,1番上手く実装できる
- 優れたarchitecture: 求められたfeatureを比較的容易に実装できるようにするもの
- 関係する一連のfeatureを実装できるようにするarchitectureの基本的な能力: capability
- architectureの成熟と進化の間に起きる相互作用: 時間とrelease cycleを変数とする関数で表現できる
- 負債の元本: 本来あるべきcapabilityを利用して構築した場合と比較したコスト
- column: architectureのcapabilityをとるか,featureのagilityをとるか
- column: entropyの縮小(ER: Entropy Reduction) ~リリースの後に必ず技術的負債を支払う~
- clientの要望するfeatureや自分たちが望むfeatureと,それをサポートするために必要なcapabilityとの相互作用は,時とともにarchitectureがどのように進化していくかを説明する
- architectureへの配慮と手入れ
- 技術動向
- 技術的負債
- 既知の不具合
- licenseの遵守
- vendorの更新情報は忘れずにreview
- 一に原則,二に原則,三に原則
- encapsulation
- interface
- 具体的な実装を超えた抽象化
- e.g. ファイルへの書き込みを抽象化して,抽象interfaceに出力する
- vendor固有のAPIに存在する「使い勝手の良さ」を諦めている
- 具体的な実装を超えた抽象化
- 疎結合
- 適切な粒度
- componentが関連するタスクに応じて
- 高凝集 1つのcomponentやcomponentのgroup内で行われる活動が,どのくらい深く関係しあっているか
- parameter化
- IoC(Inversion of Control)
- componentの制御を外部に任せる
- frameworkやplugin architectureでよく使われる
- IoC(Inversion of Control)
- delay
- 決定をできる限り遅らせる
- architectureへの理解を深める
- development_teamにとって重要な基準: architectureについての知識の確認,説明,communication,修正を,いくつかのやり方で,それもソースコードを読み返すことなくできること
- teamが「全体像」レベルの抽象度で作業するには,少なくとも1つはモデルが必要になる
- Rational社の4+1 model
- logic view
- 時系列上の1点で,システム開発に登場するobjectやentity間のあらゆる関係性を静的な視点で捉える
- 概念モデルと,概念モデルをDB schema上で実装したもの
- process view
- 並行性や同期化にかかわる設計を説明
- physical view
- softwareとそれが配置されるhardwareの対応を記述するもの
- 高可用性,信頼性,耐障害性,性能といった目的を達成するよりcomponentの分散も含む
- development view
- 開発組織におけるsoftwareの静的な構造を表す
- logic view
- → n+1 model
- 各viewを考慮しながらarchitecture上の決定を進める
- viewがもたらすcommunication上の特性を理解し,必要に応じてviewを使用することが水晶
- 時間・労力・お金を費やすに足るarchitectureは,比較的安定した問題,問題領域,business modelこれらすべてに関連する技術的solutionの固有の側面などを解決するもの
- team
- 初期開発を担当するteam
- teamの経験が大きな影響
- できる限り小さいteam
- communicationのoverheadを最小化し,team内のまとまりを最大化
- 3~5人まで
- teamが成長するprocessは,teamとarchitectureが安定するか,経営陣が定めた限界が来るまで継続
- 問題に合わせてteamの規模と構成を決定
- teamとsystemのarchitectureが結びついていることが最も重要
- 初期開発を担当するteam
- まとめ
- software_architectureは全体像に焦点を合わせている
- team構造とsystem構造は相互に結びついている
- architectureは技術的な課題にも技術的ではない課題にも関係する
- architectureが上手くいくための主な要因
- 長寿
- 安定性
- 競合優位性
- architecture_patternは新しいarchitectureを構築する際の出発点として最適
- architectureはfeatureとcapabilityとともに進化する
- architectureには配慮と手入れが必要(庭のように)
- 一度releaseを終えただけでarchitectと名乗る人間には注意
- checklist
- subsystem間の依存関係が明確
- 同じteamでsubsystemを担当しているmemberは互いに仲が良い
- 同じteamのメンバーは,誰もが生産性を改善できると考えるやり方で働いている
- 採算の取れるarchitecture
- 今のreleaseで取り組んでいる課題が進化のためなのか成熟のためなのか理解している
- systemにどれだけの技術的負債があるのか理解している.負債が特定できている
- licenseを遵守してcomponentを使っている
- architectはarchitecture上の選択を推進するためにarchitecture原則をまとめている
- teamの規模は,目的を達成するために大きすぎず小さすぎず適切
Ch02 product_development 入門
- product_managementとは何か
- 成功するsolutionを構築・維持するために欠かせない包括的な一連の活動
- software_architectureの全体像より大きなもの
- なぜproduct_managementが重要か
- 技術的な正しさだけでなく,価格モデルの決定から有用なパートナーシップの構築まで,相互に関係する数々の複雑な活動が,productを全体として成功させるために必要
- 成功するproduct_managementは,本当の意味で利益を生み出すproductと企業を作り出す
- 何よりもclientの声を代表しているために重要
- product_development_process: 1.0版を作る
- product_development_process: productに関する1番大きな全体俯瞰図
- concept提案
- productを製造するのに十分な動機付け
- marketの正当性を証明するうえで十分なbusiness dataと,feasibility(faisible: できる)を証明するうえで十分なtechnical dataが含まれる
- productを製造するのに十分な動機付け
- product 提案/ business_plan
- productの正当性を証明するために作られる主要document
- きわめて重要
- productの正当性を証明するために作られる主要document
- development plan
- marketing_team
- marketの要望を明確化し,優先順位をつけて,開発するproductが備えるべきfeatureとして表現する
- development_team
- feature間の依存関係を分析してarchitectureに求められるcapabilityを見極め
- さまざまなタスクを完了させるのに必要な時間のざっくりとした見積もり
- 技術solutionの評価
- 必要なresourceの整理
- marketing_team
- development
- すべてを1度に開発するわけではない
- 最終品質保証
- すべての基本はテスト
- column: 自動化の目隠しに注意
- 自動テストと最終品質保証は共に必要
- multi platform developmentには,慎重なresourceの割り当てが必要
- 最も主流で重要なplatformをdevelopment_teamが2・3選んで,それに基づいてsoftwareを作り,テストする
- 最終品質保証がそのほかの環境でsoftwareの動作を確認する
- 環境を再構築し直すコストの節約
- 顧客の実際の環境を再現するための投資を避けて,資金の節約
- 最終品質保証によりセキュリティテスト
- 稼働準備
- 本番稼働
- よくある誤解への回答
- waterfallと比べて,productを修正するかどうかを判断するための厳格な評価基準がある
- stagegate
- concept提案とproduct提案/business_planは決して欠かせないstage
- waterfallと比べて,productを修正するかどうかを判断するための厳格な評価基準がある
- business_plan
- productに関係するあらゆる作業の基礎
- 最初のarchitectureを構築するよりも前に策定する
- すべてのreleaseに必要というわけではない
- 表2.1: business_planの書き方
- product_development_process release n.n.nの構築
- product_development_processは,初回release語に一番変化する
- conceptやproduct提案は省略されることが多い
- business_planは,せいぜい,新規releaseの内容を反映するだけ
- MRDはreleaseに関する中心的なdocumentになる
- 稼働準備phaseや本番稼働phaseに求められることが,releaseによって全く異なる
- product_development_processの補強
- 段階的な凍結
- 凍結: 正式な変更管理processを通じてのみ変更できるようにする
- 変更管理protocol
- 変更が行われる前に適切な人物に情報を与え,しっかりと準備できるようにすることが目的
- 変更から影響を受けるstakeholderを絡める
- 変更内容について理解し,承認し,正しく処理するため
- 変更管理meetingは,product_management_teamが準備して仕切る
- product中心でない組織では,project_managerやprogram_managerが準備して仕切る
- column: document作成のバランス
- recycle_box
- 有用かもしれないideaは,将来余裕ができたときのために蓄えておくrecycle_boxに入れる
- 求められたfeatureをすべてreleaseしなければならない,というdevelopment_teamが感じるpressureを軽減する圧力弁になる
- 段階的な凍結
- product_managementの重要な概念
- marketingの4つのP
- product
- price(およびbusiness_model)
- business_model: productやserviceでclientから対価を得る方法
- business_modelに紐づく価格決定モデルによってpriceが決定
- product_managementの中で最もやりがいのある課題
- 効果的な価格設定は,心理学的な側面が強い
- place(販売channel)
- bitの集合であるsoftwareをどのように提供するか
- productやserviceを提供するk十を誰が承認するか
- system_architectureに影響を与える
- promotion(広告とmarketing communication)
- 有効市場(total_available_market),有効市場規模(total_addressable_market),market_segment
- total_available_market: 利用する可能性のあるすべてのclient
- total_addressable_market: 有効市場において対象にできる市場規模
- 効率的なmarketing programは,total_available_marketを明確なmarket_segmentに分割する
- market_segment: 共通する特別な性質を有するclientのgroup
- client簡易交流がある
- 効率を追及するなら,market_segmentをニッチ市場に分割
- 受け入れのS字カーブ
- カテゴリを理解することで,product_managementや関連するmarketing活動によってカーブの形を調整できる
- カーブの形はinnovationの性質に応じて大きく異なる
- innovator
- 限界を超えることを好む
- 動かすために必要なresourceを自由に使えることが多い
- early_adopter
- 限界を超えることを好むが,やや保守的
- より完璧なproductを期待する
- より多くの要素を望む
- 全体的な成功を収められるかどうかの試金石
- meritを納得させられれば,成功するsolutionの基礎が完成といえる
- innovatorとearly_adopterの間のギャップを埋める: chasmを越える
- early_majority
- 価格性能比が争点
- clientの成功事例やROI分析などの実証を求める
- late_majority
- 無視できない経済的圧力がかかるときに受け入れる
- early_majorityに受け入れてもらったときに比べると,経済的利益ははるかに少ない
- laggards(lag: 最後の人)
- innovationを受け入れる理由: innovationがclientの抱える問題に対して何らかの効果をもたらすと感じるため
- whole_product
- 汎用製品,期待製品,拡張製品,潜在的製品の4つの概念が合わさったもの
- target_productが上の4つを遷移していく
- 技術的優位性と市場優位性
- 市場優位性の方が強力
- position, positioning
- position: clientがproductを現在どのようにcategorizeし,認識しているかを,冷静に客観的に正確に算定したもの
- positioning: clientの気を惹いて記憶に残るような独自の印象を生み出すための,戦略的に管理される活動
- 未来のことが対象
- 潜在顧客がproductを自然と思い浮かべるようにする
- 長期的,戦略的,防衛的で所有可能なもの
- 1つのconceptにだけ集中できるようにするもの
- 技術的marketでは,positioningはpositionより重要
- positioningを設定したら,何度もその目標に向かってpositionを作り直していく
- column: effective positioningが購買動機に与える影響
- positioningは,development_teamを含む全員にとって,魅力のある未来を創らなければならない
- 記述形式
- <魅力的な購買動機>を持つ<target client>のために,このproductは<主なメリット>を持つ<product category>だ.
- <主な競合>と違って,<主な差別化要因>である.
- brand
- clientと交わす約束
- → 重視される
- partnership, customer support, 企業のWebサイトの性質や構造,clientに提供される商品やserviceの品質,利益を管理する方法などが,どれもbrandに反映される
- 用語,symbol,slogan,名前など様々な要素を通じて表現され伝えられる
- 知的財産として保護されていることが多い
- clientと交わす約束
- main_message
- positioningをcreativeに表現する短い(1,2 phrase)の文章
- positioningを強化し,かつclientは確実に手に入るものを表す
- press releaseから広告まで,あらゆるcreativeなmarketing communication活動を駆動するために重要
- client中心
- 重要な利益を表現する
- なぜ注意を払う必要があるか,またはproductが問題をどうやって解決してくれるかをclientに伝える
- 正確で,productが実際にsupportしているもの
- marketingの4つのP
- まとめ
- product_management: 成功するsolutionの構築と維持に必要な活動の包括的なまとまり
- 初版のsoftware productの構築は,productを正当化するconcept提案に始まり,本番稼働に終わる
- より幅広く包括的
- そのあとに続くreleaseを構築するprocessはより軽量で,最初のreleaseの間に作られた素材を元に構築される
- product_development_processがうまく運営されているかどうかは,各stageに厳格な「可/不可」の判断があるかどうかでわかる
- そのあとに続く凍結が変更管理protocol,recycle_boxの考え方などによって強化され,改善される
- business_planは,product_developmentを進めることを正当化する中心的なdocument
- marketingの4つのP
- 最大有効市場
- 最大標的市場
- market_segment
- 受け入れのS字カーブ
- whole_product
- position, positioning
- brand
- checklist
- 要件を表現するための何らかの仕組みを定義している
- product_management_teamはclientの声を代表している
- 企業にはproject, productを打ち切る方法がある
- 競合他社が何を行っているか理解している
- 勝つためには何を行うべきか理解している
- productを誰のために構築しているのか,誰に売り込んでいるのかを理解している
- 便利な変更管理protocolがある
Ch03 marketecture, tarchitecture
- systemのmarketing的側面と技術的側面が一緒になってbusinessの目的を達成するさまを見る
- 誰が何に責任を負うのか
- tarchitect
- software_architect, chief_technologist
- marketect
- product_marketing_manager, business_manager, program_managerなどシステムに責任を持つ人
- tarchitecture: developerがsystem architectureを検討するときに参考にする主要な枠組み
- software_systemを成立させるためのsubsystem, interface, 処理要素への責務の分割, thread modelなど
- marketecture: system architectureに関するbusiness的な観点
- business_modelに関するあらゆる物事を含む
- license_model, 販売 model, value proposition, clientに関係する技術的な詳細, data sheet, 競合他社との差別化要因, brand要素, marketing_teamがclientのために用意しようとしているmental_model, systemに固有のbusinessの目的など
- MRDやusecaseも含む
- whole_productで説明されていたもの
- business_modelに関するあらゆる物事を含む
- column: 5万ドルのboolean_flg
- marketecture, tarchitectureの違いを活用し,product_management_teamもengineering_teamも,さまざまな技術的な課題やbusiness的な課題を解決するのに最善と思われるapproachを柔軟に選択できる
- tarchitect
- solution_developmentの初期に現れるフォース
- 図3-1: software_architectureを形作るフォース
- 非機能要件
- architectureに関する様々な品質や特性
- 実行時にシステムを観察して見て取れるかどうかで二分
- performance, usability
- target_clientの意向が直接反映
- testability, 修正可能性
- target_clientとの将来の関係を左右
- 明示されないので規律がきわめて重要
- performance, usability
- 技術的な妥協がもたらす潜在的な危険度を測るためには経験以外にない
- tarchitectは少なくとも2回は完全releaseを経験する必要
- productの整合性に対して長期的に関わり続けなければ,妥協を取り除いたことを確認できない
- technology base
- super tarchitecture
- applicationの基本構成を規定
- 問題領域から導かれるtarchitectureに対応したものである必要
- super tarchitecture
- 問題領域
- 成功するsolutionを構築するための中心的フォース
- marketect: market needsを明確化し優先順位をつける
- tarchitect: そのneedsを満たす技術的なsolutionを構築
- 領域における経験も設計に組み込む必要
- 広範囲な領域の知識がほぼ必須
- 特定の領域でsystemを構築した十分な経験と,成し遂げる十分なskillが必要
- column: 時には難しいやり方でやるしかない
- clientと長期間ともに過ごす
- column: バグの深刻度,優先順位,標準化
- bug review meeting
- 品質の基準をあらかじめ決めておく
- 長期的に稼働しながら短期間で結果を出す
- clientが18か月から24か月先に求めるものをイメージするため,さまざまなデータを検証する
- それ以前は現在なども過去のこととして捉える
- 未来を想像できることはmarketectにとって重要な指標
- tarchitectも同様
- clientが18か月から24か月先に求めるものをイメージするため,さまざまなデータを検証する
- 将来像を示す
- market_map
- feature/merit map
- market event/market rhythm
- tarchitecture_map
- 既存のtarchitectureではサポートできないだろう重要なfeatureを,核心的featureとして記録
- 導入のために必要な変更を管理する必要
- 既存のtarchitectureではサポートできないだろう重要なfeatureを,核心的featureとして記録
- market_map
- feedbackを利用する
- marketect
- user conference, technical/product support review, featureへの要望のreview, 販売担当者にinterview, main clientなどとmeeting, analystなどとmeeting
- tarchitect
- conference, magazine, mailing list, 自宅のPC, 好奇心
- mapをメンテナンスし続けることでcommunication
- column: 言ってはいけないことを言ってしまったら?
- 本当に心配ならguidelineを示す
- リスト
- 本当に心配ならguidelineを示す
- marketect
- はっきりさせる
- marketectの一番の目的は,development_teamのメンバが何を構築するべきかを正確に理解することで,その責任を負う
- teamの規模と必要不可欠な外部とのやり取りの数によって,適切な構造・process・成果物を決定
- marketectは,潜在顧客に対しても,systemが彼らの環境に与える影響をはっきりさせる必要がある
- column: software developmentにおける文化的差異を管理する
- 文化的差異に注意して,特定の文化でうまくいくapproachやprocessを選ぶ
- marketectはtarchitectからの適切な情報の流れに強く依存する
- 一致団結して作業する
- tarchitectの創造的なenergyを,現実の顧客の現実の問題を解決することに向けられるようにすればよい
- 合意形成
- projectを推進するための原則を決め,それらについて合意する
- dataが手に届くようにする
- map, featureの可視化
- context_diagramとtarget_product
- context_diagram
- marketect, tarchitectが協調するうえできわめて有用なtool
- 重要な要素間の関係を表現することに注力する
- context_diagram
- まとめ
- marketectはmarketectureに責任を持つ
- tarchitectはtarchitectureに責任を持つ
- marketecture/tarchitectureはそれぞれ異なるが関連し合っている
- solution_developmentの初期ステージにおいて,特に影響力のある3つのフォース
- architectになるための経験
- bugの分類
- productとその発展を示す戦略的なビューを作る
- context_diagram
- checklist
- marketectがいる
- tarchitectがいる
- bugを深刻度と優先順位に従って分類できるDBがある
- market_map, feature/merit map, market event/market rhythm map, tarchitecture_mapを作っている.誰もが簡単にみられるところにある
- clientと対面する機会のあるdeveloperは,言っていいことと悪いことについて適切に訓練されている
- marketectがsystemのcontext_diagramを作成した
Ch04 businessとlicense_modelの共益関係
- business_model: product/serviceに関してclientに料金を請求する方法
- あらゆるbusiness_modelにlicense_modelが結びついている
- license_model: 利用規約,business_modelにより規定される
- 同じphraseで両者を説明する短い文章を簡単に作れると,2つのmodelの共存を示せる
- 2つのmodelは同じものではない
- それぞれのtarget_marketに対して,market_shareと収益を最大化するための組み合わせを検討するべき
- business_model, license_modelについての原理と,所定のtarget_marketでそれがどのように機能するか理解が必要
- controlを手放さないためにlicenseが必要
- business/license modelはmarketectureの重要な要素を体現したもので,tarchitectureにも大きな影響がある
- column: business_modelの境地
- 一般的なsoftware_business_model
- column: application?suite?bundle?feature?
- 時間ベースのアクセスや利用
- 恒久license
- supportや保守に関する全体的なコストが増えるため注意
- 潜在的な欠点があるにもかかわらず一般的
- 年間license
- enterprise systemで一般的
- target_marketに最適な期間を定義して,business_modelとlicense条項を適切に施行できるようにすることが難しい
- rental_license
- licenseが与えられた時点で使える時間が設定されているもの
- 年間licenseもこれの一部
- 重要な課題
- いつから期間が始まるか
- 期間が終わったらsoftwareはどう反応するべきか
- 価格
- subscription
- upgrade, supportの権利がある
- 何らかのbackend_serviceと結び付けられていることが多く,比較的簡単に強制できる
- 後払い
- tarchitectureに対して複雑なことを要求するが,softwareが利用された分のお金を必ず受け取れる
- modelの成熟に伴い一般的になっていくもの
- 利用者単位のlicense
- volume_license
- enterprise向けのapplicationには適切
- volume_license
- OEM(Original Equipment Manufacturer)
- business_modelはsoftwareへのアクセスに基づく
- royaltyとして正当な金額を設定
- 恒久license
- transaction
- あらかじめ定められた計測可能な処理の単位
- 主にenterprise_softwareにおけるbusiness_modelで使う
- marketectがtransactionを定義して,target_marketに対して最適なtransaction料金を組み立てる
- tarchitectは,transactionの定義に関する法的な要素と,business_model的な要素の両方の理解が必要
- column: good, but how to claim?
- product/serviceの価格設定は,marketectの最も難しい課題の1つ
- 原則
- 価格には価値を反映
- 価格にはかかった労力を反映
- 価格にはpositioningを反映
- 価格には市場勢力図を反映
- 価格モデルはclientを混乱させてはいけない
- 価格モデルはmarketの成熟度を反映
- 同じproductの価格は下げるよりも挙げる方がm図鵜香椎
- 価格モデルはtarget_marketの特徴を反映する必要
- metering_model
- 明確に定められたresourceやapplicationの処理内容に関する制約,あるいはそれらを消費することに基づくbusiness_model
- 制約model
- 既定の量のresourceにしかアクセスできないようにする
- 消費model
- 消費可能なresourceのpoolを作り出す
- 消費しきると使えなくなる
- 並行resource_management
- e.g. user, session
- 同時並行resource_management_model
- @enterprise_software
- 個別resource_management
- systemが認証した場合にのみアクセス許可
- 制約: resource
- 同時並行resource_management_modelとの組み合わせ多い
- 管理上の負担
- 既存のdirectory infrastructure, user management infrastructureにより軽減
- 消費resource_management
- 課題
- 時間の定義
- softwareがresourceの消費を追跡する方法
- licenseする時間の単位
- subscriptionにも〇
- 重大な要件
- reporting
- 補給
- 課題
- hardware
- softwareにかかる料金をhardwareに結びつける
- service
- open_source_licenseが使われることが多い
- marketは未成熟で未検証
- open_source_licenseが使われることが多い
- 得られた収入/節約できたコスト
- 歩合制
- 本当はいくら支払うべきか追跡するのが困難で,not popular
- column: open_sourceだからといって無料なわけではない
- business_modelとlicense_modelの分離の恩恵の例
- business_modelに関連する権利
- 権利と制約ができるだけ多くなるように区別することで,より大きな価値を生み出せる
- 図4-1: license_modelのsubsetの検討
- 1.競合に対する優位性が得られたり,technical supportやproduct supportのコストを削減できたり,またはclientとの関係を強化できたりするのであれば,businessにこの権利を関連付ける
- 逆にclientに関心がなかったり,厄介ごとを負わせてしまうものはやめる
- 2.transactionに基づくbusiness_modelに含まれるべき権利
- upgrade, bug fix, patch
- 3.clientに受け入れられた場合は権利を関連付ける
- hardwareではclientの権利を制限する理由がある
- 1.競合に対する優位性が得られたり,technical supportやproduct supportのコストを削減できたり,またはclientとの関係を強化できたりするのであれば,businessにこの権利を関連付ける
- business_modelに対するtarchitectureのsupport
- 一般的な課題
- 必要なデータの収集
- direct
- systemは,business_modelに必要なデータをすべて収集し,管理している
- 要自己完結
- indirect
- 必要なデータから全体像を組み立てるには,1つ以上のほかのシステムと統合が必要
- direct
- 請求.送金に関する要件
- 送信内容に関する書式,セキュリティ,監査の要件を定義することで簡単になる
- business_modelの強制
- license違反をした場合について
- 品質特性に注力する
- 品質特性: trustness, stability, scalability, ease of support, usabilityなど
- 実現するための努力が,business_modelにとっての主要な目標と一致していれば理想的
- softwareの使われ方はtradeoff
- 収益の増加,あるいは,削減したコスト
- 複製の防止・著作権侵害行為への対策
- business_model, license_modelに関するparameterの妥当性検証
- いつどこでどのように設定されるか,誰が変更できるか,妥当性をどのように検証したのか理解
- 必要なデータの収集
- 時間ベースのアクセスや利用
- tarchitectureに課せられる特別な要件はそれほど多くない
- ただし,厳密に強制する場合は,期間を過ぎたらsoftwareを無効化する仕組みが必要
- ほとんどはupdateを受け取れなくなるだけ
- transaction
- transactionを定義
- business_modelの基礎.明確さが必要
- 定義後は,tarchitectureがそのbusiness_modelに対応できること,そしてlicenseに明記してあることを確認する
- transactionに対して各componentがどのような役割を果たしているか考慮
- transactionとbusiness_modelの関係を定義
- 完了した個々のtransactionを確実にbusiness_modelへ対応付ける
- 監査証跡を残す
- transactionが一意に識別できるようにする
- DBに任せず,信頼できるalgorithmで本当の一意の識別子を生成する
- UUID, GUIDなど
- 短縮も〇
- DBに任せず,信頼できるalgorithmで本当の一意の識別子を生成する
- transaction state, lifecycle, 継続期間を理解する
- 請求のタイミング
- transactionを定義
- metering
- どうやってユーザを認証するか
- 多額のお金を引き出すものであれば,LDAPなどユーザ認証のための確立されたinfrastructure technologyを組み合わせるべき
- ユーザは何人いるか
- 設定値の管理
- 同時並行ユーザをどう数えるか
- ユーザはいなくなったことにするのか,または非活性にするのか
- session_management
- 消費resource_managementを実施する場合は,tarchitectureに厳しい制約
- ある種の状態の記録
- hardware
- 定義と強制方法が必要
- どうやってユーザを認証するか
- 一般的な課題
- license_modelを強制する
- 自己申告制
- 権利を放棄しているわけではない
- 要因の注意深い分析が必要
- enterprise_softwareでは,licenseを更新するたびにclient担当者が直接clientと話す機会があるので,購入を促すこともでき〇
- consumer_softwareでは×
- 自家製license_manager
- licenseの費用に応じて専用のsolutionと比較する
- sessionに基づくlicenseの場合は,自家製のlicense_managerのような仕組みが必要
- column: 絶対に突破できないものでなくてよい
- 正直者に嘘をつかせない仕組み
- third_partyのlicense_manager, 専用のlicense_manager
- 自己申告制
- marketの成熟度がbusiness_modelに与える影響
- target_marketの成熟度は,business_modelの選択や管理に一番影響する要因の一つ
- business_modelの強制度合いも,target_marketの成熟度と足並みを揃える
- business_modelを選択する
- marketectが直面する最も困難な課題の1つ
- target_marketは何か?その価値は?
- どのように使ってもらいたいか
- target_marketにおける目的は?
- shareの確保,維持など
- business_modelは何か?
- どのような権利を与えたいか?
- business_modelはどのような影響をarchitectureに与えるか?
- 価格モデルはどうか?
- まとめ
- business_modelとはお金を生む方法
- business_modelとlicense_modelは結びついている
- license_modelとは,softwareの使用にかかわる条項,条件
- software_business_modelがお金を生み出す方法一覧
- userに関係するbusiness_modelによって,ユーザを管理する企業システムとの統合が促進
- business_modelに関連する全ての権利を理解
- 権利を分割することで,利益を生む可能性が高まることもある
- license_modelを強制するには,自家製のlicense_managerか,third_partyの専用license_managerを使う
- checklist
- development_teamのメンバー一人一人が,現在のbusiness_modelや将来に向けて真剣に検討されているbusiness_modelを説明できる
- license_contractがbusiness_modelと一致している
- license_contractによって,clientに提供される権利が一式定められている
- business_modelを強制するための,適切な仕組みを選択している
- 別のbusiness_modelをsupportするためにtarchitectureを変更するcostにすいて,marketectと話し合い,理解してもらえている
Ch05 in_license technology
- license_contractのriskと見返り
- (merit)
- (risk)
- 複雑さやリスクの削減
- 責任がproviderに移り,依存性が高まる
- component化
- technologyとsolutionとの結びつきが強いと変更が難しくなる
- system構築がはかどる
- 設定が複雑になりうる
- 互換性の問題
- license上の制約が増える
- 特許によって保護されたlicenseにより,法的に保護
- 補償からの免責を勝ち取ることが難しい
- technologyのreuseによるmarket投入までの時間短縮
- 必ずしも短くはならない
- 品質
- 必ずしも高くはない
- 軽量,最適化
- そうでない場合も多い
- 調整困難
- technologyの流行を追う苦労の軽減
- 必ずしも素早く更新してくれるわけではない
- 費用
- 不要な費用になるかもしれない
- service/support cost
- 外部から導入したことによる新しいエラーへの対処が難しい
- 適切にsupportできるか確認する必要
- column: 他人の書いたコードを修正してもらう
- vendorとの協力が重要
- (merit)
- contract: どこで何を約束したのか
- technologyのlicense取得の核心: 使用に伴う規約を定義した契約を交わすこと
- contractの基本
- 申し込み
- 受諾
- 約因
- 両者の間で取り交わされるあらゆる価値
- license条項
- 定義
- licenseの契約書に記されたあらゆる重要な項目や条項に関する正確で法的な説明
- 使用あるいは許諾
- 外部から導入したtechnologyの使い方に関するもの
- 期間や条項,その他の重要な日付
- 契約の開始と終了に関すること
- 有効期間
- 期日
- 支払日
- 試用期間
- 解除通知
- その他
- column: 高くついた更新費用
- 更新期限の意識が重要
- 領域
- 特に地理的な制約
- 特定の用途
- 独占権
- ほとんどの場合では不要
- sub_license
- 組込technologyで必要
- 終了,解除
- 予告期間はできるだけ長い期間にするよう交渉する
- 置き換えに時間がかかるため
- 予告期間はできるだけ長い期間にするよう交渉する
- 更新
- 自動更新は非推奨
- 料金または支払条項
- tarchitectureが必要な支払条項をsupportしていることが重要
- deploymentの制限事項
- その他の一般的な制限事項
- できないことも慎重に定義されている
- いずれもtarchitectureに影響を与える
- 競合禁止条項
- source_codeへのアクセス
- 第三者機関がsource_codeの複製を委託されていても,結局管理できなくなっていれば意味はないことが多い
- marketingの要件
- 注意深く読む必要
- tarchitectureにとって好ましくない影響を与える可能性もある
- 定義
- business_modelの衝突→交渉の始まり
- 自分たちのbusiness_modelがvendorのbusiness_modelと整合していることをlicense取得前に確認
- 不整合 → 妥協点を交渉
- tarchitectureに重大な影響
- license_contractの遵守
- tarchitectureに影響を与えるもの
- technical用語の定義
- license_contractを遵守するうえで最も重要
- 実際のproductを反映
- version number
- OSや運用環境の定義
- business_planに従って,productを利用する権利がある
- API
- 公開できるかどうか
- support
- branding
- in_license technologyの管理
- wrapperのコスト
- open_source_license
- licenseの詳細の確認
- productへの統合の仕方を定めている部分の確認: 最も重要
- open_sourceの定義のバージョンを確認
- license_cost
- vendorのbusiness_modelが反映されていることを考慮する
- 先払い
- tarchitectureへの影響は最小限
- technologyをsystemに統合するうえで最大限の柔軟性があるため
- tarchitectureへの影響は最小限
- 使用量
- tarchitectureに影響がある
- 実現しやすい
- 収益に対する割合
- tarchitectureには景況が小さいが,marketectureには影響が顕著
- 交渉の戦略
- productを陳腐化させない
- 価格付けを守る
- milestoneごとの支払い
- column: 価値に基づく費用交渉
- moduleごとに価格設定を別にする
- system全体の費用を体系化
- 研修コストと開発コスト
- licenseの収益構造
- 収益構造を理解してリスクを事前に検知
- in_license契約のコストをproduct planやbusiness_planに組み込むことの重要性
- まとめ
- どんなシステムでも,in_license契約のtechnologyを含む場合がある
- 効果的なin_license契約のためには,marketectとtarchitectの両者が,リスクと対価について完全に理解する必要がある
- 強い立場で交渉に臨める,より有利な契約を結べる
- in_license契約はどれも,marketectureとtarchitecture両方に,direct/indirectな影響を与える
- あらゆるbusiness_modelと整合させて,solution全体を組み立てる
- in_license契約したtechnologyの管理には,さまざまなtechniqueが必要
- 本質的: tarchitectureとtechnologyを分離する
- 費用の取り決めと,license_contractに関連するコストの理解が必要
- open_source_licenseのtechnologyの進化
- checklist
- licenseを取得したcomponentやtechnology全てについて,妥当な契約を交わしているか
- in_license契約に関する主要な利用規約を理解しているか
- 契約違反の原因になる行為を全て確認し,気づかないうちに破ってしまうことがないことを確認
- licenseを取得したcomponent, technologyを置き換えることの影響を評価したか
- licenseを取得したcomponent, technologyと,productのbusiness_modelは互換性があるか
Ch06 portability
- portabilityのmerit
- 〇複数のplatformに対応すれば,新しいmarketに参入できる
- segment分割を上手くやることが長期の成功のために重要だが,これはplatformでのsegment分割ではなく,clientの問題を基準にするべき
- 普通はbusiness課題によってplatformが選択される
- そのほかはportabilityを追及するための根拠となるmotiveが適切なことはあまりない
- 本当にportable applicationなら,platformに依存したfeatureが生み出されないように設計される
- 〇複数のplatformに対応すれば,新しいmarketに参入できる
- portabilityに関連するbusinessの事例
- cost大きい
- target_marketが十分に大きいときのみ,コストを正当化できる
- cross_platformの条件
- market分析の結果,複数のplatformに対応したproductを開発・サポートし続けていくコストを正当化するだけの十分な収入が見込める
- supportするすべてのplatformについての開発・テスト・サポートにかかるコストの合計が考慮済み
- 対応する複数のplatformについて,構築・テストするための人員を確保できている
- supportしなければならないさまざまなplatformの開発活動に与える相対的な影響を理解し管理できる
- applicationを移植可能にするより,technologyを移植可能にする方が説得力がある
- technology: より大規模なsolutionを構成するための部品として設計されているsolution
- 結局はtarget_marketがはっきりしているかどうかが重要
- → development_teamがapplicationをどのように作るべきか決める
- column: portabilityはお金のためでしかない
- 単独のplatformでしか動かなくても素晴らしいsolutionであることが重要なcase
- core technologyとしてportabilityが重要なcase
- portabilityのあるapplicationの構築
- interpreter_languageを使う
- 実行環境との結合を疎にする豊かな絶縁層があるため
- compiler_languageの場合は,条件付きinclude/compileが必要
- 標準的なpersistence_storageを使う
- business_logicを移植可能にする
- 利用者へ寄り添うことで失われる可能性
- portabilityに最大限の投資をするなら,backendやinfrastructure
- subsystem間の相互運用性と標準化のためにXMLを使う
- portabilityの名のもとにplatform固有の能力を埋もれさせるのは×
- interpreter_languageを使う
- 苦痛のmatrix
- 苦痛の最小化のために,developerや品質保証チームにmarketにおけるportabilityの相対的な優先順位を伝える
- 優先順位づけのtechnique
- developerや品質保証チームが利用する,marketありきの構成のmatrix(苦痛のmatrix)を作ること
- all pair法の変形でもある
- step1: 構成を減らす
- step2: 組み合わせをランク付けして並びかえる
- 理由となるもののリスト
- red/yellow/blueに背景を分ける
- step3: final_cutを決める
- clientの環境の分布について情報収集し,パレート図にする
- 初期段階における組み合わせと優先順位を,marketの特徴から自動的に算出する方法が必要 → all pair法
- marketからの要求を満たしているかreviewは必要
- developerや品質保証チームが利用する,marketありきの構成のmatrix(苦痛のmatrix)を作ること
- 約束には用心する
- 契約上の管理とサポートする対象にはくれぐれも用心する
- 「以降」などは×
- 販売した後のことも配慮する
- 対応するversionとplatformは具体的に明確にするべき
- 契約上の管理とサポートする対象にはくれぐれも用心する
- まとめ
- portabilityを求めるためには適切な理由が必要
- portabilityのあるapplicationが欲しい理由も,それを構築したい理由も,実際は大したことではないことが多い
- portabilityのあるapplicationを作るためにbusiness上の正当な理由があるとしたら,それは利益が出るということのみ
- costを正しく見積もる
- portabilityのあるapplicationを作るのは考えているよりもずっと困難で,長い時間がかかる
- 苦痛のmatrixはtestしなければならない組み合わせを網羅
- 無意味な組み合わせ,対応しない組み合わせを取り除く
- 優先順位をつける
- reviewを実施してfinal_cutを決める
- supportする構成を常に明確にする
- 新しい構成をサポートする場合は慎重にすすめる
- checklist
- 対応しているそれぞれのplatformについて,開発,品質管理,技術サポート,営業のresourceが足りているか
- 対応しているそれぞれのplatformをテストするのに十分な時間をかけているか
- productが動作するplatformのvendorによって提供された重要なreleaseが,technology loadmapに追加されているか
- テストに費やす労力を最適化するため,market_drivenな構成matrixを作っているか
Ch07 deployment_architecture
- deployment_architecture: clientが自分の手でsystemをdeploymentするやり方のときに使う
- deploymentの選択肢
- client site
- clientのmachineにinstall
- client自身が設定,運用,保守
- application_service_provider(ASP)
- 限定されたサービスを提供.完全なsolutionを提供することはほぼない
- managed_service_provider(MSP)
- transaction(Web service)
- 単一の完全なtransactionで応答を算出
- Web service protocolを介して実行されることが多い
- 個々のユーザに向けてサービスを提供
- column: hybrid deployment_architecture
- client site
- deployment_architectureにおけるclientの影響力
- clientの期待に見合うdeployment_architectureの選択は重要
- management, integration
- systemをonsiteで運用しているのと変わらない水準で管理できることが重要な場合
- 長期間にわたるデータの保全と管理
- marketectが,clientが最も重要と考える管理面での課題を理解する必要
- integrationへの要求が増えると,systemがclient siteにdeploymentされる可能性も高くなる
- security, privacy, peek_load(ピーク負荷)
- systemの扱うデータの性質に応じてdeployment_architectureに対するclientの印象は異なる
- cost, vendorへの信頼
- solutionの販促素材は,単にsolutionの利点を示すだけでなく,marketにおける長期的な生存可能性を示す必要がある
- 生存可能性テスト
- clientのデータをどのように保存し維持していくかが関心の的
- いつでも古いデータが取り出せるとclientに安心してもらうことは,solution全体を設計するうえで重要
- solutionの販促素材は,単にsolutionの利点を示すだけでなく,marketにおける長期的な生存可能性を示す必要がある
- clientのskillと経験,地理的な分布
- applicationとそのdeployment_architecture次第で,client企業に求められるskillや経験が変わる
- clientの要求だけでなく,client企業の目的もよく理解できるようにつとめることがとても重要
- column: credit card informationを粗末に扱わない
- 運用管理の手順について慎重なreviewと監査を要求する権利がxSPに対してある
- column: riskが大きくなり過ぎたときは
- 諦めて成功するsolutionをしっかり構築するべき
- 企業がdeployment_architectureに与える影響
- 持続可能な成功するsolutionのためには,marketectが利用する企業の様々な事情を理解する必要がある
- sales_cycle
- 売り上げを算出するまでにかかる期間や手順
- 価格やsoftwareの複雑さと相関
- もしsales_cycleの短縮に取り組むなら,xSPとしてdeploymentすることや,正式な審査の元にlicenseすることを検討する
- infrastructureへの投資
- 長期的なサービスを提供し続けるためにどれだけの投資が必要になるか注意深く考える必要
- 投資額の計算には技術的resourceと非技術的resourceをともに含める
- 信頼できるinfrastructureを構築するための資金がない場合は,xSPやWebサービスの運営は×
- cash_flow
- 余剰資金を管理するための確実なアプローチが必要
- 柔軟性
- xSPやWebサービスでは管理の主導権がある
- upgrade scheduleなどをかなり柔軟に予定できる
- 新規市場では政調のためにrelease_cycleを素早く回すことは欠かせないし,必要なときに必要なだけupgradeすることもできる
- xSPやWebサービスでは管理の主導権がある
- 地理的な広がり
- 価格よりサービス
- softwareのdeployment_architectureを選択する
- 図7-1: clientと企業が及ぼす影響とdeployment_architectureとの関係性
- deployment_architectureと作業の分担
- 図7-2: deployment_architectureと作業の広がり
- information_appliance(ance: していること)
- deploymentの選択がsoftware_architectureに与える影響
- 柔軟でparameter化されている,または統合の選択肢がない
- xSPこそ標準化に取り組むべき
- 構築も管理も単純になり,運用コストdown
- 機能と比較的単純な境界がうまく定義されている場合は,統合の選択肢を一切与えないことはうまくいく
- xSPこそ標準化に取り組むべき
- upgrade policy
- systemを開発する初期の時点でupgradeを容易にする変更を行うことで,MSPなどは頻繁に安全に本番システムを変更している
- data保護とアクセス
- application dataの保守は,applicationや利用者,データのsensitibity,重要さに応じて,適切に行う必要
- 移行手段の選択肢
- 移行の与える影響は,architectureの全体的な設計においても考慮されるべき
- 柔軟でparameter化されている,または統合の選択肢がない
- 一般消費者向けsoftwareの未来
- softwareのlicenseはrental, subscriptionが望まれる
- 持続性があり,信頼性も高く,高速なインターネット接続を経由してWebサービスへ接続するようになる
- 一般消費者向けsoftwareの環境も,enterpriseクラスのsoftwareと同じように複雑になっていく
- softwareのlicenseはrental, subscriptionが望まれる
まとめ
- deployment_architectureとは,顧客の使用するシステムがどうやってデプロイされるのかを説明するもの
- 一般的な選択肢
- client siteへのdeployment
- application_service_provider
- managed_service_provider
- 様々な種類のservice provider(xSP)
- Web service(transaction応答型サービス)
- 今後は,hybrid形態が一般的になっていく
- 一般的な選択肢
- deployment_architectureの選択におけるclientへの影響
- 管理統制に関する要望
- ほかシステムとの統合
- dataのsecurity/privacy
- peek_loadを捌く処理能力
- 初期コストと保守コスト
- 信頼関係
- systemを運用するスタッフのスキルと経験
- deployment_architectureの選択における自分たちの所属する会社からの影響
- 理想的なsales_cycleと実際のsales_cycle
- infrastructureへの投資
- cash_flowに代表される財務モデル
- 効果的で素早いclient基盤の管理
- client側拠点に合わせた地理的な広がり
- どのdeployment_architectureを選択しても,うまくシステムを管理するために必要な作業の全体量は変わらない.変わるのは作業の分担
- information_applianceは,幅広い環境におけるdeployment_architectureにおいて,成長し続けている分野
- open_sourceの契約モデルは所有者にとっての全体コストを下げてくれており,成長を支えている
- deployment_architectureとは,顧客の使用するシステムがどうやってデプロイされるのかを説明するもの
checklist
- 自分たちのdeployment_architectureはtarget_marketにおける次のいずれかの要望に合致している
- 管理
- 統合
- データのsecurity/privacy
- 有用な性能モデルがあり,自分たちのdeployment_architectureが期待されている仕事量を処理できる
- 適切な運用policyを定めている
- 自分たちのdeployment_architectureでは,次の事項についてどのような選択をしたのか説明できる
- sales_modelとsales_cycle
- infrastructureへの投資
- clientにやってもらう作業の量を定義している
- 自分たちのdeployment_architectureはtarget_marketにおける次のいずれかの要望に合致している
Ch08 integration, extention
- integration: systemがどれくらいほかのsystemと連携できるか,またはどれくらいほかのsystemと連携しなければならないかを表す度合
- extention: systemを元にしてextended_productを作り出すとしたら,どれくらいextendできるかを表す度合
- clientをやる気にさせる--原動力
- integration/extentionとも,複雑なシステムならどちらでも,より優れたproductを作る能力が得られるという同期
- 自分たちが管理しているという感覚から,作業が増えても満足度が上がるケースがある
- integration/extentionの動機
- 予測はできないが計画はできる
- 統合や拡張のための方法をclientに提供することで,未来を予測できなくても未来に備えることができる
- clientはできませんと言われるのを嫌う
- 統合したり拡張したりする戦略が必要
- 大規模なsolutionは複数のより小さいsolutionから構成される
- 予測はできないが計画はできる
- 自分のsystemにはない情報がほしい
- 興味を引くreportや分析結果のために,別のシステムの情報と組み合わせる必要があることがある
- 切り替えコストを増やしたい
- productのecosystemを作りたい
- column: 何かと組み合わせなければならない
- 他システムとの統合が前提のケース
- 一般常識
- layered_business_architecture: logic_structure
- business_applicationのsystem_architectureとして最も一般的な物の1つに,subsystemを論理的にも物理的にもlayer化するというものがある
- ui layer
- userに情報を提示するとともに,情報に対するuserのinteractionを管理する
- applicationの国際化に関する作業のほとんどを引き受ける
- simpleなcommand_line interfaceがおすすめ
- 自動化テストが容易
- service layer
- application固有のさまざまなserviceを提供
- serviceとusecaseは似ている
- 1つのusecase全体が1つのserviceとして表現されることもあれば,1つのusecaseのなかの1つの手順がserviceに相当することもある
- domain_model layer
- businessの重要概念やapplication domainの規則を表現
- enterprise applicationにおいてこのlayerは付加的なもの
- このlayerが必要になるのは,単純なサービスでは表現しきれないほどbusiness ruleが複雑だったり,構造化されたobjectをメモリ上で表現した方がより効率的な場合くらい
- domain_modelが必要とされるときは,applicationの中核として生まれ出ることが多い
- 正確性が重要
- domain_modelとpersistence_data_modelは実際はほとんどのapplicationのtarchitectureにおいて同一
- domain layerをuiやtransaction管理layerから分離しておくと,system開発はかなり柔軟になる
- 基盤となるapplication logicに手を入れることなく,service agent向けに表示する画面を,interactiveな音声対話システムやWebページに置き換えることもできる
- 凝集度も高まる
- architectureを構成するそれぞれのlayerは関連する操作の集まりに責任を負う
- 先だって設計しておけば,uiを設計する際に役立つ
- domain_modelの提供する公開interfaceに従ってuiを実装
- persistence_data layer
- ほとんどのbusiness_applicationは,persistence_storageに格納したobjectの管理をDB管理システムに任せている
- domain layerのobjectとrelational_DB内のobjectの間のmappingを管理するlayerを別に設けるやり方が一般的
- 基盤となるDB schemaとの連携を効果的に,かつ簡単にするために,domain_modelを構築した方がよいことも珍しくない
- DB schemaが以上に複雑だったり,性能要件があまりに厳しいときは,domain_modelは取り払ってservice layerとDB schemaを単純に結びつける方が良い
- business_logicをDBの中に移す
- portabilityを損なう
- persistence_data layer teamがSQLを効率的に使えていないことにもなる
- それでも,慎重を期した上で移す方が適切な場合もある
- 性能
- 巨大なDBのときは特に
- 高度な制約付きdataを扱う場合
- 条件付きでデータを削除する場合など
- dataの操作がどうあれ,何らかのアクションを行われることを保証したい場合
- DB layerでシステムを統合することを許すような場合は特に重要
- 性能
- themeに応じた変化形
- distributed_computing, legacy_systemの統合, 構造的な関係性, 特別なDBのサポート, 特別なhardwareのサポートなどを考慮する場合
- layered_business_architectureの構築
- ほとんどのlayerの基本的な部分は,スパイクと呼ばれるprocessで構築
- スパイク: 利用者の目に見える機能をtarchitectureに含まれるすべてのsubsystemやlayerを通して適切に実装するprocess
- すべてのsubsystemをまたぐ特定の機能を「通す」という特徴
- subsystem間のつながりのための釘
- incremental_developmentのための手法
- 最初のスパイクはarchitectureの基本的な部分を検証し,risk_managementの土台となる
- 開発の起点はservice layerかuiのどちらか片方で決める
- スパイク: 利用者の目に見える機能をtarchitectureに含まれるすべてのsubsystemやlayerを通して適切に実装するprocess
- serviceの定義が重要
- service interfaceを正しく決められれば,残りのtarchitectureを正しく組み上げていくのはずっと簡単になる
- すでに存在しているserviceを元に,新しいserviceの実装もできる
- DB設計から始める場合もある
- 既存の膨大なデータを扱う必要があったり,persistence_storageの要件が極端な場合など
- 並行開発
- architectureにおける中心的なsubsystemを早い段階で確立することが重要
- subsystemのinterfaceについて合意
- subsystemの同期を保証するために日次または週次でビルド
- service/domainどちらかのレイヤから始めることに合意する必要
- architectureにおける中心的なsubsystemを早い段階で確立することが重要
- column: 全layerをスパイクする
- 機能がすべてのレイヤを通ることが重要
- ほとんどのlayerの基本的な部分は,スパイクと呼ばれるprocessで構築
- business_logic layerにおける統合と拡張
- technologyと制御ポイント
- technology
- service architectureやmodelを上手く設計し,target_marketの必要とするtechnologyをサポート
- 制御ポイント
- 呼び出し元が主導権
- applicationは結果を生成
- システムが主導権
- 登録モデル,callback modelで呼び出し元が何らかの機能をシステムに登録できるようにする
- 呼び出し元が主導権
- technology
- APIによる結合
- registrationによる拡張
- registration: developerが何らかの方法で自分のcomponentやcallback_functionを登録して,applicationのcapabilityを拡張する処理のこと
- 制御ポイントはシステム側
- e,g, web_browserのplugin
- callback, listener, plugin, event_notificationなどの仕組み
- 検討事項(伝えるべきもの)
- registration modelを定義する
- registration可能なcomponentを構築するためのprogramming_languageについて,詳細な技術情報をdeveloperに提供する
- event modelを定義する
- 実行制御semanticsを定義
- timingに関する要件
- resource_management policyを定義
- error/exception protocolを定義
- registration modelを定義する
- technologyと制御ポイント
- persistence_dataの統合と拡張
- view
- DB_schemaのデータの使われ方と,実際の定義を橋渡しする中間層
- column: business_logicをDBに組み込む
- 必要な場合もある
- より単純で効果的な場合
- clientに統合オプションとして提案できる安全な方法がほかにないとき
- 必要な場合もある
- user_definition_field
- 些細な利便性に対して,本当にapplicationにとって適切なのか判断が必要
- table_hook
- 関連の完全性の強制は難しい
- 遅延の可能性があるのでできるだけ小規模にする
- 表計算ソフトのpivot_table
- dynamicなreport生成のためのcapabilityが必要なときの例
- ETL(Extract, Transform, Load) script
- DBに格納されている構造化データを操作しやすくするために設計された,utility program
- tarchitecture的な見方として,clientが確実に正しいデータを得られるようにできる
- marketecture的な見方として,ETL_sctiptを製品化して利益を得られる
- product全体の価値を大幅に高められる
- column: ETL_sctiptに課金する
- 何が起きているのか伝える
- data dictionary, data, table, 命名規則, 特定のカラムの値の大事な意味などを詳細に記したドキュメントを公開する
- view
- businessへの影響
- 専門サービス部門
- clientが統合によって目標を達成するのを手助けしたり,質問に回答したり,環境で動いているシステムを統合するのにかかる時間を短縮したりする
- marketectが価格付けを支援する
- marketectは専門サービス部門から上がってきた要望をdevelopment_teamに連携する
- 開発部門は,APIの使い方を示すsample programを作成する
- 研修program
- target userの具体的な目的に合わせる
- clientの満足度を高め,サポート負担を軽減
- tarchitectはbest practiceを捉え表現して伝える研修教材を作る
- systemの設計や統合拡張の方法の理解を深める
- stakeholder全員への考慮が必要
- column: どうやってdocumentを追加するか?
- documentの重要性
- 資格認定
- market shareが十分に大きいときのみ
- 検討事項
- product ecosystem
- 競合優位性
- 流行
- professional認定
- 独立した資格認定
- 学術的な信用
- user_community
- 一次情報源
- marketectが健全で活発なuser_communityを探して支援する必要
- 手掛かりとなりそうな活動
- community_Web_site
- 企業が用意する
- 教育素材
- column: ありがとう,でも自分たちのuiは自分たちで作るから・・・
- userが好みのuiを作ることもある
- uiへのニーズはそれぞれある
- mailing_list
- user_conference
- community_Web_site
- 使用許諾契約
- APIの仕様を必要以上に制限する文言がないことを法律部門に確認
- column: testにlicenseは要らない
- 同上
- 専門サービス部門
- 複数回のreleaseに向けてAPIを管理する
- まとめ
- integration: systemをprogram的にほかのシステムと接続するprocess
- extention: plugin architectureのように,明確に定義されたやり方でsystemに新しい機能を追加するprocess
- integration/extentionによって,clientが探し求めているproductを構築できるようになる
- 副次的な効果として,clientとの関係性がより密になり,競合他社への乗換リスクが軽減
- layer化architecture_patternとは,論理的,物理的なlayerによってシステムのさまざまな機能を構成するもの
- enterpriseクラスのsoftware_systemにおける統合や拡張に際して非常に優れた選択肢
- main layer
- ui
- service
- domain_model
- persistence_data
- architectureがどんな構成でも,スパイクが必要
- service layerやdomain_model layerに統合や拡張の口を設けるやり方はいくつもある
- 公開されたAPIを利用
- Web_browserのようなplugin architectureでcomponentを登録
- persistence_dataの統合・拡張を実現する手段
- view
- table_hook
- 表計算ソフトのpivot_table
- ETL_sctipt
- 統合や拡張が可能なapplicationがbusinessに与える影響
- clientを誘導する専門サービス部門
- clientが自分たちが何をすればよいのか理解するための研修program
- applicationのecosystemを作るための資格認定program
- applicationを取り巻くuser_community
- integration/extentionを明示的にサポートする使用許諾契約
- applicationの統合や拡張に際してclientと接することになる方法はすべて,慎重に管理
- それらの安定性について公にcommitmentしているため
- checklist
Ch09 brand, brand_element
- brand_element
- 名前
- system_componentの物理的配置
- 会社名/product name/sub component nameが〇
- product name/sub component nameとなることもある
- 認知や拡張について〇
- 主要componentの名前
- 重要な戦略
- marketectやmarketingの知見を持つ人が命名するべき
- 何をするものか技術的に正しく伝わる必要がある
- 実装と名前の乖離は正当化できる
- 変更にはコストがある
- marketectureと関わる名前の変化は技術的な面よりも遅い
- column: developerが名前をつけるべきなのは変数名くらいで,product名をつけさせてはいけない
- 良いproduct nameは成功の可能性を高める
- enterprise marketでは,product名以外の要素が支配的
- 提供元企業に興味がある
- 名前の国際化の必要性
- brand_elementは設定ファイルやログファイルにも潜んでいる
- brand_elementはerror_message, diagnostic_message, information_messageにも潜んでいる
- 名前は変わりやすい,最初のreleaseならなおさら
- codenameのようなもので当面は呼んでいた方が良い
- system_componentの物理的配置
- 見た目,スローガン,その他のbrand_element
- icon, splash_screen
- brand_color
- 音声のbranding
- trademark(™)記号を使うべきタイミング
- 法律上の権利を保護
- 注意を促す
- 商標記号は形容詞としてだけ使う
- productを配布するときや,productを利用しているときは,登録商標を表示する
- 法律上の制限はあるものの,internet domain nameはtrademarkにできる
- 名前
- in_license_brandの管理
- 適切に扱う必要
- column: 続行するためにOKをクリックしてください
- splash_screenを扱う必要があったとき
- brand_elementのカスタマイズ
- marketectは開発サイクルの早い段階で要件を具体化する必要
- どの要素が変更可能/変更すべきかを把握することが最重要
- 前者は常識的な初期値を決定,後者はカスタマイズの手続きの定義が必要
- どの要素が変更可能/変更すべきかを把握することが最重要
- 正確で詳細な情報の伝達
- bitmap画像の大きさ,サポートする画像形式,brand_elementとしての既定値など
- brand_elementをカスタマイズするための特別な契約が必要な場合もある
- marketectは開発サイクルの早い段階で要件を具体化する必要
- brand_elementの変更
- 困難な理由
- brand_elementの変化は大変ゆっくりしたもので,変更できるようになっていない
- brandがproductやarchitectureに与える影響が分かりにくい
- productの関連要素への影響
- subsystemの名前
- source_code repository
- product nameそのものが使われていなければ問題ない
- product nameに合わせている場合は必ず同期する
- 品質保証と技術サポート追跡システム
- categoryについて,実際のproductとの差異を小さくするよう保守
- componentの物理的配置
- APIの名前と構造
- error_message, log file, diagnostic_message
- 品質保証processと自動化テストのinfrastructure
- 販促品
- 変更と品質保証
- brand, brand_elementの変更にあたって,品質保証は非常に大きな役割
- 困難な理由
- まとめ
- brand_elementは,tarchitectureに対して,すぐにはわかりにくい影響を広範囲に与える
- icon~install先にまで及ぶ
- product nameを考えるのはmarketectの仕事
- brand_elementの国際化は難しい
- product nameに関するさまざまな法的表示の使い方の理解
- in_license technologyはbrand_elementとしての要件を強制する場合がある
- product nameを変更する際は,あらゆる観点のclient experienceを総合的にreviewする
- brand_elementは,tarchitectureに対して,すぐにはわかりにくい影響を広範囲に与える
- checklist
- 必要なbrand_elementはすべて整理したうえで合意できているか
- すべてのbrand_elementが必要に応じて国際化されているか
- error_message, diagnostic_message, log fileに,合意済みのbrand_elementがきちんと反映されているか
- 登録商標やtrademarkなどの記号が適切に使われているか
- partner企業や技術license供与者による変更など,置き換えられる可能性のあるbrand_elementが明確になっているか
Ch10 usability
- 性能と拡張性について
- tarchitectureがusabilityに与える影響,特に性能を改善する要因としての影響を見る
- usabilityはお金になる
- usabilityはuiよりもはるかに奥が深い
- ほとんどの場合tarchitectureが影響している
- user自身と,systemを利用する状況の両方の理解が必要
- marketectにとっては,システムの競争上の優位性を保証するため
- userにとって最も重要なニーズを確実に満たし,長期にわたって友好関係を築くために必要な基礎を整えるためでもある
- tarchitectにとっては,性能や国際化などをより一層追及したcapabilityを構築するため
- usabilityの量的,質的な指標になる
- 量
- 性能やデータ入力エラー率など
- 質
- 満足度や理解のしやすさ
- 量
- marketectにとっては,システムの競争上の優位性を保証するため
- userを理解するための手法
- 観察,interview,質問,〇直接体験
- 直接体験: 経験的な要件
- 観察,interview,質問,〇直接体験
- usabilityはproductの生涯にわたって見返りをもたらす
- merit
- 研修コストの削減
- support, serviceコスト削減
- error発生時のコスト軽減
- userの生産性の向上
- client_satisfactionの向上
- 保守性の向上
- merit
- column: market needsとusability
- usabilityのmeritは社内でもmarketと同じように受け入れられるはず
- mental_model, metaphor, usability
- userがどのようにタスクに取り組むかについて理解が深まれば,そのタスクについてのmental_modelを理解できるようになる
- mental_model: 世界の物事を説明したり,simulation,予想,制御したりするうえで,心の中に抱く一連の考え方や構造のこと
- taskやそれを達成するためのツールに応じて形成される
- 時間とともに変わっていく
- 概念モデルはmental_modelの表現の1つ
- mental_modelを理解し,概念モデルの役割を明確にする → metaphorを生むための基盤
- metaphor: systemを作ったり修正するために使える,ある物事の理解を助けるために別の言葉で表現するもの
- tarchitectは,application architectureを説明するのにmetaphorを使うことが多い
- よいmetaphorは,tarchitectureとuiの両方を具体化してくれる
- tarchitectが選んだmetaphorが,明確に定義されたmarketに対して適切なやり方で伝えられる必要がある
- metaphorはbrand_elementや価格モデルにも影響する
- systemが最大の価値をもたらす特定のmarket_segmentがmetaphorによって強調 → 価格モデルに影響
- 完全に新しいproduct: productの機能や提供の仕方を伝えられるかどうかmetaphorによる
- これらのすべての理由から,metaphorはmarketect/tarchitectが密に連携して開発する必要がある
- metaphorはusabilityに影響を与える点で特に重要
- training cost激減,満足度急上昇,システム全体が快適になる
- ui設計におけるtarchitectureの影響
- 影響範囲
- cardinality(濃度)
- あるrelationに参加しているentityの数
- tarchitecture全体とui設計それぞれで必要
- 増加し続ける大規模なデータを可視化するツールは幸運にもたくさんある
- feedback
- 最も重要なheuristic(探したり発見するのに役立つ → 発見的(手法))の1つ
- e.g. progress bar
- tarchitecture上の課題がいかにしてui設計に影響するかの例
- validationなど,最高のusabilityを得るにはそれぞれのdeviceごとのcapabilityを理解する必要がある
- 明示的なuser_model
- systemの振る舞いをmental_modelにあうように調整する
- e.g. word processor
- workflow support
- workflow: ある種の目に見えるuser_model
- mental_modelへの理解の上に成り立つ
- workflow: ある種の目に見えるuser_model
- transaction support
- transactionはuiに特有のcapabilityをsupportするように設計されている
- e.g. 情報の表示方法など
- error_response
- errorの表現方法に関する選択も,基礎となるtarchitectureに影響する
- globalization/localization
- OSやplatformはuserが言語を指定するためのinfrastructureを備えている: 大きな利点
- development_teamの設計上の選択がglobalizationを困難にすることがある
- e.g. 部品を固定サイズにしてしまうこと
- userの目に触れる情報はsource_codeには埋め込まず,何らかの識別子を参照する必要がある
- development processのできるだけ早い時期に,対象言語を定めるべき
- column: 詳しく,もっと詳しく,もっともっと詳しく
- 地域化されるsoftwareに必要なのは,強力なベータテスト計画
- requestの中断
- requestを区別するための識別子が必要
- かなりのコストがかかるが,見合うだけの成果がでることもある
- requestの取り消し
- transaction managementやpublish_subscribe protocolなど洗練されたtarchitecture設計では,状態が共有されていても適切に取り消せるcapabilityを提供できることもある
- 高機能なuiがありapplicationの永続状態が適切に反映されていれば,操作の取り消しに意味があるかどうかを判断するための情報をユーザに提供できる
- transactionの相殺
- 設計の早い時期に追加することでusabilityは劇的に改善する
- timeout
- 初期値の選択は非常に繊細な判断が必要だが,うまくやればusabilityが改善する
- sessionに関するparameterをsystem administratorが変更できる必要がある
- network_availability, network_speed
- userの置かれる状況を考える
- shared_resource
- 障害回復
- systemが障害を制御して,妥当なやり方でそれを復旧するのが望ましい
- systemに関連することはユーザに注意を促す方が好ましい
- cardinality(濃度)
- 影響範囲
- speedの必要性
- 話している対象をはっきりさせよう
- 性能用語
- throughput
- performance
- throughputの逆数
- latency
- capacity
- entity数
- scalability
- reliability
- response_time
- 最小限未満,最小限,平均,理想,最大限
- ますはシステム構成を特定(「固定」)
- 次に,テストしたいtransactionあるいは操作を特定し,利用するデータを用意する
- systemの性能は線形増加しない
- queueの上限を知ることは重要
- serverが処理できる最大のバーストがわかる → 信頼性を構成する要素の1つ
- systemの応答に関する心証は性能値と必ずしも一致しない
- response_timeはできるだけ短いほうが良い
- 5s~10s以内である必要
- 故障への適切な対応 → 信頼性の一因
- user_modelの定義が重要
- 多様な操作のsupport
- 本番環境で取得したシステムの操作に関連したログファイルを使うと,優れたuser_modelを導ける
- testの複雑さについて,性能の見積もりは控えめを心がけるべき
- 性能用語
- marketectが性能について心から望むこと
- 性能に関連した質問について,自信に満ちた,信頼できる,正しい回答をしたい
- 新しいreleaseや推奨hardwareの変更があった場合は,性能を計算しなおす必要がある
- 性能改善が期待される
- userに応答を返す
- 性能とtarchitecture上の影響
- hardware任せの問題解決
- 最も簡単
- tarchitectureのscalabilityが理解されている場合だけ,適切に機能する
- 粗粒度transactionを使う
- thread処理が性能に及ぼす影響
- profilerで計測
- 通常の処理と異常時の処理を分離
- 通常時は高速,異常時は適切さが必要
- 結果をキャッシュする
- 誤った結果を返してはいけない場合を特定しておく必要
- dynamicにcacheの有効無効を切り替えできるarchitectureが必要
- background_processing
- self_serviceで使えるように設計
- platformのidiomを学ぶ
- taskを減らす
- hardware任せの問題解決
- 話している対象をはっきりさせよう
- まとめ
- usability: systemが備える属性で,ユーザが必要なタスクを完了させるうえで簡単に,効率よく,最小限のエラーで済むようにするためのもの
- 使いやすさ: 目的を達成するためにfrastrationを全く感じないか,ごくわずかで済むこと
- usability: product brandingにとって中心的feature, productのあらゆる面に関係する
- 成功するsolutionのusabilityは優れている.長期的な利益に貢献
- mental_model: 観測しているものを説明,模擬,予想,制御するための考え方や構造の集まり
- 概念モデル: mental_modelの何らかの表現
- 言葉/図,形式的/非形式的
- metaphor: あるものを別の観点から理解するためのモデル
- uiをtarchitectureから分離することで保守性が高まる
- 絶対確実な方法はない
- 性能は重要
- marketectは性能に関する質問に対して,自信をもって正確で信頼できる回答をしたいと思っている
- especially, enterprise_software
- 性能改善のためのtechnique
- cache, background_processing, 空いているprocessorへ処理を渡す,処理自体をなくすなど
- usability: systemが備える属性で,ユーザが必要なタスクを完了させるうえで簡単に,効率よく,最小限のエラーで済むようにするためのもの
- checklist
- 主要なタスクのusabilityをテストしている
- 概念モデルを描いており,userのmental_modelを理解したり,システムのmetaphorを作るのに使っている
- systemの設計や実装を見れば,systemのmetaphorが分かるようになっている
- 性能を定義する合意された用語集がある
- marketing部門や販売部門が構成を見積もるための方法を提供している
- 問題解決をhardwareに任せるのはいつなのか,どうやるのか理解し,それによりどうなるか理解している
Ch11 install
- 設計が拙いと経済的な損失が大きい
- OOBE(Out of box experience)
- computerなどの端末を初めて使うときの経験を表現
- installに関連するわかりやすい目標をmarketectがtarchitectのために設定していると有用
- e.g. 平均的なユーザがtechnical supportに電話しなくてもinstallできるようにするというもの
- column: installが拙いことのコスト
- 痛い!きっと怪我をしたに違いない
- clientが恐れていること
- 難しすぎる
- 複雑すぎる
- 簡単に何かを壊してしまう
- どのくらい時間がかかるか分からない
- 入力する情報が多すぎる
- clientが恐れていること
- installとarchitectureの関係
- forceと選択
- sub_componentの管理
- installerを用意するのが自分かユーザか決める必要
- どちらのケースもある
- installerを用意するのが自分かユーザか決める必要
- in_license_contractの要件
- license_contract
- EULA(End User License Agreement)
- license_contractの一部または全てをinstall programに含めておく
- EULA(End User License Agreement)
- column: 準備ができたら引き継ぎます
- install準備programで,softwareに必要なcomponentがあること,適切に設定されていることを保証
- business_model
- business_modelはinstallerの作り方に影響
- install processがbusiness_modelを実現する助けになる
- installの責任分割
- InstallShieldなどのinstall programにどれだけのことができるのか学習しておく
- install先の環境
- target_marketの環境によって望ましいprocessが異なる
- installの役割
- 複雑なinstallも役割ごとに対応してまとめておく
- developerに気づかせる
- developerにやってみてもらう
- sub_componentの管理
- forceと選択
- installのやり方
- installのための情報を収集し,事前条件を検証する
- 必要不可欠なすべての構成情報と,重要なcustomize情報を出しておく
- 検証内容
- 空き領域
- network_connection
- 必須entityの設定
- access権限
- 必要な場面を限定する
- install
- install後に確認する
- softwareの実際の動作に注目
- ログを記録
- 後始末
- manual
- user registration
- installのための情報を収集し,事前条件を検証する
- 仕上げ
- userはmanualを読まない
- 一目でわかるロードマップを提供する
- install/uninstallをテストする
- installerは最初のiterationでテストする
- 自動ビルドprocessに組み込める
- あらゆるoptionを試す
- 自動化する
- platformのガイドラインに従う
- install処理をscript化する
- installerは最初のiterationでテストする
- userはmanualを読まない
- まとめ
- installはお金そのもの
- ほとんどのユーザはinstallを恐れている
- installはarchitectureの構造によって決まる
- すべてのcomponentとcomponentの依存関係は適切に管理が必要
- 検討中のinstall processがすべてのlicense条項を満たしているか確認する
- softwareをinstallする典型的なalgorithm
- installに利用する情報を収集し,事前条件を検証
- install
- install後に確認
- 内部でテストするため,install processを自動化する
- enterprise環境のために自動化しやすくする
- とにかくテスト
- checklist
- install processによって,sub_componentがそれぞれどのように扱われるのかを明らかにしている
- install processが全てのlicenseされたtechnologyの要件を満たしている
- software_architectureのinstall担当者に求められるスキルレベルを明らかにしている,レベルは妥当である
- installの正しさを検証する方法がある
- install processは対象platformのガイドラインに完全に従っている
- 平均的なユーザならdocumentを見なくてもinstallできる
- install/uninstallをともにテストしている
Ch12 upgrade
- upgradeはinstallよりも問題になることが多い
- clientにとって深刻な被害を引き起こすことがある
- dataの喪失,統合の問題,元のfeatureの振る舞いの変更など
- 最初のreleaseの売上よりもupgradeの売り上げの方が多い,marketingの主な役割はclientの発見と関係性の維持
- clientにとって深刻な被害を引き起こすことがある
- installと同じように悪いことしか思い当たらない
- upgradeに伴う恐れ
- 手戻り
- 特に統合の問題
- upgradeの連鎖
- upgradeに関連する依存関係を全て明らかにする必要がある
- data移行
- DB_schemaについて,めったに変更されないもの,一度作成したら変更すべきでないもの,頻繁に変更され得るものを分離することが重要
- 労力が大きく減る
- DB_schemaについて,めったに変更されないもの,一度作成したら変更すべきでないもの,頻繁に変更され得るものを分離することが重要
- data保全
- 認証process
- new API
- clientのニーズに基づいて慎重な管理が必要
- column: 一度統合したものは二度とupgradeしない
- 新しいfeature
- 学習コスト
- systemにアクセスできない
- 切り戻し
- upgrade processの手順を入念に計画して理解を深められれば,簡略化できる可能性がある
- column: 私はもっと違うことで困っている
- upgradeに伴う苦痛の一覧を作る必要がある
- 手戻り
- upgradeに伴う恐れ
- upgradeの苦痛を取り除く
- 苦痛のないupgradeのための選択肢
- 回数とtiming
- marketに起きるevent, rhythmを理解する必要
- upgradeの準備
- Ch11と同じ
- clientが現在利用しているversionの確認
- data移行
- column: yesよりnoが簡単
- featureを取り除けるのはupgrade processの間だけ
- featureはapplicationのpersistence_dataと何らかの関連を持っていることが多い
- 調査 → featureが使われているかどうか突き止められる
- 最適なupgrade方法を決めるため,現在のinstall状態を評価するprogramを書くのは良い考え
- upgradeでできること,やるべきことがわかる
- featureを取り除く
- development_team全体への調整が必要
- featureの追加と同じくらい大変,たいていはそれ以上に大変
- 移行processの間のみ移行できる
- ツールを使う場合
- ファイルのデータを変換するときは警告が必要
- column: yesよりnoが簡単
- 設定情報とcustomize情報のupgrade
- あわせてupgradeする
- old version
- clientがすべて同じold versionではない
- 一段階移行
- すべてを最新に直接upgrade
- 多段階移行
- 最新のversionへupgradeできるいくつかのold versionを部分集合として抽出する
- 共存,または置き換え
- 可能な限り置き換えが〇
- ほかに削除するタイミングはないため
- column: どのversionを使えばよいのだろう
- 共存はテストの複雑さやサポートコストが象か
- 可能な限り置き換えが〇
- 回数とtiming
- 苦痛のないupgradeのための選択肢
- upgradeとmarketの成熟度
- innovatorはほかのprocessとは異なりデータ移行についてはかなり重要視している
- 欠陥のあるupgrade processはmarket_segmentによらず将来的に重大な問題を起こす
- まとめ
- upgradeは,相当数のclientにとて実に様々な形で頭痛の種となる
- 継続的なtechnologyの進化によって,upgradeの連鎖が発生する
- 前提となる条件を詳細に調べる必要
- upgradeの間にclientのdataを削除しないようにする
- clientにとって,どのくらいの頻度ならupgradeを受け入れられるのか理解する
- upgradeの準備ができているか評価するtool
- upgradeの影響が分かる
- 不要な/未使用のfeatureを取り除けるか分かる
- すべてのmarketのadopter segmentは優れたupgrade processを求めている
- checklist
- upgradeによって何かしら問題が生じる可能性のある分野が特定できていて,それを小さくしようと試みている
- すべての移行パスをテストしている
- どんなclientであろうとも,現在のversionへupgradeできるのは,以前のversionの中でも特定のversionを使用している場合だけであると定めている
- upgradeに伴いどのくらいの停止期間が生じるのか定めている
- upgradeを完全にuninstallし,すべてのdataを旧versionに戻すための詳細な手順を用意している
- upgradeが正常に完了したかどうかを確かめ,もし失敗したならどこで失敗したか特定できるテストを用意している
Ch13 configuration
- 設定しやすくすること自体が,marketectにとってもtarchitectにとっても価値ある目標
- 設定可能性--usabilityの一因
- costの問題
- architectureの性質に左右される
- module化を進めると影響も大きくなる
- 設定に関するコストは増え続ける
- 適切な設定parameterが必要
- system_context
- 設定parameterに反映する必要があるもの
- systemを適切に機能させるために必要な,あらゆる側面におけるcontextの情報
- 取得・設定できるようにすることで,developmentのときとdeploymentのときそれぞれで設定できるようになる
- contextの情報
- tarchitectが整理する
- 主要なfile, directoryの配置
- 起動するための情報
- 情報が失われた場合も,必要最低限の機能を提供できるようにしておく
- portability switch
- 互換性の制御
- clientと問題を共有して,いくつくらいの設定parameterを使って対応するか決めてもらう
- 性能parameter
- 高度に洗練されていれば自動調整もできる
- いくつかの簡単なparameterは利用者が調整できるようにしておく
- column: 過ぎたるは猶及ばざるが如し
- 設定しないとシステムが動作しないものだけを設定parameterにするべき
- 必要な場合は,変更可能なparameterとして妥当な初期値を設定しておく
- 初期化と実行
- 実行中にも設定を変えられるようにしておく必要があるもの
- 透過的なparameter
- 実際はapplicationを素通りしてin_license componentに引き渡されるparameter
- tarchitectは,初期化中と稼働中それぞれのtimingでどの設定を扱うべきか検討が必要
- 値を設定する
- system administratorのような人間,system間の相互通信によって値をやりとりしたり指示を受け取ったりするような他システム,1つ以上の値を自動的に設定するように構成されたシステム自身の3つのentityが連携して設定する
- それぞれにそれぞれの権限がある
- marketを念頭に置いてどのentityによる変更を優先するか決定する
- 適切な値を設定する
- parameterの影響を受けるユーザのニーズと,それを設定するユーザのニーズの両方を満たす必要
- 複雑になるほどusabilityを損なう
- どんな値を,どうやって,なぜ設定するのか,システムの運用にどのような影響があるのかの理解が必要
- 設定ファイル自身に記載する
- 設定parameterのheuristics
- 簡単に変更できるよう,そして,たとえシステムのほかの部分がダウンしていたとしても変更できるようにする
- 単純で管理もしやすい平文のまま,システムの外部に配置する
- 全ての情報を一か所に配置する
- 分かりやすい場所に分かりやすい名前のファイルを配置する
- 「システム設定情報」のような名前で,installしたapplicationのroot directoryに出力するなど
- XMLは,単純で必要に応じて詳しく書け,管理もしやすい
- Windows registryのようなものを使う場合は十分に注意する
- 移植性がなく,技術に明るくないユーザが変更するのは困難
- 設定情報の取得と技術サポートへの送信を簡単にできるようにする
- 設定する値を間違いにくくする
- 間違えたら,すみやかにユーザに伝えて,システムを停止するか,妥当な初期値に変更して続行する
- 対故障性に優れた設計では,設定parameterを何らかのentityやobjectの属性を永続化したものとして扱う
- ほかのobjectやsubsystemはこの設定objectを介して情報を取得できるようになる
- file管理も関知不要
- 検討すべきobject
- system context objectでシステム全ての環境の情報を格納する
- computer, service instance, user, user profileそれぞれを表すobject
- 設定情報の意味を理解するobject
- あとからつじつまを合わせるのはとても面倒
- ほかのobjectやsubsystemはこの設定objectを介して情報を取得できるようになる
- 簡単に変更できるよう,そして,たとえシステムのほかの部分がダウンしていたとしても変更できるようにする
- まとめ
- 設定とは,usabilityにおけるきわめて重要な観点
- 設定parameterはsystem_contextを表現できるように設計される必要
- system_contextの定義
- systemの設定が必要なtimingは,実行前と稼働中
- 実行中に設定を変更できるようになっているとデバッグしやすい
- clientが自ら適切な設定をするには支援が必要.労を惜しまないようにする
- checklist
- systemのすべての設定parameterを決定
- それぞれのparameterについてsecurityと監査の要件を規定
- 初期化中にだけ設定を使うのか,実行中に変更できるようにするのかも決定
- それぞれのparameterの設定方法をdocument化し,正しい設定をするための手引きを提供
- systemのすべての設定parameterを決定
Ch14 log
- column: たとえ後からでもやらないよりはマシ
- logは掛けた手間に見合う成果ある
- 何が起きているか知りたい
- log category
- debug, error
- error recovery
- performance tuning
- capacity planning
- 行動追跡と監査
- system configuration
- 運転状態
- 正しいcategoryに出力する
- logにはsystemが生成した情報だけを出力する
- 付随的な処理に過ぎない
- column: 2つに分けるくらいなら,分けない方がマシ
- eventの種類ごとに適切な識別子をつけて,1つのlogファイルに出力
- log category
- 事実だけではない
- logの生成には,起きていることを表現するのに十分なcontextを含める
- logはprogramのtraceをきれいに見せるためのものではない
- developer用のデータを出力するログは別のファイルにするのが〇
- logの書式と管理
- logの書式
- logの管理
- dynamic logging
- logのparameter(詳細レベルや出力対象など)を実行中に変更できる機能があると〇
- 必要に応じてlog出力
- threadごとのlogging
- log level
- 開始と終了に加えて,詳細レベルの指定を可能にする
- logに出力されるべきか表す数値の割り当て
- 補完として,labelづけもある
- e.g.
- debug
- information
- warning
- error
- e.g.
- categoryに従う
- API
- 設定可能な構文
- 異なる書式の方が処理しやすい場合もある
- あらゆる例外をログに出力
- 消せるようにする
- ファイル名に日付があると消しやすい
- security
- 暗号化,権限
- 転送の自動化
- dynamic logging
- loggingの標準とlibrary
- platformへの依存の有無
- libraryは代替手段
- column: logのreuseには十分に気を付ける
- userが変更しやすいなどの問題になる
- logの後処理
- 圧縮サービス
- 循環ログなども〇
- 同期ツール
- logの時間の同期を保証
- log viewer
- 圧縮サービス
- logging service
- loggingをdevelopment_teamに提供するサービスとして考えることは有用
- 賢いログを実装することに伴う複雑さを抽象化
- ほとんどはsingleton
- 中央集権的なサービスとして実装するmerit
- globalization
- 時間的な順序の統一
- 柔軟な出力先の選択
- 複数のinstanceやサーバを跨いだ統合
- log file nameやその内容に,instanceやserverの情報を付加
- e.g. processやthread id, machine IP address
- log file nameやその内容に,instanceやserverの情報を付加
- まとめ
- logが情報提供以外に支援する活動
- debug
- error recovery
- performance tuning
- capability planning
- 行動追跡,および監査
- system configuration
- 運転状態
- logは利用者目線で構成が必要
- 文脈を表す情報を含めるべき
- 分析しやすいように構成
- log fileが運用や環境に与える影響を評価
- disc領域が不足しているときの異常発生などにも適切に処置できるようにしておく
- logが情報提供以外に支援する活動
- checklist
- log fileそれぞれの目的が定まっている
- log fileそれぞれについて,利用する立場になって有用性を確認した
- 適切な情報が適切な書式で含まれているなど
- log fileからdeveloper専用のdebug informationを注意して除去した
- log fileはui部品のために用意されたglobalization guidelineに従っている
- log fileはsystemのほかの部品のために用意された可搬性guidelineに従っている
- log fileは分析しやすい
Ch15 release_management
- release_management
- clientに対して正しい成果物が出荷されることを保証する
- 説明ラベルをつけて成果物を特定・整理・管理
- SKUあるいは部品番号によってしかるべきback_office_systemに統合する
- 構成管理と密接な関係
- 構成管理
- systemの構築中にさまざまなcomponentや関連する成果物を特定・整理・管理するprocess
- 構成管理
- 本当に必要なのだ
- 変更管理が必要になる
- 構成管理の影響
- teamwork
- componentの必要条件の理解
- 問題発生を防いだり診断したりしやすくなる
- messaging_systemでは,messageにversionの識別子が必要
- 内容や処理条件の変更を管理
- release_managementはclientに必要
- どのversionを発注したのか,以前のversionと互換性があるのかどうかを知っておく必要
- 利用可能なパッチやupgradeが自分たちの環境で自分たちの発注したversionに適用できるかどうかも知る必要
- 複雑な問題
- technology自体の観点もあるし,基盤となるtechnologyによっては選択肢がないこともある
- baselineの確立
- 用語
- program family
- すべてのcomponentのすべてのversion全体
- component/成果物
- system内部で個別に識別可能な最小単位
- version
- 固定・凍結したcomponent/成果物
- source_codeと派生物(object code, API document)のバージョンの保守が重要
- 内部用と外部用それぞれのバージョン識別子が必要
- revision
- component/成果物の新しいバージョン
- 以前のバージョンの置き換えを意図したもの
- variation
- componentやその他の成果物の代わりになる実装
- OSごとのsoftware componentなど
- distribution
- 特定のclient群に配布するために作られたバージョン
- 1つ以上の保証されたcomponent/成果物から構成される
- release
- 命名されバージョンが払いだされたcomponent/成果物の集合
- revisionとは違い,必ずしも線形ではない
- program family
- 依存関係や前提条件の管理が重要
- ほかのcomponentの特定のバージョンに依存するcomponentを内包していることが多いため
- 用語
- release_management
- 何をreleaseするか
- 完全なrelease: product全体のrelease
- 部分release
- base systemのcapabilityを拡張
- option moduleなど
- patch_release
- install済みで既知の不具合がある1つ以上のcomponentを完全に置き換えるもの
- 機能追加などは×
- いずれも同じrelease process
- 対象は誰か
- alpha releaseは内部userが対象,ほかは外部user
- 限定release, 管理releaseは特定のclientに対して行われるrelease
- 誰を対象: scope, 何をrelease: size
- 混同しないようにする
- clientの利用状況などの理解が必要
- なぜそれが必要か
- アメとムチの使い分けでmarketectはclientにreleaseを受け入れてもらうようにする
- install baseをできる限り最新に保つことが,marketectのメインタスクになる
- どのversionからでも最新バージョンにupgradeできるようにする必要
- 手順がややこしくなったとしても
- 何をreleaseするか
- releaseの識別子
- clientがreleaseを識別できるようにするために使われる
- 完全な識別子
- product nameと,productの適切なrevisionやvariationを含むversion情報によって構成
- 目的: 必要な情報をできる限り短い名称と識別子で表現 → 全体的な効率が改善
- 完全release
- product name + revision情報を表現するための4桁の数字文字列(x.y.z.ビルド番号) + 任意のvariation識別子
- x: major
- clientから見えるarchitecture, featureの変更
- marketectが合意し,変更内容を定義
- business上の理由もある
- client全体にできるだけ早く配布する目的意識をmarketectは持つ
- clientから見えるarchitecture, featureの変更
- y: minor
- 望まれるfeatureやその他の改善
- marketectはバージョンをあげる契機となるイベントをすべて定義する必要
- z: 保守, dot
- x, yを共有するほかのdot releaseと互換性が必要
- x: major
- marketingのためには,clientにはmajor, minor numberのみ見せるのが〇
- 管理コストを減らすため
- product name + revision情報を表現するための4桁の数字文字列(x.y.z.ビルド番号) + 任意のvariation識別子
- 特別release
- 識別子の形式は,ほぼ完全にmarketingの要因に依存する
- 基本的な配布物のoptionになっていたりする場合は,4桁の数字文字列の識別子が〇
- 命名規則が一貫
- option componentに対するmental_modelを構築しやすくなる
- 入手可能なproductすべての一覧の作成も容易になる
- 命名規則が一貫
- 一意の識別子があればよい
- 特別に定義した名前と日付があれば十分
- 課題
- componentや機能の間の依存関係とそれらに対するproductの依存関係の管理
- release_cycle識別子を管理するruleやarchitectureの設計を通じて表現
- componentや機能の間の依存関係とそれらに対するproductの依存関係の管理
- 互換性のruleを守る必要がある
- patch_release
- productのsubset
- 既知の不具合を含むcomponentを正確にreplace
- 識別子の採番方法の問題
- どんな場合でも実際に使われている機能に関係がある
- 既存のproductに強く依存しているので,patchの識別子からproductを参照できるようになっていると分かりやすい
- 原因と紐づけるため,名前と日付の参照
- product name-x.y(.z(.build number))-patch nameとなる
- 複雑なproductの場合は,patchで影響を受ける領域を示すこともある
- technical supportのWebサイトでどのパッチをダウンロードすればよいかclientが見つけやすくするため
- patch nameの前にoption nameを入れるようになる
- あるproductに関する大量のパッチがあるなら,すべての保守releaseにまとめるのが〇
- service packというapproachも〇
- documentにパッチを累積的に適用するものかどうかはっきりと記載する
- service packというapproachも〇
- patchをバージョン管理するとしても,どうしても必要になるまではバージョン識別子を含めない方が良い
- → product name-x.y(.z(.build number))-patch name.patch versionとなる
- 洗練されたarchitectureでは,patchをproductを一緒にpackagingできるし,何がinstallされ何がinstallされないかを追跡できる
- softwareを内部で実行し自動更新できるようになっていることもある
- ManageSoftのように企業の管理者の代わりにさまざまなPCのソフトウェアのスナップショットを取得するproductを開発している会社もある
- architectureを拡張して,パッチ管理機能を持たせたいとき
- 何がinstallされ何がinstallされていないかを判断できるくらい,洗練されたarchitectureが必要
- internetを通じて更新データを取得 → remote serverと通信する仕組みが必要
- 前提条件が整っているか判断が必要
- 自動更新がシステムや何らかの設定を壊すことなく正しくインストールできるかどうかを判断する必要
- 間違っていたら変更のロールバックが必要
- これらは極めて複雑な要件なので,一般的にはおすすめできない
- column: bug fixは無料でなくてもよい
- variation
- 命名してその名前を識別文字列の中に意味のある形で含めるのが最もよい
- portability, globalization, 性能特性などが要因となり複雑になることもある
- 冗長でも間違えにくければ〇
- SKU(Stock Keeping Unit, 在庫管理単位)とserial_number
- SKUの管理
- SKUによりreleaseを一意に識別でき,社内の在庫追跡システムで1つのreleaseをその他の全てのreleaseと区別できるようになる
- 識別子としてのSKUはreleaseとは独立したもの
- 使用される状況
- 特定のreleaseへの注文情報,価格情報
- 物理的な商品の在庫数
- guideline
- scope, targetには関係なく,販売するreleaseにはすべてSKUを割り当てる
- scopeに関係なく,一般releaseにはすべてSKUを割り当てる
- 一般消費者が入手できるものを追跡しやすい
- 取引先管理システムや頒布管理システムでSKUをキーとして用いているなら,すべてのreleaseにSKUを割り当てる
- 誰がそのreleaseを入手したか追跡できる
- technical supportや無料download siteなど,self serviceのWebサイトに置かれるだけのreleaseには,SKUを割り当てないようにする
- 販売されるわけではないので,財務システム内でSKUを払いだす必要がない
- 誰が入手したか基本的には追跡不要
- ただし,企業のpolicyに従う
- SKUの形式は口出しできないが,必要な量は伝えられるし,目的を達成できる形式についてともに考えることはできる
- SKU,外部名称,完全なrelease識別子の3つの論理的なcomponentを使う例
- productに関連するSKUの総数の見積もり(以下の合計)
- 完全release数*完全releaseのvariation数
- SKUを与えられる特別release数*特別releaseのvariation数
- option component数*option componentのvariation数
- そのほか何らかの理由で生成されるSKUの合計
- serial_number, registration, activation
- SKUは,個々のclientに販売したproduct 1つ1つを区別することはできない → serial_numberが必要
- serial_numberをキーとしてclientと企業が紐づく
- activation: 強制的なregistration
- licenseの強制
- 電子productへのserial_numberの埋め込みはコストをかけて開発processの変更が必要
- license強制スキームを適用して,softwareの複製や改変を防ぐ
- serial_numberにより貴重な統計情報を収集でき,marketing campaignを調整できる
- serial_numberが適切に登録されれば著作権侵害行為も減らせる
- activationも効果がある
- activationの手順
- activation processを採用するかどうかは戦略的判断
- vendorの提供するサービスを利用するときは,既存・計画中のbackend_systemやworkflowを照合して評価する必要
- backend_systemを管理する方がactivationのvendorを選ぶよりずっと大変
- SKUの管理
- tarchitectureへの影響
- release_managementの要件の理解 → release_managementをより簡単にする選択ができるようになる: tarchitectureの改善
- 考慮点
- すべて作り直す
- 出荷するものは全て導出(ソースから実行ファイルをビルド)か構築(Framemakerで作ったマニュアルを印刷)する
- できるだけ既存のsolutionとinfrastructureを利用する
- できる限り早い時点でtarchitectureにバージョン情報を組み込む
- 各componentは自身が依存しているcomponentを把握しておく必要がある
- data_driven_approachが1番簡単
- 設定ファイルに書き込まれた依存関係を処理する,特殊な依存性check componentを使う
- message, protocolのversion_control
- DB, tableはバージョン情報が必要
- schema内の各tableのバージョン識別子を含むシステムテーブルを作る
- update可能なあらゆるcomponentはversion_controlが必要
- 内部のcomponentは自分が必要とする外部のcomponentのversionを把握する必要
- version_controlされたすべての成果物について,バージョンを取得する方法を提供する
- patchに伴うテストやサポートに注意
- 複雑さは指数的に増大する
- すべて作り直す
- まとめ
- release_managementとは,productを求めているclientに対して,正しい成果物の提供を保証すること
- 考え方
- program family
- component/成果物
- version
- revision
- variation
- distribution
- release
- release_managementには3つの要因
- 何を誰になぜreleaseするか
- releaseは区別が必要
- 4桁の数字文字列は識別子として実績がある
- SKUは経理やfulfillmentなど,back_office_systemでrelease_managementするために使われる
- checklist
- releaseごとに,何をreleaseし,誰が対象なのかを定義している
- releaseに対するclientの期待値を見積もっている
- releaseを区別する仕組みを定義した
- SKUが必要なreleaseには,SKUが振られている
- serial_numberが必要なreleaseには,serial_numberが発行されている
- releaseごとに,何をreleaseし,誰が対象なのかを定義している
Ch16 security
- securityが根本的に違うところは,物事を簡単にするのではなく難しくするものだという点
- 成功するsolutionにとってきわめて重要だが,完成する直前まで見落とされがち
- 初めから入れておく必要がある
- virus, crack, 海賊
- identity_control
- 利用者ごと,役割ごとに異なるcapabilityを定義できる仕組みや,公にユーザの行動を追跡する仕組み,ユーザが申告通りのユーザであることを検証する仕組みを構築する必要
- transaction_security
- 安全な通信
- 未認可のcomponentと置き換えたり,メッセージに介入したり,変更したり,ハイジャックしたりできないようにする必要がある
- software_security
- 未承認の人は誰もsoftwareを変更できないようにするべき
- 未認可の権限昇格を許さない
- virusや著作権侵害行為から守ってくれる
- information_security
- 未認可のアクセスを防ぐ
- applicationにとってsecurityのどの要さが重要か明確にすることが,成功するsolutionを構築するカギになる
- risk_control
- 安全性を高める難易度は指数関数的に上昇する
- riskは無視はできないが管理はできる
- securityはmarketectとtarchitectが連携する分野
- marketect
- riskを評価する活動を率先して行う
- tarchitect
- さまざまな問題を解決する方法をmarketectに説明する
- 自身のriskを評価する
- 対処すべきリスクや仕組みを明らかにし,潜在的な問題への対象について難しい決定を下す
- 見ざる,言わざる
- riskは大きい
- identity_control
- identity_control
- 認可,誰に何ができるのか定義
- 静的な側面
- systemの中で特定の操作を実行する権限や特定の部分にアクセスする権限,DBの特定の部分にアクセスする権限を,どの主体が持つか定めること
- 信頼できるユーザの作成,ユーザや分類に応じて権限を定義
- 動的な側面
- 指定されたユーザ・主体が特定の操作を実行したり,システムやDBの特定の部分にアクセスするために必要な権限を持っているかチェックすること
- 実行時にチェック
- 認可ルールはさまざまな動的なパラメタに基づくため
- アクセスしたときの役割,直前の振る舞い,システムの状態,システムを利用している別のユーザの振る舞いを同時に考慮
- 認可ルールはさまざまな動的なパラメタに基づくため
- 認可とアクセス制御を提供するtechnologyや権限管理システムには,利用できるものが多くある
- 認可制御のために利用するシステムを分離し,自前の認可制御レイヤで包み込むのが〇
- 静的な側面
- 認証,身元を証明する
- 主体が誰で,誰であると主張しているかsystemが保証する
- 認可に先駆けて行う処理として重要,信頼されたtransactionに参加するだけであっても必要
- 第三者機関による同一性の証明が必要
- 非公開システム
- 第三者機関による同一性の証明の必要はほとんどない
- systemの持つ構造や操作だけで,妥当な水準の認証機能を提供する
- 認証の要素
- userだけが知っている情報
- userだけが持っている情報
- user自身の情報
- いずれか2つを組み合わせると強力な認証になる
- きわめてセキュアな環境では3つすべてから,複合的に応用が必要
- 認証方式は抽象化が〇
- 公開システム
- hybrid_system
- e.g. email
- 認可,誰に何ができるのか定義
- transaction_security
- client-server application, Web serviceにおいて最重要
- 目的: 監査可能性,一貫性,機密性,説明性の成立
- 監査可能性,行為の証明
- 一貫性,情報の改ざん,改変を防ぐ
- 機密性,権限のないものから情報を隔離する
- 説明性,人々に自分の取った行動の責任を負ってもらう
- software_security
- technique
- serial_number, activation
- 検証コードの保護
- 単に真偽値を返すだけのコードでlicenseをチェックしてはいけない
- applicationが動作するために必要な何らかの情報を暗号化して,署名されたlicenseに埋め込んでおく方法が〇
- 初期化routineやapplicationにsub_componentを登録する機能と同じくらい,きわめて重要な機能
- 高度なlicense_managerの実装の難しさ → Ch04
- hardware_binding
- machine_binding
- 低コストで〇
- machineのupgrade困難で×
- crackerが最もcrackしやすいことも×
- hardware_binding
- softwareをserial_portやUSB_portに接続した専用の物理デバイスと紐づける
- 一般にドングル度呼ばれる
- portability, security強度の高さで〇
- 金銭的・管理コストで×
- softwareをserial_portやUSB_portに接続した専用の物理デバイスと紐づける
- machine_binding
- software_securityのコストと恩恵
- 正当なuserに面倒な思いはさせないようにする
- 対策を実装するリスクと,潜在的な利益損失を比較する
- crackerのWeb siteやUsenetのsoftware一覧にある場合は,強力な防護策を検討する
- technique
- information_security
- algorithmを秘匿するか,鍵を秘匿するか
- 秘匿されたalgorithmは×
- 知られていないことによって成り立つsecurityは最弱
- 公開されて利用可能な優れたalgorithmのどれか1つを採用するのが〇
- 標準的なalgorithmと秘密鍵が〇
- 秘匿されたalgorithmは×
- backdoor
- 窓が増えるほど安全の保証は困難
- systemの重要部分は100%の安全性の確保が必要
- backdoorを作るくらいなら,パスワードと鍵を管理する別会社を紹介する方が良い
- column: client supportはbackdoorを使ってclientを盗み見してはならない
- backdoorは断固反対
- securityとmarketecture
- securityは特定の分野で目に見える競合優位性
- 影響のある領域
- 認証,business_model,運用
- 強力な二段階認証が重大な影響 → business_model, 運用モデル
- userがid, passwordを共有を防ぐ
- clientからの信頼
- 強力な二段階認証が重大な影響 → business_model, 運用モデル
- 規制の影響
- FIPSなど
- 業界の発展
- internetの標準規格に従うように対策 → 受け入れられやすくなる
- 信頼
- securityの知識や仕組みを根拠に信頼 → 競合優位性
- 論争の解決
- 一貫性や説明性のテクニックがあれば,そのときどきの最適なやり方で論争を解決できる
- security technologyで問題を回避できる分野について,法務部門に見極めてもらう
- column: あなたの主張を証明しなければならないのはいつか
- log fileが法的な根拠となり得るように確認していたことで〇のケース
- 認証,business_model,運用
- まとめ
- applicationの全体的なsecurity計画においては,適切なrisk要因を考慮する必要
- security 4種類
- identity_control
- transaction_security
- software_security
- information_security
- identity_controlの主張なtool, technique
- 認可制御
- 誰が何ができるか設定
- 認証制御
- 身元の証明
- 公開/非公開システムによって仕組みが大きく異なる
- 認可制御
- transaction_securityの主要なtool, technique
- 監査可能性
- 一貫性
- 機密性
- 説明性
- software_securityの主要なtool, technique
- 著作権侵害行為を防ぎ,license条項の遵守を促す
- softwareを,それが実行される機会やhardware tokenと結び付ける
- information_securityの主張なtool, techniqueは,transaction_securityに用いられるものと同様であるが,少し効果は落ちる
- 主なtechniqueは,情報が格納される環境の安全性を確保すること
- 自分たちでsecurity algorithmを開発するべきではない
- 適切な鍵を用意して公開されているalgorithmを使う
- backdoorは×
- securityが自分たちの長所となるようにする
- 第一級のブランド要素は信頼,securityはそれを支える
Appendix A release checklist
- product追跡用の情報
- engineering, development
- version文字列は最終バージョン情報で更新されているか
- debug codeやtest codeはsoftwareから除去したか
- 埋め込まれた欠陥は全て取り除かれたか
- 品質保証
- すべての欠陥について最終的な処置や解決は住んでいるか
- 最終ビルドに対する適切なテスト(完全な回帰テスト,clientによる確認テスト,スモークテストなど)が完了しているか
- release mediaによるまっさらな対象マシンへのprogram installは完了しているか
- program(file, registryなど)は正しくinstallできるか
- target machineからprogramはuninstallできるか
- upgradeのinstallは完了しているか
- 最終版のhelp fileが含まれているか
- 最終版のreadme fileが含まれているか
- 移行scriptはすべて完了しているか
- support対象のplatformはすべてテストし,検証されているか
- 技術出版物
- release_note
- product内のinline help(readme, release_note, quick start)
- tarchitectによってreview済みの最新のtraining教材
- product_managementの必須事項
- press release
- 既存clientへのemail campaign
- sales training
- price
- 公開plan
- 販促物(white paper, 用語集, Web site)
- 知識の移転-専門service
- clientはsystemのinstall, upgrade, integrationができるか
- systemのinstall, upgrade, integrationにどのくらい時間がかかるか
- installしたものを機能させるために,何らかの統合をやり直す必要があるか
- training素材はreleaseと合わせて最新化されているか
- 知識の移転-販売と販路
- 販売チームは既存clientに新規releaseのmeritを説明できるか
- 販売チームは新規clientに新規productのmeritを説明できるか
- 以前のreleaseからの変更を含むbusiness_modelを完全に理解できているか(価格の変更や販促用の値引きなど)
- 企業に提供されるsolution全体というcontextの中でproductについて説明できるか
- 知識の移転-technical support
- technical supportはproductをsupportする準備ができているか
- release活動
- releaseされたproductに含まれる最終的なファイルの一覧は入手できるか
- release fileのtimestampは同期しているか
- 最終頒布media(golden master)は入手できるか
- 頒布用mediaのvirus scanは完了しているか
- 頒布用mediaに含まれるfileの最終確認は完了しているか
- build environmentのbackupは変更管理課に置かれているか
- Web siteは最新化されているか
Appendix B 戦略的product_managementのためのpattern_language
- marketect, tarchitectにとってそれぞれの専門分野の間にまたがるgapを埋めるのに役立つ
- patternを適用する
- market_mapの構築 → 統合期 → feature, meritの計画 → tarchitectureの進化の管理
- market_mapから: 狙っているtarget_marketについて,product_development_teamの中でもかなり混乱があるため
- 1つのpatternを選んで初めて見ることが重要
- patternは体系の一部でしかない
- feedback loopの中で,1つのmarket_segmentを見定めることから始める
- そのうえでmarket eventやfeatureを追加
- 結果を表現して共有する
- 誰にでもアクセスできる場所に専用のスペースを設けて,大きなchartを一式掲示が〇
- market_map
- context
- 新しいproductのideaについて,潜在的な価値を理解しようとしている
- 既存のproductのmarketingが効率よく行われることを確かめたい
- 問題
- marketをどのようにsegment分割するか
- force
- market_segment分割は難しい
- 既存のmarket歯変化する
- 台糖市場の予測は,科学的な取り組みであるのと同じくらいセンスが問われる
- market_segment分割は極めて重要
- まるごと相手にしては必ず失敗するだろう
- market_segmentが異なればsolutionも異なる
- うまくsegment分割できなければ,最も収益性の高いsegmentを見定めることもできない
- marketのすべてのニーズを満たすことはできない
- usabilityを考えるには,対象のmarketのことの理解が必要
- market_segment分割は難しい
- solution
- 同じ特徴や属性を共有するuserを分類したりgroupingしたりして,market_segmentを分割
- 特徴
- 決定的なニーズや購買パターン,そのほか重要な何らかの属性
- 消費者市場の属性
- 年齢,世帯収入,internet接続できるか,技術リテラシーなど
- ビジネス市場の属性
- 収益,従業員数,事業所の所在地
- 初めは,現在のproductを実際に使っているuserに集中
- userが直面する問題を見定める
- できるだけうまくsegmentを定義
- 妥当な数のsegmentになったら,featureを並び替え
- market_mapはteam_member全員に渡す
- 結果として現れるcontext
- 次のproduct cycleで取り組むsegmentをより精密に分析できるようになるかもしれない
- 関連するpattern
- market event/market rhythm
- feature/merit map
- context
- market event/market rhythm
- context
- 特定のreleaseや今後予定されている一連のrelease_cycleをmarketに投入する次期を見定める
- market_segmentの調査に役立つmarket_mapが既存
- 問題
- 適切なrelease日を決めるにはどうすればよいか,今後のrelease_cycleを確立するにはどうすればよいか
- force
- たいていのclientはいつでもreleaseを受け入れてくれるわけではない
- clientはreleaseがあまりに頻繁なことに耐えられないが,遅すぎても心配する
- developerはrelease_dateが未定なことを嫌う
- 販促活動には十分なリードタイムが必要
- releaseまでの期間が短すぎるとほぼ間違いなく失敗する
- releaseまでの期間が長すぎても失敗する
- 長期間productをreleaseしていないと,組織から貴重なスキルが喪失
- 長期間productをreleaseしない組織で働いていても楽しくない
- solution
- どんなドメインにもマーケットを動かす原動力となるようなイベントとリズムがある
- 重要なevent
- conference
- 競合他社のpress release
- productが関連するdomainに焦点を合わせた特集記事や,product/serviceに関連する特集記事
- その他のrhythm: 歳末,新学期,夏季休暇など
- 今後のreleaseをmarketに投入する時期やrhythmを決定する
- 9~12か月の間でが定期的なrelease_cycle
- market_segmentの成熟度もrelease_cycleに影響する
- 結果として現れるcontext
- developerは根拠を理解できる
- release_dateの共有はdevelopment_team全体を活気づけ結びつける
- 戦略レベルの計画の実現性を保証でき,clientが満足
- 関連するpattern
- market_map
- context
- feature/merit map
- context
- 重要なmarketing目標とdevelopmentで力を入れている部分の整合性の確認
- 重要なmarket_segmentを見定めるためのmarket_mapがある
- 問題
- それぞれのmarket_segmentにとって魅力的なfeature, meritを表現するにはどうすればよいか
- force
- 特定のmarket_segmentにおける重要なfeatureとmeritを理解できていると誤認しがち
- 1つのfeatureが複数のmarket_segmentにとってのmeritになることもある
- 複数のmarket_segmentに訴求するfeatureがあっても,それぞれのmarket_segmentにとってのmeritは同じではない
- developerはfeatureのことだけを考える傾向がある.marketing担当者はmeritのことだけを考える傾向がある
- 相違をそのままにしておくと,marketで成功するはずもない粗末なproductになる
- developerはfeatureのもたらす将来的なmeritについて,その性質や意図の質問が必要
- tarchitectureの設計がそれらに見合ったものかどうか確かめるため
- solution
- 各market_segmentについて,productの購入を望む重要なfeature, meritを表現する
- feature/meritは並べて書くことが重要
- 省略は×
- mapの並び順を整える
- releaseまでの所要時間,featureの難易度や複雑さ,marketの要望の順など
- treeでのfeatureの描画
- productの成長の実感
- product_development_teamの規模の拡大と関連
- 結果として現れるcontext
- target market_segmentの攻略のために必要な,主要なfeature/meritを表現
- tarchitecture_loadmapを最新化するために必要な情報が技術担当チームに提供される
- 関連するpattern
- market_map
- tarchitecture_loadmap
- context
- tarchitecture_loadmap
- context
- 今後も含めて複数回のreleaseを期待されたapplicationを開発している
- market_segmentを見定めるためのmarket_mapと,market_segmentが求めているfeatureを見つけるためのfeature/merit mapがある
- 複数のmarketに対応できるarchitectureがあるかもしれない
- 問題
- 技術的な変化をどのように管理し,どのように活用するか
- force
- applicationのarchitectureがどれほどうまく構成されていても,technologyが変化することで以前の目論見が無意味になる
- たとえ計画していたとしても,十分な時間をかけて順応しなければtechnologyの全貌を捉えられない
- developerは目標を知りたがる
- developerは新しいことを勉強するのが大好き
- developerは安易に実装されたfeatureやcapabilityをtarchitecture的に進化させる方法を追及している
- みすぼらしいfeature/capabilityを明らかにし,改修案とともに記録する方法を求めている
- marketing部門が望んでいる新しいfeatureは,technologyが変化することで実現できるようになるかもしれない
- marketing部門が望むfeatureは新しいtechnologyでしか実現できないかもしれない
- 競合他社が新しいtechnologyを採用すると,自分たちは不利な立場になるかもしれない
- 技術脳の人間は核心的なtechnologyにあれこれ反論する
- 時には議論を通じて課題を学ぶことになる
- 合意のための唯一の方法は,議論に十分な時間を割くこと
- solution
- architectureの進化の歩みを示すtechnology loadmapを作る
- 主要なmarket_segmentにmeritをもたらすtechnologyを示すことで,technology loadmapとmarket_map, feature/merit mapを関連付け
- 重要なmilestoneを達成したときや,何も起きなくても少なくとも6か月ごとに,technology loadmapを見直す
- 内部的なmilestone
- code freeze,productの出荷,既存顧客の半分以上が最新versionへupgradeしたことなど
- 外部的なmilestone
- 競合他社による新しいproductのrelease発表,新しい特許の公開,技術スタッフが次世代のtechnologyを発見する,重要なmarket eventが生じる
- 内部的なmilestone
- technology loadmapを作成して維持していくには,少なくともチームの中の1人が外部の環境で新しい開発方法を実践する必要がある
- needsを満たす新しいtechnologyが登場していないか定期的に確認できる → チームに集中力
- 結果として現れるcontext
- 将来productに搭載されるかもしれないfeatureを見通せるようになる
- teamの規律があいまいだと,新技術渇望症候群(SNOS: Shiny New Object Syndrome)にかかってしまう
- 関連するpattern
- market_map
- feature/merit map
- context
訳者あとがき
- Web baseのserviceの進化に伴い,licenseやinstall, upgradeに対する考え方は現在にそのまま当てはまらない
[学習記録]Ruby on RailsでのWebアプリケーション作成
勤務表作成アプリケーション
- フレームワーク: Ruby on Rails
- デプロイ先: Heroku
- 拡張機能
- Mailgun
- 拡張機能
学習内容
- Web application作成
- ユーザ認証機能の実装
- CRUD
- validation
- session
- remember digest
- friendly forwarding
- activation
- password reset
- HerokuでのSSL
- Herokuでのメール送信
- Mailgun
- 勤務表機能の実装
- ユーザモデルとの関係づけ
- berofe_actionでのアクセス制御
- ルート設定
- 複数データの一括CRUD
- Formモデルの作成
- parameterの取り出し
- validation
- クエリ
- 環境ごとのクエリの使い分け
- 画面作成
- form_withでの引数を伴った画面遷移
- Bootstrapの使用
- 登録データに描画内容を対応させる
- ユーザと描画内容を対応させる
- 休祝日の対応
- ヘルパーメソッドの使用
- ユーザ認証機能の実装
機能概要
- ユーザまわりの機能
- Ruby on Rails チュートリアル 第4版の第12章までに基づいて実装
- 学習メモの赤字部分を主に実施
- Ruby on Rails チュートリアル 第4版の第12章までに基づいて実装
勤務表まわりの機能
- 一括登録機能は→ こちら を参考に実装
- 登録済みの場合は登録画面を表示しないようにする
- 作成ボタンを表示しない & 画面以外から入ろうとしてもredirect_toで返す
- 日付は対象の年月から配列を作成して,valueに設定する
- 編集しないように,readonly: true, tabindex: -1を設定する
- 新規登録時は,年月と時間の既定値をフォームで送って設定できるようにする
- 一括更新
- 既存のデータのリンクのみ生成する
- ユーザモデルにクエリを実行するメソッドを追加して,それを呼び出す
- 各月の初日(1日)のデータのみ取得して,そこから年月を取得している
- 本番環境ではpostgresqlでextractを使えるが,ローカルのsqlite3では使えないため,環境によって分岐させている
- 各月の初日(1日)のデータのみ取得して,そこから年月を取得している
- default_scopeでorderを設定し,勤務日について昇順で並ぶようにする
- ユーザモデルにクエリを実行するメソッドを追加して,それを呼び出す
- paramsをhashに変換して,eachで1つずつvalueを取得し,update_attributes
- url, methodはyieldで取得するように変更
- 登録フォームをパーシャルに切り出して,登録・更新・参照で共用
- methodをputにすることでcontrollerのupdateを呼べるようになる
- 既存のデータのリンクのみ生成する
- 一括削除
- 対象のデータを取得して,1件ずつdestroy
- 既存のデータの削除リンクのみ生成する
- Formの基底を用意して,それを継承するcollectionクラスを作成する
- 作成・更新・参照画面では,collection クラスのインスタンスを生成
- form_for
- fields_for
- 作成・更新・参照画面では,collection クラスのインスタンスを生成
- 曜日・祝日の取得
- flashでメッセージを1度だけ表示
実装におけるtips
『Ruby on Rails チュートリアル』学習メモ
Ch01 0からdeployまで
- お手軽すぎるscaffold
- 自動生成されたcodeは無駄に量が多く複雑 → 初学者には向かない
- rails 5.1.6 new hello_app ← バージョン指定
- direcroty 構成 → 表1.2
- Gemfileのバージョン指定
- ~>では,メジャーバージョンのアップデートはしない
- list 1.5でGemfileの内容
- root 'controller_name#action_name'
- Git
- git config --global user.name "自分の名前"
- git config --global user.email your.email@example.com
- git config --global alias.co checkout
- git config --global credential.helper "cache --timeout=86400"
- git init
- git add -A
- .gitignoreで指定していないすべてのファイルをバージョン管理に追加
- git commit -m "Initialize repository"
- git log
- git checkout -f
- 以前のcommitに復元できる
- git remote add origin https://github.com/<あなたのGitHubアカウント名>/hello_app.git
- git push -u origin master
- git checkout -b modify-README
- git branch
- github.com/settings/repositoriesをブラウザで開いて「Repository default branch」をmainからmasterに変更
- 編集後に,git statusで確認
- git commit -a -m "Improve the README file"
- -a: 変更を一括でcommit
- 慎重に使う
- 新しいファイルはaddが必要
- -a: 変更を一括でcommit
- commit message: 現在形かつ命令形
- git checkout master
- git merge modify-README
- git branch -d modify-README
- git branch -D
- topic branch上の変更を破棄
- 変更をマージしていなくても削除できる
- git push
- deploy
- Herokuのセットアップ
- PostgreSQL
- group :production do gem 'pg', '0.20.0' end
- bundle install --without production
- pg gemを追加したことをGemfile.lockに反映させないと、本番環境へのデプロイで失敗してしまう
- git commit -a -m "Update Gemfile for Heroku"
- curl https://cli-assets.heroku.com/install.sh | sh
- heroku --version
- heroku login --interactive
- heroku keys:add
- heroku create
- git push heroku master
- heroku open
- heroku rename rails-tutorial-hello
- heroku logs
- Herokuのセットアップ
- まとめ
- Ruby on Rails: Web developmentのためのframework, Rubyによる
- rails new, rails server
- controller_actionの追加やroot routingの変更だけで,hello, world applicationを作成できる
- Herokuへのdeploy
Ch02 Toy application
- application plan
- 下準備: Ch01と同じところまで
- 最初に,applicationの構造を表すためのdata_modelを作成する
- userのdata_model design
- minimum 表現
- id, name, email
- minimum 表現
- micro_postのdata_model design
- id, content, user_id(associate)
- Users resource
- resource: data_modelとWeb_interfaceの組み合わさり
- CRUDできるobjectとみなせるようになる
- rails generate scaffold User name:string email:string
- rails db:migrate
- かつてはRake
- ユーザーページを探検する
- /users: index
- /users/1: show
- /users/new: new
- /users/1/edit: edit
- MVCの挙動
- config/routes.rb
- URL, actionの組み合わせを効率よく設定
- resources :users
- :usersはRubyのsymbol
- app/controllers/users_controller.rb
- 各ページは、Usersコントローラ内のアクションにそれぞれ対応
- ページの数よりもアクションの数の方が多い
- create、update、destroyアクションがあります。通常、これらのアクションは、ページを出力せずにデータベース上のユーザー情報を操作
- URLには重複しているものがある
- これらのアクション同士の違いは、それらのアクションに対応するHTTP requestメソッドの違い
- REST
- RailsアプリケーションにおけるRESTとは、アプリケーションを構成するコンポーネント (ユーザーやマイクロポストなど) を「リソース」としてモデル化することを指す
- これらのリソースは、リレーショナルデータベースの作成/取得/更新/削除 (Create/Read/Update/Delete: CRUD) 操作と、4つの基本的なHTTP requestメソッド (POST/GET/PATCH/DELETE) の両方に対応
- RESTfulなスタイルを採用することで、作成すべきコントローラやアクションの決定が楽になる。作成(C)・取得(R)・更新(U)・削除(D)を行うリソースだけでアプリケーション全体を構成してしまうことも可能
- RailsアプリケーションにおけるRESTとは、アプリケーションを構成するコンポーネント (ユーザーやマイクロポストなど) を「リソース」としてモデル化することを指す
- @記号で始まる変数をRubyではインスタンス変数と呼び、Railsのコントローラ内で宣言したインスタンス変数はビューでも使えるようになる
- app/models/user.rb
- ビューはその内容をHTMLに変換し (⑦)、コントローラがブラウザにHTMLを送信して、ブラウザでHTMLが表示
- config/routes.rb
- Usersリソースの欠点
- データの検証が行われていない
- ユーザー認証が行われていない
- テストが書かれていない
- レイアウトやスタイルが整っていない
- 理解が困難
- resource: data_modelとWeb_interfaceの組み合わさり
- Micropostsリソース
- マイクロポストを探検する
- rails generate scaffold Micropost content:text user_id:integer
- マイクロポストをマイクロにする
- app/models/micropost.rb
- validates :content, length: { maximum: 140 }
- app/models/micropost.rb
- ユーザーはたくさんマイクロポストを持っている
- 異なるデータモデル同士の関連付けは、Railsの強力な機能
- app/models/user.rb
- has_many :microposts
- app/models/micropost.rb
- belongs_to :user
- 演習
- <%= @user.microposts.first.content %>
- app/models/micropost.rb
- presence: true
- app/models/user.rb
- validates :name, presence: true
- 継承の階層
- ActiveRecord::Baseという基本クラスを継承したことによって、作成したモデルオブジェクトはデータベースにアクセスできるようになり、データベースのカラムをあたかもRubyの属性のように扱えるようになる
- ActionController::Base
- アプリケーションをデプロイする
- マイクロポストを探検する
- 最後に
- 良い
- 弱点
- レイアウトもスタイルも設定されていない
- “Home” や “About” のような定番の静的なページがない
- ユーザーがパスワードを設定できない
- ユーザーが画像を置けない
- ログインの仕組みがない
- セキュリティのための仕組みがまったくない
- ユーザーとマイクロポストの自動関連付けが行われていない
- Twitterのような「フォロワー (following)機能」や「フォロー中 (followed)機能」がない
- マイクロポストをフィードできない
- まともなテストがない
- 理解が困難
- 本章のまとめ
- Scaffold機能でコードを自動生成すると、Webのあらゆる部分からモデルデータにアクセスしてやりとりできるようになる
- Scaffoldは何よりも手っ取り早いのがとりえだが、これを元にRailsを理解するには向いていない
- RailsではWebアプリケーションの構成にMVC (Model-View-Controller) というモデルを採用している
- Railsが解釈するRESTには、標準的なURLセットと、データモデルとやりとりするためのコントローラアクションが含まれている
- Railsではデータのバリデーション (validation) がサポートされており、データモデルの属性の値に制限をかけることができる
- Railsには、さまざまなデータモデル同士を関連付けを定義するための組み込み関数が多数用意されている
- Railsコンソールを使うと、コマンドラインからRailsアプリケーションとやりとりすることができる
Ch03 ほぼ静的なページの作成
- 静的なページを自分の手で作成することは良い経験になり、多くの示唆も得られる
- セットアップ
- rails 5.1.6 new sample_app
- Gemfile
- git
- README
- helloアクションをApplicationコントローラーに追加
- ルートルーティングを設定
- Herokuにプッシュ
- RailsのデフォルトのページはHeroku上ではうまく表示されない仕様になっています。このため、hello worldのような変更を加えないとデプロイが成功したかどうかが分かりづらい、というのが本当の理由
- heroku logsでerrorの確認
- 静的ページ
- コントローラとは (基本的に動的な) Webページの集合を束ねるコンテナのこと
- git checkout -b static-pages
- 静的なページの生成
- rails generate controller StaticPages home help
- git push -u origin static-pages
- 元に戻す方法
- GETやその他のHTTPメソッドについて
- StaticPagesコントローラは一般的なRESTアクションに対応していないことに注意してください。これは、静的なページの集合に対しては、適切なアクション
- ApplicationControllerクラスを継承しているため、StaticPagesControllerのメソッドは (たとえ何も書かれていなくても) Rails特有の振る舞い
- /static_pages/homeというURLにアクセスすると、RailsはStaticPagesコントローラを参照し、homeアクションに記述されているコードを実行
- 今回の場合、homeアクションが空になっているので、/static_pages/homeにアクセスしても、単に対応するビューが出力されるだけ
- /static_pages/homeというURLにアクセスすると、RailsはStaticPagesコントローラを参照し、homeアクションに記述されているコードを実行
- 静的なページの調整
- テストから始める
- 何らかの変更を行う際には、常に「自動化テスト」を作成して、機能が正しく実装されたことを確認する習慣をぜひ身に付けましょう
- 結局テストはいつ行えばよいのか
- テストを行う目的
- テストの手法やタイミングは、ある意味テストをどのぐらいすらすら書けるかで決まると言ってよい
- 「テスト駆動」にするか「一括テスト」にするかを決める目安となるガイドライン
- アプリケーションのコードよりも明らかにテストコードの方が短くシンプルになる (=簡単に書ける) のであれば、「先に」書く
- 動作の仕様がまだ固まりきっていない場合、アプリケーションのコードを先に書き、期待する動作を「後で」書く
- セキュリティが重要な課題またはセキュリティ周りのエラーが発生した場合、テストを「先に」書く
- バグを見つけたら、そのバグを再現するテストを「先に」書き、回帰バグを防ぐ体制を整えてから修正に取りかかる
- すぐにまた変更しそうなコード (HTML構造の細部など) に対するテストは「後で」書く
- リファクタリングするときは「先に」テストを書く
- 特に、エラーを起こしそうなコードや止まってしまいそうなコードを集中的にテスト
- 不安定な要素が特に見当たらないアプリケーションや、(主にビューが) 頻繁に改定される可能性の高いアプリケーションのコードを書くときには、思い切ってテストを省略してしまうこともある
- 本書における主要なテストは、コントローラテスト (この節より)、モデルテスト (第6章より)、統合テスト (第7章より) の3つ
- 統合テストでは、ユーザーがWebブラウザでアプリケーションとやりとりする操作をシミュレートできるので特に強力
- まずは取っ付きやすいコントローラテストから
- 最初のテスト
- Red
- Green
- Refactor
- 少しだけ動的なページ
- ページの内容に応じて、ページのタイトルを自ら書き換えて表示
- タイトルをテストする (Red)
- 典型的なWebページは、リスト 3.23のようなHTMLの構造
- 1) document type (doctype)は使用するHTMLのバージョン (ここではHTML5) をブラウザに対して宣言
- 2) headセクション。ここではtitleタグに囲まれた「Greeting」(=あいさつ) という文字
- 3) bodyセクション。ここには「Hello, world!」という文字列
- セレクタ
- assert_select "title", "Home | Ruby on Rails Tutorial Sample App"
- 典型的なWebページは、リスト 3.23のようなHTMLの構造
- タイトルを追加する (Green)
- レイアウトと埋め込みRuby (Refactor)
- 重複を取り除くテクニックの1つとして、ビューで「埋め込みRuby」(Embedded Ruby) が使えます
- Railsのprovideメソッドを使ってタイトルをページごとに変更
- ERBと呼ばれている、Rubyの埋め込みコード
- ERBはWebページに動的な要素を加えるときに使うテンプレートシステム
- <% provide(:title, "Home") %>
- <% ... %>という記法が使われており、その中からRailsのprovideメソッドを呼び出し
- "Home"という文字列と:titleというラベルを関連付け
- <% ... %>と書くと、中に書かれたコードを単に実行するだけで何も出力しません。<%= ... %>のように等号を追加すると、中のコードの実行結果がテンプレートのその部分に挿入
- HTMLの重複した構造をDRYにする
- Railsにはそのためにapplication.html.erbという名前のレイアウトファイルがあります
- <%= yield %>
- レイアウトを使う際に、/static_pages/homeにアクセスするとhome.html.erbの内容がHTMLに変換され、<%= yield %>の位置に挿入
- スタイルシートとJavaScriptは、Asset Pipeline (5.2.1) の一部
- csrf_meta_tagsは、Web攻撃手法の1つであるクロスサイトリクエストフォージェリー (Cross-Site Request Forgery: CSRF)を防ぐために使われるRailsのメソッド
- レイアウトと重複するHTMLがまだ残っているので、それらを削除して、内部のコンテンツだけ残します
- 演習
- まずはテストを追加
- urlを変えるのも必要
- routesを追加
- controller_actionを追加
- erbを追加
- まずはテストを追加
- ルーティングの設定
- 演習
- root_url
- 演習
- 最後に
- 本章のまとめ
- 新しいRailsアプリケーションをゼロから作成したのはこれで3度目。今回も必要なgemのインストール、リモートリポジトリへのプッシュ、production環境まで行った
- コントローラを新規作成するためのrailsコマンドはrails generate controller ControllerName アクション名 (省略可)
- 新しいルーティングはconfig/routes.rbファイルで定義
- Railsのビューでは、静的HTMLの他にERB (埋め込みRuby: Embedded RuBy) が使える
- 常に自動化テストを使って新機能開発を進めることで、自信を持ってリファクタリングできるようになり、回帰バグも素早くキャッチ
- テスト駆動開発では「red ・ green ・REFACTOR」サイクルを繰り返す
- Railsのレイアウトでは、アプリケーションのページの共通部分をテンプレートに置くことでコードの重複を解決
- 本章のまとめ
- 高度なセットアップ
- minitest reporters
- minitest-reporters gemを利用
- test/test_helper.rb
- require "minitest/reporters"
- Minitest::Reporters.use!
- test/test_helper.rb
- minitest-reporters gemを利用
- Guardによるテストの自動化
- テストをしようとする度にエディタからコマンドラインに移動して、手動でコマンドを打ち込み、実行しなければならないという点が面倒
- Guardは、ファイルシステムの変更を監視
- 具体的には、「home.html.erbファイルが変更されたらstatic_pages_controller_test.rbを自動的に実行する」
- bundle exec guard init
- guard :minitest, spring: "bin/rails test", all_on_start: false do
- GuardからSpringサーバーを使って読み込み時間を短縮しています (SpringはRailsの機能の1つです)
- 開始時にテストスイートをフルで実行しないようGuardに指示
- .gitignoreファイルにspring/ディレクトリを追加
- Springのプロセスが起動したまま多数残留すると、テストのパフォーマンスが低下
- bundle exec guard
- テストを変更ファイルだけではなく、フルで実行したい場合は、guard>プロンプトでReturnキーを押します
- (このとき、Springサーバーに接続できないなどのエラーが表示されることがあります。問題を修正するには、もう一度Returnキーを押します)
- Guardを終了するにはCtrl-Dキー
- minitest reporters
Ch04 Rails風味のRuby
- Railsにおいて重要となるRubyのさまざまな要素について探っていく
- Rails開発者にとって必要な知識は比較的少なくて済みます
- この章の目的は、「Rails風味のRuby」というものについての確固たる基盤を、皆さんのこれまでの言語経験に関わらず提供すること
- 動機
- 初歩的な作業をいつまでも続けるわけにはいきません
- Rubyに関する知識と経験の限界に真正面から挑み、これを乗り越えるためにこの章を割り当てる
- 組み込みヘルパー
- カスタムヘルパー
- app/helpers/application_helper.rb
- 文字列とメソッド
- Railsコンソールはirb (IRB: Interactive RuBy) を拡張して作られているため、Rubyの機能をすべて使うことができます
- rails console
- Railsコンソールは素晴しい学習ツールであり、その中を自由に探索できます
- コンソールの中で何をしようとも、何かを壊すことは (まず) ありえないので、ご安心ください
- Railsコンソールが何かおかしな挙動になったら、Ctrl-Cを押してコンソールから強制的に抜け出すことができます。もしくは、Ctrl-Dを押して正常にコンソールを終了させることもできます
- Rubyリファレンスマニュアル (通称: るりま) やるりまサーチが大変便利
- コメント
- Rubyのコメントはナンバー記号# (「ハッシュマーク」や「オクトソープ」とも呼ばれます) から始まり、その行の終わりまでがコメント
- 文字列
- 文字列 (string) は、Webアプリケーションにおいておそらく最も重要なデータ構造
- Webページというものが究極的にはサーバーからブラウザに送信された文字列にすぎないため
- 文字列リテラルと呼ばれ (面白いことにリテラル文字列とも呼ばれます)ダブルクォート " で囲むことで作成
- 演算子を使って、文字列を結合
- 文字列を組み立てる他の方法として式展開 (interpolation) というものがあり、#{}という特殊な構文を使います
- 出力
- puts (putの三人称単数現在形ではなく「put string」の略なので、本来は「put ess」と発音しますが、最近は「puts」と発音されることも多くなってきました)
- putsを使って出力すると、改行文字である\nが自動的に出力の末尾に追加されます (これはターミナルのechoコマンドと同じ振る舞いになります)
- printメソッドも同様の出力を行いますが、次のように、改行文字を追加しない点が異なります
- シングルクォート内の文字列
- 演習
- tab: \t
- 文字列 (string) は、Webアプリケーションにおいておそらく最も重要なデータ構造
- オブジェクトとメッセージ受け渡し
- オブジェクトとは (いついかなる場合にも) メッセージに応答するもの
- オブジェクトに渡されるメッセージは、一般にはメソッドと呼ばれます。メソッドの実体は、そのオブジェクト内で定義されたメソッド
- メソッドがtrueまたはfalseという論理値 (boolean) を返すことを、末尾の疑問符で示す慣習
- 論理値は、特に処理の流れを変更するときに有用
- Rubyでは、あらゆるものがオブジェクトです。したがって、nilもオブジェクト
- puts "x is not empty" if !x.empty?
- Rubyではこのように、後続するifでの条件式が真のときにだけ実行される式 (後続if) を書くことができ、コードが非常に簡潔になります。なお、unlessキーワードも同様に使えます
- Rubyにおいてnilは特別なオブジェクト
- オブジェクトとは (いついかなる場合にも) メッセージに応答するもの
- メソッドの定義
- メソッドの引数を省略することも可能です (カッコですら省略可能です)
- 引数にデフォルト値を含めているから
- Rubyのメソッドには「暗黙の戻り値がある」ことにご注意ください。これは、メソッド内で最後に評価された式の値が自動的に返される
- メソッドの引数を省略することも可能です (カッコですら省略可能です)
- titleヘルパー、再び
- module ApplicationHelperという要素について
- モジュールは、関連したメソッドをまとめる方法の1つで、includeメソッドを使ってモジュールを読み込むことができます (ミックスイン (mixed in) とも呼びます)
- Railsでは自動的にヘルパーモジュールを読み込んでくれるので、include行をわざわざ書く必要がありません
- module ApplicationHelperという要素について
- ほかのdata_structure
- 配列と範囲演算子
- "fooxbarxbaz".split('x')
- Rubyでは、角カッコ以外にも配列の要素にアクセスする方法が提供されています
- first, second, last
- a.last == a[-1]
- true
- 配列の内容を変更したい場合は、そのメソッドに対応する「破壊的」メソッドを使います。破壊的メソッドの名前には、元のメソッドの末尾に「!」を追加したものを使うのがRubyの慣習です
- pushメソッド (または同等の<<演算子) を使って配列に要素を追加することもできます
- 他の多くの言語の配列と異なり、Rubyでは異なる型が配列の中で共存できます
- 文字列を配列に変換するのにsplitを使いました。joinメソッドはこれと逆の動作
- a.join(', ')
- 範囲 (range) は、配列と密接に関係しています。to_aメソッドを使って配列に変換すると理解しやすい
- (0..9).to_a
- 範囲は、配列の要素を取り出すのに便利
- a[0..2]
- インデックスに-1という値を指定できるのは極めて便利です。-1を使うと、配列の長さを知らなくても配列の最後の要素を指定することができ、これにより配列を特定の開始位置の要素から最後の要素までを一度に選択することができます
- a[2..-1]
- 文字列に対しても範囲オブジェクトが使えます
- ('a'..'e').to_a
- ブロック
- 配列と範囲はいずれも、ブロックを伴うさまざまなメソッドに対して応答することができます
- (1..5).each { |i| puts 2 * i }
- メソッドに渡されている{ |i| puts 2 * i }が、ブロックと呼ばれる部分
- |i|では変数名が縦棒「|」に囲まれていますが、これはブロック変数に対して使うRubyの構文で、ブロックを操作するときに使う変数を指定
- ブロックであることを示すには波カッコ で囲みますが、次のようにdoとendで囲んで示すこともできます
- 短い1行のブロックには波カッコを使い、長い1行や複数行のブロックにはdo..end記法
- ブロックは見た目に反して奥が深く、ブロックを十分に理解するためには相当なプログラミング経験が必要
- (1..5).each { |i| puts 2 * i }
- mapメソッドなどを使ったブロックの使用例
- 3.times { puts "Betelgeuse!" }
- (1..5).map { |i| i**2 }
- %w[a b c].map { |char| char.upcase }
- mapのブロック内で宣言した引数 (char) に対してメソッドを呼び出しています。こういったケースでは省略記法が一般的で、次のように書くこともできます (この記法を“symbol-to-proc”と呼びます)
- %w[A B C].map(&:downcase)
- 元々Ruby on Rails独自の記法でした。しかし多くの人がこの記法を好むようになったので、今ではRubyのコア機能として導入
- mapメソッドは、渡されたブロックを配列や範囲オブジェクトの各要素に対して適用し、その結果を返します
- 最後のブロックの例として、単体テスト
- ('a'..'z').to_a.shuffle[0..7].join
- 演習
- (0..16).map {|i| i**2}
- str.map {|s| s.upcase}.join
- s.split('').shuffle.join
- 配列と範囲はいずれも、ブロックを伴うさまざまなメソッドに対して応答することができます
- ハッシュとシンボル
- ハッシュは本質的には配列と同じですが、インデックスとして整数値以外のものも使える点が配列と異なります (この理由から、Perlなどのいくつかの言語ではハッシュを連想配列と呼ぶこともあります)
- ハッシュのインデックス (キーと呼ぶのが普通です) は、通常何らかのオブジェクト
- ハッシュの波カッコは、ブロックの波カッコとはまったく別物
- {"last_name"=>"Hartl", "first_name"=>"Michael"}
- ハッシュでは要素の「並び順」が保証されない
- ハッシュの1要素を角カッコを使って定義する代わりに、次のようにキーと値をハッシュロケットと呼ばれる=> によってリテラル表現するほうが簡単
- user = { "first_name" => "Michael", "last_name" => "Hartl" }
- key: Railsでは文字列よりもシンボルを使う方が普通
- シンボルは文字列と似ていますが、クォートで囲む代わりにコロンが前に置かれている
- 余計なことを一切考えずに、シンボルを単なる文字列とみなしても構いません
- 余計なものを削ぎ落した結果、シンボル同士の比較を容易に行えます。文字列は1文字ずつ比較する必要がありますが、シンボルは一度に全体を比較できます。これはハッシュのキーとして理想的な性質です。
- シンボルは、Ruby以外ではごく一部の言語にしか採用されていない特殊なデータ形式
- 文字列と違って、全ての文字が使えるわけではない
- 未定義のハッシュ値は単純にnil
- ハッシュではシンボルをキーとして使うことが一般的なので、Ruby 1.9からこのような特殊な場合のための新しい記法がサポート
- h2 = { name: "Michael Hartl", email: "michael@example.com" }
- :name =>とname:は、ハッシュとしてのデータ構造は全く同じ
- ハッシュの値にはほぼ何でも使うことができ、他のハッシュを使うことすらできます → ネストされたハッシュ
- 配列や範囲オブジェクトと同様、ハッシュもeachメソッドに応答します
- 便利なinspectメソッドを紹介します。これは要求されたオブジェクトを表現する文字列を返します
- オブジェクトを表示するためにinspectを使うことは非常によくあることなので、 pメソッドというショートカットがあります
- p :name # 'puts :name.inspect' と同じ
- 演習
- ハッシュは本質的には配列と同じですが、インデックスとして整数値以外のものも使える点が配列と異なります (この理由から、Perlなどのいくつかの言語ではハッシュを連想配列と呼ぶこともあります)
- CSS、再び
- 配列と範囲演算子
- Rubyにおけるクラス
- 最後に
- 本章のまとめ
- Rubyは文字列を扱うためのメソッドを多数持っている
- Rubyの世界では、すべてがオブジェクトである
- Rubyではdefというキーワードを使ってメソッドを定義する
- Rubyではclassというキーワードを使ってクラスを定義する
- Railsのビューでは、静的HTMLの他にERB (埋め込みRuby: Embedded RuBy) も使える
- Rubyの組み込みクラスには配列、範囲、ハッシュなどがある
- Rubyのブロックは (他の似た機能と比べ) 柔軟な機能で、添え字を使ったデータ構造よりも自然にイテレーションができる
- シンボルとはラベルである。追加的な構造を持たない (代入などができない) 文字列みたいなもの
- Rubyではクラスを継承できる
- Rubyでは組み込みクラスですら内部を見たり修正したりできる
- 本章のまとめ
Ch05 レイアウトを作成する
- この章では、アプリケーションにBootstrapフレームワークを組み込み、そして、カスタムスタイルを追加します
- 構造を追加する
- カスタムCSSルールの他に、Twitter社によるオープンソースのWebデザインフレームワークとして公開しているBootstrapも利用します
- コードそのものにもスタイルを与えます。つまり、散らかり始めたレイアウトのコードを、パーシャル (Partial) 機能を使って整えていくということ
- Webアプリケーションを作成するときに、ユーザーインターフェイスの概要をできるだけ早いうちに把握しておくことがしばしば有用
- ナビゲーション
- RailsはデフォルトでHTML5を使います
- 一部のブラウザ (特に旧式のInternet Explorer) ではHTML5のサポートが不完全である場合があります。そのため、次のようなJavaScriptのコード (通称: HTML5 shim (or shiv))3 を使ってこの問題を回避
- Internet Explorerで特別にサポート
- 一部のブラウザ (特に旧式のInternet Explorer) ではHTML5のサポートが不完全である場合があります。そのため、次のようなJavaScriptのコード (通称: HTML5 shim (or shiv))3 を使ってこの問題を回避
- headerタグは、ページの上部に来るべき要素を表します
- すべてのHTML要素には、クラスとidの両方を指定することができます
- これらは単なるラベルで、CSSでスタイルを指定するときに便利
- クラスとidの主な違いは、クラスはページ内で何度でも使えるのに対し、idは一度しか使えない点
- divタグは一般的な表示領域を表し、要素を別々のパーツに分けるときに使われます。特に古いスタイルのHTMLでは、divタグはサイト内のほぼすべての領域で使われていました
- HTML5からはよく使われる領域ごとに細分化できるようになり、具体的にはheader要素、nav要素、section要素が新たに使えるようになりました
- リンクを生成するために、Railsヘルパーのlink_to
- リストアイテムタグliと順不同リストタグulによって作られた、ナビゲーションリンクのリスト
- navタグには「その内側がナビゲーションリンクである」という意図を明示的に伝える役割
- yieldメソッドはWebサイトのレイアウトにページごとの内容を挿入
- 今後のスタイル要素を利用するために、home.html.erbビューに特別な要素をいくつか追加
- 第7章でサイトにユーザーを追加するときに備えて、最初のlink_toで次のような仮のリンクを生成
- divタグのCSSクラスjumbotronや、signupボタンのbtnクラス、btn-lgクラス、btn-primaryクラスはすべて、Bootstrapにおいて特別な意味
- image_tagヘルパーの能力が示されています。このヘルパーでは、シンボルを使ってalt属性などを設定できます
- BootstrapとカスタムCSS
- 多くのHTML要素にCSSクラスを関連付けました。こうしておくことで、CSSベースでレイアウトを構成する際に高い柔軟性
- これらのクラスの多くは、Twitterが作成したフレームワークであるBootstrap特有のもの
- Bootstrapを使うと、洗練されたWebデザインとユーザーインターフェイス要素を簡単にHTML5アプリケーションに追加することができます
- Bootstrapを使うことでアプリケーションをレスポンシブデザイン (Responsive Design) にできるということです。これにより、どの端末でアプリケーションを閲覧しても、ある程度見栄えをよくすることができます
- bootstrap-sass gemを使ってRailsアプリケーションに導入できます
- カスタムCSSを動かすための最初の一歩は、カスタムCSSファイルを作ること
- Bootstrap CSSを追加する
- app/assets/stylesheets/custom.scss
- @import "bootstrap-sprockets";
- @import "bootstrap";
- app/assets/stylesheets/custom.scss
- すべてのページに適用される共通のスタイルをCSSに追加する
- CSSルールでは一般に、クラス、id、HTMLタグ、またはそれらの組み合わせ、のいずれかを指定します。そしてその後ろにスタイリングコマンドのリストを記述します。
- .center冒頭のドット.は、このルールがクラスに対してスタイルを適用することを示しています
- centerクラスに属している (divなどの) タグの内側にある要素が、すべて中央揃えになることを意味
- 冒頭がポンド記号#で始まる場合は、そのルールがCSSのidに対してスタイルを適用することを示します
- Bootstrapには洗練されたタイポグラフィーを利用できるCSSルールがあります
- テキストの見栄えを変えてみましょう
- いくつかのルールをサイトロゴに追加
- テキストを大文字に変換し、サイズ、色、配置を変更
- 演習
- comment out
- <%#= image_tag("kitten.jpg", alt: "Kitten") %>
- すべての画像を非表示
- img {
- display: none;
- img {
- comment out
- 多くのHTML要素にCSSクラスを関連付けました。こうしておくことで、CSSベースでレイアウトを構成する際に高い柔軟性
- RailsはデフォルトでHTML5を使います
- パーシャル (partial)
- app/views/layouts/application.html.erb
- Sassとアセットパイプライン
- CSS、JavaScript、画像などの静的コンテンツの生産性と管理を大幅に強化する「アセットパイプライン (Asset Pipeline)」
- アセットパイプラインの概要と、素晴らしいCSS生成ツールである「Sass」の使い方について
- アセットパイプライン
- Rails開発者の視点からは、アセットディレクトリ、マニフェストファイル、プリプロセッサエンジンという、3つの主要な機能が理解の対象
- アセットディレクトリ
- マニフェストファイル
- プリプロセッサエンジン
- 本番環境での効率性
- Asset Pipelineの最大のメリットの1つは、本番のアプリケーションで効率的になるように最適化されたアセットも自動的に生成されること
- 具体的には、Asset Pipelineがすべてのスタイルシートを1つのCSSファイル (application.css) にまとめ、すべてのJavaScriptファイルを1つのJSファイル (javascripts.js) にまとめてくれます。さらに、それらのファイルすべてに対して 不要な空白やインデントを取り除く処理を行い、ファイルサイズを最小化してくれます。
- 結果として、開発環境と本番環境という、2つの異なった状況に対してそれぞれ最高の環境を提供
- 素晴らしい構文を備えたスタイルシート
- Sass は、スタイルシートを記述するための言語であり、CSSに比べて多くの点が強化
- SCSSは厳密な意味で、CSS本体を抽象化したフォーマットです。つまり、SCSSはCSSに新しい機能を追加しただけで、全く新しい構文を定義したようなものではない
- Railsのアセットパイプラインは、.scssという拡張子を持つファイルをSassを使って自動的に処理
- ネスト
- Sassは、SCSSをCSSに変換する際に、&:hoverを#logo:hoverに置き換え
- 変数
- $light-gray: #777;
- 定義されている変数はBootstrapページの「LESS変数一覧」で参照
- このWebサイトでは、SassではなくLESSを使って変数が定義されていますが、bootstrap-sassというgemを使えば、Sassでも同様の変数が使えるようになります
- リスト 5.20: ネストや変数を使って初期のSCSSファイルを書き直した結果
- レイアウトのリンク
- 名前付きルート
- サイトリンクのルーティングとURLのマッピング
- ページ名 URL 名前付きルート
- Contactページ
- 第3章の演習のとおり,Contactページについて追加
- RailsのルートURL
- 名前付きルートをサンプルアプリケーションの静的ページで使うために、ルーティング用のファイル (config/routes.rb) を編集
- ルートURLのようなルーティングを定義することの効果
- ブラウザからアクセスしやすくする
- 生のURLではなく、名前付きルートを使ってURLを参照することができる
- root_path: ルートURL以下の文字列
- root_url: 完全なURLの文字列を返します
- Railsチュートリアルでは一般的な規約に従い、基本的にはpath書式を使い、リダイレクトの場合のみurl書式を使う
- HTTPの標準としては、リダイレクトのときに完全なURLが要求されるため
- ほとんどのブラウザでは、どちらの方法でも動作します
- get '/help', to: 'static_pages#help'
- 演習
- 名前付きルートは、as:オプションを使って変更することができます
- get '/help', to: 'static_pages#help', as: 'helf'
- 名前付きルートは、as:オプションを使って変更することができます
- 名前付きルート
- link_toメソッドの2番目の引数で、適切な名前付きルートを使ってみましょう
- リンクのテスト
- 「統合テスト (Integration Test)」を使って一連の作業を自動化
- rails generate integration_test site_layout
- Railsは渡されたファイル名の末尾に _test という文字列を追加する
- assert_selectメソッドの高度なオプション
- 特定のリンクが存在するかどうかを、aタグとhref属性をオプションで指定して調べています
- assert_select "a[href=?]", about_path
- assert_select "a[href=?]", root_path, count: 2
- リンクの個数も調べる
- assert_selectは柔軟でパワフルな機能で、ここでは紹介し切れないほど他にも多くのオプションがあります
- 経験的には、このメソッドで複雑なテストはしない方が賢明
- 今回のようなレイアウト内で頻繁に変更されるHTML要素 (リンクなど) をテストするぐらいに抑えておくとよい
- rails test:integration
- 演習
- test環境でもApplicationヘルパーを使えるようにする
- ユーザー登録: 最初のステップ
- Usersコントローラ
- ユーザー登録用URL
- ユーザー登録URL用にget '/signup'のルートを追加(1つ上の演習と同じ)
- get '/signup', to: 'users#new'
- Usersコントローラのテストで名前付きルートを使うようにする
- ボタンにユーザー登録ページへのリンクを追加する
- 最初のユーザー登録ページ (スタブ)
- 演習
- get signup_path
- assert_select "title", full_title("Sign up")
- ユーザー登録URL用にget '/signup'のルートを追加(1つ上の演習と同じ)
- 最後に
- この章では、アプリケーションのレイアウトを形にし、ルーティングを洗練させました
- 本章のまとめ
- HTML5を使ってheaderやfooter、logoやbodyといったコンテンツのレイアウトを定義
- Railsのパーシャルは効率化のために使われ、別ファイルにマークアップを切り出すことができます
- CSSは、CSSクラスとidを使ってレイアウトやデザインを調整します
- Bootstrapフレームワークを使うと、いい感じのデザインを素早く実装できる
- SassとAsset Pipelineは、(開発効率のために切り分けられた) CSSの冗長な部分を圧縮し、本番環境に最適化した結果を出力する
- Railsのルーティングでは自由にルールを定義することができ、また、その際に名前付きルートも使えるようになる
- 統合テストは、ブラウザによるページ間の遷移を効率的にシミュレートする
Ch06 ユーザーのモデルを作成する
- 一番重要なステップであるユーザー用のデータモデルの作成と、データを保存する手段の確保について
- 第6章から第12章を通して、Railsのログインと認証システムをひととおり開発
- コラム 6.1. 自分で認証システムを作ってみる
- Userモデル
- ユーザー登録でまず初めにやることは、それらの情報を保存するためのデータ構造を作成すること
- Railsでは、データモデルとして扱うデフォルトのデータ構造のことをモデル (Model)
- Railsでは、データを永続化するデフォルトの解決策として、データベースを使ってデータを長期間保存
- データベースとやりとりをするデフォルトのRailsライブラリはActive Record
- Active Recordは、データオブジェクトの作成/保存/検索のためのメソッドを持っています
- これらのメソッドを使うのに、リレーショナルデータベースで使うSQL (Structured Query Language)2 を意識する必要はありません
- Active Recordは、データオブジェクトの作成/保存/検索のためのメソッドを持っています
- Railsにはマイグレーション (Migration) という機能
- Railsは、データベースの細部をほぼ完全に隠蔽し、切り離してくれます
- 実際、本書ではSQLiteを開発 (development) 環境で使い、また、PostgreSQLを (Herokuでの) 本番環境 (production) で使います (1.5) が、本番環境のデータの保存方法の詳細について考える必要はほとんどありません
- データベースの移行
- Railsでユーザーをモデリングするときは、属性を明示的に識別する必要がありません
- nameやemailといったカラム名を今のうちに考えておくことで、後ほどUserオブジェクトの各属性をActiveRecordに伝えるときに楽になります
- rails generate model User name:string email:string
- データベースで使いたい2つの属性をRailsに伝えます。このときに、これらの属性の型情報も一緒に渡します
- generateコマンドの結果のひとつとして、マイグレーションと呼ばれる新しいファイルが生成
- マイグレーションは、データベースの構造をインクリメンタルに変更する手段を提供
- マイグレーション自体は、データベースに与える変更を定義したchangeメソッドの集まり
- リスト 6.2の場合、changeメソッドはcreate_tableというRailsのメソッドを呼び、ユーザーを保存するためのテーブルをデータベースに作成
- create_tableメソッドはブロック変数を1つ持つブロック (4.3.2) を受け取ります
- そのブロックの中でcreate_tableメソッドはtオブジェクトを使って、nameとemailカラムをデータベースに作ります
- モデル名は単数形 (User) ですが、テーブル名は複数形 (users) です
- モデルはひとりのユーザーを表すのに対し、データベースのテーブルは複数のユーザーから構成
- t.timestampsは特別なコマンドで、created_atとupdated_atという2つの「マジックカラム (Magic Columns)」を作成
- rails db:migrate
- 演習
- modelファイル
- class User < ApplicationRecord
- ユーザーオブジェクトを作成する
- rails console --sandbox
- 初期化ハッシュ (hash) を引数に取る
- 同様の方法でオブジェクトを初期化するActive Recordの設計
- Active Recordを理解する上で、「有効性 (Validity)」という概念も重要
- user.valid?
- ただオブジェクトが有効かどうかを確認しただけ
- データベースにUserオブジェクトを保存するためには、userオブジェクトからsaveメソッドを呼び出す必要
- モデルの生成と保存を2つのステップに分けておくと何かと便利
- しかし、Active RecordではUser.createでモデルの生成と保存を同時におこなう方法も提供されています
- User.create(name: "A Nother", email: "another@example.org")
- User.createは、trueかfalseを返す代わりに、ユーザーオブジェクト自身を返すことに注目
- destroyはcreateの逆
- 削除されたオブジェクトは次のようにまだメモリ上に残っています
- しかし、Active RecordではUser.createでモデルの生成と保存を同時におこなう方法も提供されています
- モデルの生成と保存を2つのステップに分けておくと何かと便利
- user.valid?
- ユーザーオブジェクトを検索する
- User.find(1)
- recordなし → findメソッドは例外 (exception) を発生
- 存在しないActive Recordのidによって、findでActiveRecord::RecordNotFoundという例外が発生
- User.find_by(email: "mhartl@example.com")
- find関連メソッドは、ユーザーをサイトにログインさせる方法を学ぶときに役に立ちます
- User.first
- User.all
- 演習
- User.allで生成されるオブジェクトを調べ、ArrayクラスではなくUser::ActiveRecord_Relationクラスであることを確認
- User.allに対してlengthメソッドを呼び出すと、その長さを求められることを確認
- ダックタイピング (duck typing)
- ユーザーオブジェクトを更新する
- user.email = "mhartl@example.net"
- 変更をデータベースに保存するために最後にsaveを実行
- 保存を行わずにreloadを実行すると、データベースの情報を元にオブジェクトを再読み込みするので、変更が取り消されます
- user.update_attributes(name: "The Dude", email: "dude@abides.org")
- 特定の属性のみを更新したい場合は、次のようにupdate_attribute
- user.update_attribute(:name, "El Duderino")
- 検証を回避
- 演習
- created_atも直接更新
- 更新するときは「1.year.ago」を使うと便利です。これはRails流の時間指定の1つで、現在の時刻から1年前の時間を算出してくれます
- created_atも直接更新
- ユーザーを検証する
- Active Record では検証 (Validation) という機能を通して、こういった制約を課すことができる
- 存在性 (presence)の検証、長さ (length)の検証、フォーマット (format)の検証、一意性 (uniqueness)の検証
- 最終検証として確認 (confirmation)
- 有効性を検証する
- 具体的なテスト方法についてですが、まず有効なモデルのオブジェクトを作成し、その属性のうちの1つを有効でない属性に意図的に変更します。そして、バリデーションで失敗するかどうかをテストする、といった方針で進めてい
- test/models/user_test.rb
- setup
- rails test:models
- 存在性を検証する
- @user.name = " "
- assert_not @user.valid?
- name属性の存在を検査する方法は、リスト 6.9に示したとおり、validates メソッドにpresence: trueという引数を与えて使うこと
- validates :name, presence: true
- validatesは単なるメソッド
- presence: trueという引数は、要素が1つのオプションハッシュです。4.3.4のようにメソッドの最後の引数としてハッシュを渡す場合、波カッコを付けなくても問題ありません
- validatesは単なるメソッド
- user.errors.full_messages
- 演習
- u.errors.messagesを実行すると、ハッシュ形式でエラーが取得
- u.errors.messages[:email]
- 長さを検証する
- @user.email = "a" * 244 + "@example.com"
- :maximumパラメータと共に用いられる:lengthは、長さの上限を強制
- validates :name, presence: true, length: { maximum: 50 }
- 演習
- u = User.new(name: "a"51, email: "a"244 + "@example.com")
- :email=>["is too long (maximum is 255 characters)"]
- フォーマットを検証する
- valid_addresses = %w[user@example.com USER@foo.COM A_US-ER@foo.bar.org first.last@foo.jp alice+bob@baz.cn]
- assert @user.valid?, "#{valid_address.inspect} should be valid"
- assertメソッドの第2引数にエラーメッセージを追加していることに注目してください。これによって、どのメールアドレスでテストが失敗したのかを特定できるようになります
- メールアドレスのフォーマットを検証するためには、次のようにformatというオプションを使います
- 演習
- 一意性を検証する
- メールアドレスの一意性を強制するために (ユーザー名として使うために)、validatesメソッドの:uniqueオプションを使います
- 重大な警告
- 一意性のテストのためには、メモリ上だけではなく、実際にレコードをデータベースに登録する必要があります
- まずは重複したメールアドレスからテスト
- duplicate_user = @user.dup
- @user.save
- assert_not duplicate_user.valid?
- 通常、メールアドレスでは大文字小文字が区別されません
- duplicate_user.email = @user.email.upcase
- uniqueness: { case_sensitive: false }
- Active Recordはデータベースのレベルでは一意性を保証していないという問題
- データベース上のemailのカラムにインデックス (index) を追加し、そのインデックスが一意であるようにすれば解決
- コラム 6.2. データベースのインデックス
- データベースにカラムを作成するとき、そのカラムでレコードを検索する (find) 必要があるかどうかを考えることは重要
- すべてのユーザーを最初から順に一人ずつ探していく
- データベースの世界では全表スキャン (Full-table Scan) として知られており、数千のユーザーがいる実際のサイトでは極めて不都合
- emailカラムにインデックスを追加することで、この問題を解決
- すべてのユーザーを最初から順に一人ずつ探していく
- データベースにカラムを作成するとき、そのカラムでレコードを検索する (find) 必要があるかどうかを考えることは重要
- 既に存在するモデルに構造を追加するので、次のようにmigrationジェネレーターを使ってマイグレーションを直接作成する必要があります
- テストDB用のサンプルデータが含まれているfixtures内で一意性の制限が保たれていないため、テストは red になります
- → 今は削除
- いくつかのデータベースのアダプタが、常に大文字小文字を区別するインデックス を使っているとは限らない問題への対処
- 今回は「データベースに保存される直前にすべての文字列を小文字に変換する」という対策
- Active Recordのコールバック (callback) メソッドを利用
- before_save { self.email = email.downcase }
- 演習
- assert_equal mixed_case_email.downcase, @user.reload.email
- email.downcase!
- メソッドの末尾に!を付け足すことにより、email属性を直接変更できるようになります
- Active Record では検証 (Validation) という機能を通して、こういった制約を課すことができる
- セキュアなパスワードを追加する
- セキュアパスワードという手法では、各ユーザーにパスワードとパスワードの確認を入力させ、それを (そのままではなく) ハッシュ化したものをデータベースに保存
- ハッシュ化: ハッシュ関数を使って、入力されたデータを元に戻せない (不可逆な) データにする処理
- ユーザーの認証は、パスワードの送信、ハッシュ化、データベース内のハッシュ化された値との比較、という手順
- ハッシュ化されたパスワード
- セキュアなパスワードの実装は、has_secure_passwordというRailsのメソッドを呼び出すだけでほとんど終わってしまいます
- has_secure_password
- セキュアにハッシュ化したパスワードを、データベース内のpassword_digestという属性に保存できるようになる
- 2つのペアの仮想的な属性 (passwordとpassword_confirmation) が使えるようになる。また、存在性と値が一致するかどうかのバリデーションも追加される
- authenticateメソッドが使えるようになる (引数の文字列がパスワードと一致するとUserオブジェクトを、間違っているとfalseを返すメソッド)
- 魔術的なhas_secure_password機能を使えるようにするには、1つだけ条件
- モデル内にpassword_digestという属性が含まれていること
- digestという言葉は、暗号化用ハッシュ関数という用語が語源
- モデル内にpassword_digestという属性が含まれていること
- 専門用語としての「暗号」というのは、設計上元に戻すことができることを指します
- 「パスワードのハッシュ化」では元に戻せない (不可逆) という点が重要
- 「計算量的に元のパスワードを復元するのは困難である」という点を強調するために、暗号化ではなくハッシュ化という用語を使っています
- まずはpassword_digestカラム用の適切なマイグレーションを生成
- has_secure_passwordを使ってパスワードをハッシュ化するためには、最先端のハッシュ関数であるbcryptが必要
- bcrypt gemをGemfileに追加
- gem 'bcrypt', '3.1.12'
- ユーザーがセキュアなパスワードを持っている
- has_secure_passwordには、仮想的なpassword属性とpassword_confirmation属性に対してバリデーションをする機能も(強制的に)追加されている
- password: "foobar", password_confirmation: "foobar"
- 演習
- {:password=>["can't be blank"]}
- has_secure_passwordには、仮想的なpassword属性とpassword_confirmation属性に対してバリデーションをする機能も(強制的に)追加されている
- パスワードの最小文字数
- @user.password = @user.password_confirmation = "a" * 5
- 多重代入 (Multiple Assignment)
- validates :password, presence: true, length: { minimum: 6 }
- @user.password = @user.password_confirmation = "a" * 5
- ユーザーの作成と認証
- authenticateメソッド
- 間違ったパスワードを与えた結果、user.authenticateがfalseを返した
- 正しいパスワードを与えてみましょう。今度はauthenticateがそのユーザーオブジェクトを返すようになります
- authenticateがUserオブジェクトを返すことは重要ではなく、返ってきた値の論理値がtrueであることが重要
- !!でそのオブジェクトが対応する論理値オブジェクトに変換できることを思い出してください。この性質を利用すると、user.authenticateがいい感じに仕事をしてくれるようになります
- 演習
- user.update_attribute(:name, "test")
- authenticateメソッド
- セキュアパスワードという手法では、各ユーザーにパスワードとパスワードの確認を入力させ、それを (そのままではなく) ハッシュ化したものをデータベースに保存
- 最後に
- ゼロからUserモデルを作成し、そこにname属性やemail属性、パスワード属性を加えました
- それぞれの値を制限する多くの重要なバリデーションも追加
- 渡されたパスワードをセキュアに認証できる機能も実装
- たった12行でここまでの機能が実装
- Railsの注目に値する特長
- 本番環境でUserモデルを使うためには、heroku runコマンドを使ってHeroku上でもマイグレーションを走らせる必要があります
- heroku run rails db:migrate
- 本章のまとめ
- マイグレーションを使うことで、アプリケーションのデータモデルを修正することができる
- Active Recordを使うと、データモデルを作成したり操作したりするための多数のメソッドが使えるようになる
- Active Recordのバリデーションを使うと、モデルに対して制限を追加することができる
- よくあるバリデーションには、存在性・長さ・フォーマットなどがある
- 正規表現は謎めいて見えるが非常に強力である
- データベースにインデックスを追加することで検索効率が向上する。また、データベースレベルでの一意性を保証するためにも使われる
- has_secure_passwordメソッドを使うことで、モデルに対してセキュアなパスワードを追加することができる
Ch07 ユーザー登録
- ユーザーを表示する
- ユーザーの名前とプロフィール写真を表示するためのページを作成
- デバッグとRails環境
- Webサイトのレイアウトにデバッグ情報を追加
- コラム 7.1. Railsの3つの環境
- Railsにはテスト環境 (test)、開発環境 (development)、そして本番環境 (production) の3つの環境がデフォルトで装備されています。
- Rails consoleのデフォルトの環境はdevelopment
- RailsにはRailsというオブジェクトがあり、それには環境の論理値 (boolean) を取るenvという属性があります
- Rails.env.test?はテスト環境ではtrueを返し、それ以外の環境ではfalse
- rails console test
- rails server --environment production
- rails db:migrate RAILS_ENV=production
- console、server、migrateの3つのコマンドでは、デフォルト以外の環境を指定する方法がそれぞれ異なっており、混乱を招く可能性があります
- heroku run rails consoleというコマンドを打つことで、本番環境を確認
- Herokuは本番サイト用のプラットフォームなので、実行されるアプリケーションはすべて本番環境
- デバッグ出力をきれいに整形
- app/assets/stylesheets/custom.scss
- Sassのミックスイン機能 (ここではbox_sizing)
- CSSルールのグループをパッケージ化して複数の要素に適用
- @include box_sizing;
- デバッグ出力には、描画されるページの状態を把握するのに役立つ情報が含まれます
- paramsに含まれている内容で、YAML5 という形式で書かれています
- YAMLは基本的にハッシュであり、コントローラとページのアクションを一意に指定
- Usersリソース
- Railsアプリケーションで好まれているRESTアーキテクチャ (コラム 2.2) の習慣に従う
- resources :users
- この1行を追加すると、ユーザーのURLを生成するための多数の名前付きルート (5.3.3) と共に、RESTfulなUsersリソースで必要となるすべてのアクションが利用できるようになる → 表7.1
- 表 7.1: リスト 7.3のUsersリソースが提供するRESTfulなルート
- app/views/users/show.html.erb
- <%= @user.name %>, <%= @user.email %>
- ユーザー表示ビューが正常に動作するためには、Usersコントローラ内のshowアクションに対応する@user変数を定義する必要
- debuggerメソッド
- Gravatar画像とサイドバー
- Gravatar (Globally Recognized AVATAR) をプロフィールに導入
- app/views/users/show.html.erb
- <%= gravatar_for @user %>
- GravatarのURLはユーザーのメールアドレスをMD5という仕組みでハッシュ化
- app/helpers/users_helper.rb
- ユーザーのサイドバーの最初のバージョンを作りましょう
- asideタグを使って実装
- SCSSを使ってサイドバーなどのユーザー表示ページにスタイルを与える
- HTML要素とCSSクラスを配置したことにより、プロフィールページ (とサイドバーとGravatar) にSCSSでリスト 7.11のようにスタイルを与えることができるようになりました
- 演習
- ユーザー登録フォーム
- form_forを使用する
- ユーザー登録ページで重要な点は、ユーザー登録に欠かせない情報を入力するためのform
- Railsでform_forヘルパーメソッドを使います。このメソッドはActive Recordのオブジェクトを取り込み、そのオブジェクトの属性を使ってフォームを構築
- Rails 5.1から推奨されているform_withメソッドを使って学びたい方は『第6版』
- ユーザー登録ページ /signup のルーティングは、Usersコントローラーのnewアクションに既に紐付けられている
- 次のステップは、 form_forの引数で必要となるUserオブジェクトを作成すること
- newアクションに@user変数を追加
- app/views/users/new.html.erb
- <%= form_for(@user) do |f| %>
- app/assets/stylesheets/custom.scss
- フォームHTML
- <%= form_for(@user) do |f| %>
- doキーワードは、 form_forが1つの変数を持つブロックを取ることを表します。この変数fは “form” のf
- fオブジェクトは、HTMLフォーム要素 (テキストフィールド、ラジオボタン、パスワードフィールドなど) に対応するメソッドが呼び出されると、@userの属性を設定するために特別に設計されたHTMLを返します
- ユーザーの作成で重要なのはinputごとにある特殊なname属性
- Railsはnameの値を使って、初期化したハッシュを (params変数経由で) 構成
- このハッシュは、入力された値に基づいてユーザーを作成するときに使われます
- Railsはnameの値を使って、初期化したハッシュを (params変数経由で) 構成
- 次に重要な要素は、formタグ自身
- <%= form_for(@user) do |f| %>
- form_forを使用する
- ユーザー登録失敗
- 正しいフォーム
- createアクションでフォーム送信を受け取り、User.newを使って新しいユーザーオブジェクトを作成し、ユーザーを保存 (または保存に失敗) し、再度の送信用のユーザー登録ページを表示するという方法で機能を実装
- app/controllers/users_controller.rb
- if-else分岐構造を思い出してください。この文を使って、保存が成功したかどうかに応じて@user.saveの値がtrueまたはfalse (6.1.3) になるときに、それぞれ成功時の処理と失敗時の処理を場合分け
- Strong Parameters
- マスアサインメント
- 値のハッシュを使ってRubyの変数を初期化
- @user = User.new(params[:user])
- paramsハッシュ全体を初期化するという行為はセキュリティ上、極めて危険
- Strong Parametersを使うことで、必須のパラメータと許可されたパラメータを指定することができます
- paramsハッシュをまるごと渡すとエラーが発生するので、Railsはデフォルトでマスアサインメントの脆弱性から守られるようになりました
- params.require(:user).permit(:name, :email, :password, :password_confirmation)
- 戻り値は、許可された属性のみが含まれたparamsのハッシュ
- これらのパラメータを使いやすくするために、user_paramsという外部メソッドを使うのが慣習
- app/controllers/users_controller.rb
- user_paramsメソッドはUsersコントローラの内部でのみ実行され、Web経由で外部ユーザーにさらされる必要はないため、リスト 7.19に示すようにRubyのprivateキーワードを使って外部から使えないようにします
- app/controllers/users_controller.rb
- マスアサインメント
- エラーメッセージ
- ユーザーのnewページでエラーメッセージのパーシャル (partial) を出力
- form-controlというCSSクラスも一緒に追加することで、Bootstrapがうまく取り扱ってくれる
- app/views/shared/_error_messages.html.erb
- 'shared/error_messages'というパーシャルをrender (描画) している
- Rails全般の慣習として、複数のビューで使われるパーシャルは専用のディレクトリ「shared」によく置かれます
- app/views/shared/_error_messages.html.erb
- pluralizeという英語専用のテキストヘルパー
- 最初の引数に整数が与えられると、それに基づいて2番目の引数の英単語を複数形に変更したものを返します
- このメソッドの背後には強力なインフレクター (活用形生成) があり、不規則活用を含むさまざまな単語を複数形にすることができます
- エラーメッセージにスタイルを与えるためのCSS id error_explanationも含まれている
- pluralizeという英語専用のテキストヘルパー
- Railsは、無効な内容の送信によって元のページに戻されると、CSSクラスfield_with_errorsを持ったdivタグでエラー箇所を自動的に囲んでくれます
- このラベルを使うことで、リスト 7.22のようにエラーメッセージをSCSSで整形
- Sassの@extend関数を使ってBootstrapのhas-errorというCSSクラスを適用
- ユーザーのnewページでエラーメッセージのパーシャル (partial) を出力
- 失敗時のテスト
- まずは、新規ユーザー登録用の統合テストを生成するところから始めていきます
- 演習
- assert_select 'div#error_explanation'
- assert_select 'div.field_with_errors'
- post '/signup', to: 'users#create'
- <%= form_for(@user, url: signup_path) do |f| %>
- assert_select 'form[action="/signup"]'
- 正しいフォーム
- ユーザー登録成功
- 登録フォームの完成
- ユーザー登録に成功した場合はページを描画せずに別のページにリダイレクト (Redirect) する
- 新しく作成されたユーザーのプロフィールページにリダイレクト
- redirect_to @user
- redirect_to user_url(@user)と同じ
- ユーザー登録に成功した場合はページを描画せずに別のページにリダイレクト (Redirect) する
- flash
- Webアプリケーションに常識的に備わっている機能を追加してみましょう。
- 登録完了後に表示されるページにメッセージを表示し (この場合は新規ユーザーへのウェルカムメッセージ)、2度目以降にはそのページにメッセージを表示しないようにするというもの
- Railsでこういった情報を表示するためには、flashという特殊な変数
- flash[:success] = "Welcome to the Sample App!"
- Welcome to the Sample App!になる
- Bootstrap CSSは、このようなflashのクラス用に4つのスタイルを持っています (success、info、warning、danger)
- app/views/layouts/application.html.erb
- 演習
- "#{:success}" → "success"
- Webアプリケーションに常識的に備わっている機能を追加してみましょう。
- 実際のユーザー登録
- 成功時のテスト
- 登録フォームの完成
- プロのデプロイ
- 本番環境でのSSL
- Webサイト全体で適用できるため、第8章で実装するログイン機構をセキュアにしたり、9.1.で説明するセッションハイジャック (Session Hijacking) の脆弱性に対しても多くの利点を生み出します
- SSLを有効化するのも簡単です。production.rbという本番環境の設定ファイルの1行を修正するだけで済みます
- Herokuではデフォルトの設定でもSSLを使用できるのですが、SSLの使用をブラウザに強制するわけではありません
- Railsではありがたいことに、本番環境用の設定ファイルであるproduction.rbのコードをたった1行変更するだけでSSLを強制し、httpsによる安全な通信を確立
- config/environments/production.rb
- config.force_ssl = true
- Heroku上でサンプルアプリケーションを動かし、HerokuのSSL証明書に便乗
- Herokuのサブドメインでのみ有効
- www.example.comなどの独自ドメインでSSLを使いたい場合は、HerokuのSSLに関するドキュメント (英語) を参照
- 本番環境用のWebサーバー
- 本番環境へのデプロイ
- 本番環境でのSSL
- 最後に
- 本章のまとめ
- debugメソッドを使うことで、役立つデバッグ情報を表示できる
- Sassのmixin機能を使うと、CSSのルールをまとめたり他の場所で再利用できるようになる
- Railsには標準で3つ環境が備わっており、それぞれ開発環境 (development)、テスト環境 (test)、本番環境 (production)と呼ぶ
- 標準的なRESTfulなURLを通して、ユーザー情報をリソースとして扱えるようになった
- Gravatarを使うと、ユーザーのプロフィール画像を簡単に表示できるようになる
- form_forヘルパーは、Active Recordのオブジェクトに対応したフォームを生成する
- ユーザー登録に失敗した場合はnewビューを再描画するようにした。その際、Active Recordが自動的に検知したエラーメッセージを表示できるようにした
- flash変数を使うと、一時的なメッセージを表示できるようになる
- ユーザー登録に成功すると、データベース上にユーザーが追加、プロフィールページにリダイレクト、ウェルカムメッセージの表示といった順で処理が進む
- 統合テストを使うことで送信フォームの振る舞いを検証したり、バグの発生を検知したりできる
- セキュアな通信と高いパフォーマンスを確保するために、本番環境ではSSLとPumaを導入した
- 本章のまとめ
Ch08 基本的なログイン機構
- ログインの基本的な仕組みとは、ブラウザがログインしている状態を保持し、ユーザーによってブラウザが閉じられたら状態を破棄するといった仕組み (認証システム (Authentification System))
- このような制限や制御の仕組みを認可モデル (Authorization Model) と呼び、例えば本章で実装するログイン済みかどうかでヘッダー部分を切り替える、といった仕組みもこれにあたります
- セッション
- HTTPはステートレス (Stateless) なプロトコル
- ユーザーログインの必要なWebアプリケーションでは、セッション (Session) と呼ばれる半永続的な接続をコンピュータ間 (ユーザーのパソコンのWebブラウザとRailsサーバーなど) に別途設定
- Railsでセッションを実装する方法として最も一般的なのは、cookiesを使う方法
- cookiesとは、ユーザーのブラウザに保存される小さなテキストデータ
- アプリケーションはcookies内のデータを使って、例えばログイン中のユーザーが所有する情報をデータベースから取り出すことができます
- 本節および8.2では、sessionというRailsのメソッドを使って一時セッションを作成
- セッションをRESTfulなリソースとしてモデリングできると、他のRESTfulリソースと統一的に理解できて便利
- ログインページではnewで新しいセッションを出力し、そのページでログインするとcreateでセッションを実際に作成して保存し、ログアウトするとdestroyでセッションを破棄する
- Usersリソースと異なるのは、UsersリソースではバックエンドでUserモデルを介してデータベース上の永続的データにアクセスするのに対し、Sessionリソースでは代わりにcookiesを保存場所として使う点
- ログインの仕組みの大半は、cookiesを使った認証メカニズムによって構築
- 本節と次の節では、セッション機能を作成する準備として、Sessionコントローラ、ログイン用のフォーム、両者に関連するコントローラのアクションを作成
- Sessionsコントローラ
- ログインフォーム
- 7.3.3ではエラーメッセージの表示に専用のパーシャルを使いましたが、そのパーシャルではActive Recordによって自動生成されるメッセージを使っていた
- 今回は、フラッシュメッセージでエラーを表示
- app/views/sessions/new.html.erb
- セッションにはSessionモデルというものがなく、そのため@userのようなインスタンス変数に相当するものもない
- 新しいセッションフォームを作成するときには、form_forヘルパーに追加の情報を独自に渡さなければなりません
- form_for(:session, url: login_path)
- ユーザーの検索と認証
- ログインでセッションを作成する場合に最初に行うのは、入力が無効な場合の処理
- 最初に最小限のcreateアクションをSessionsコントローラで定義し、空のnewアクションとdestroyアクションも作成
- createアクションの中では、ユーザーの認証に必要なあらゆる情報をparamsハッシュから簡単に取り出せる
- app/controllers/sessions_controller.rb
- user = User.find_by(email: params[:session][:email].downcase)
- if user && user.authenticate(params[:session][:password])
- フラッシュメッセージを表示する
- フラッシュのテスト
- ログイン
- ログイン中の状態での有効な値の送信をフォームで正しく扱えるようにします
- cookiesを使った一時セッションでユーザーをログインできるようにします
- app/controllers/application_controller.rb
- include SessionsHelper
- log_inメソッド
- session[:user_id] = user.id
- ユーザーのブラウザ内の一時cookiesに暗号化済みのユーザーIDが自動で作成
- cookiesメソッド (9.1) とは対照的に、sessionメソッドで作成された一時cookiesは、ブラウザを閉じた瞬間に有効期限が終了
- app/helpers/sessions_helper.rb
- def log_in(user)
- session[:user_id] = user.id
- def log_in(user)
- sessionメソッドで作成した一時cookiesは自動的に暗号化され、リスト 8.14のコードは保護されます
- 攻撃者がたとえこの情報をcookiesから盗み出すことができたとしても、それを使って本物のユーザーとしてログインすることはできない
- ただし今述べたことは、sessionメソッドで作成した「一時セッション」にしか該当しません
- cookiesメソッドで作成した「永続的セッション」ではそこまで断言はできません
- 永続的なcookiesには、セッションハイジャックという攻撃を受ける可能性が常につきまといます
- log_inというヘルパーメソッドを定義できたので、やっと、ユーザーログインを行ってセッションのcreateアクションを完了し、ユーザーのプロフィールページにリダイレクトする準備ができました
- app/controllers/sessions_controller.rb
- log_in user
- redirect_to user
- app/controllers/sessions_controller.rb
- session[:user_id] = user.id
- 現在のユーザー
- if session[:user_id]
- セッションにユーザーIDが存在しない場合、このコードは単に終了して自動的にnilを返します
- @current_user ||= User.find_by(id: session[:user_id])
- app/helpers/sessions_helper.rb
- def current_user
- if session[:user_id]
- @current_user ||= User.find_by(id: session[:user_id])
- if session[:user_id]
- def current_user
- if session[:user_id]
- レイアウトリンクを変更する
- app/helpers/sessions_helper.rb
- def logged_in?
- !current_user.nil?
- def logged_in?
- app/views/layouts/_header.html.erb
- <% if logged_in? %>
- <%= link_to "Profile", current_user %>
- current_userを使う方が、Railsによってuser_path(current_user)に変換され、プロフィールへのリンクが自動的に生成できるので便利
- <%= link_to "Log out", logout_path, method: :delete %>
- <%= link_to "Log in", login_path %>
- app/helpers/sessions_helper.rb
- application.jsにBootstrapのJavaScriptライブラリを追加する
- app/assets/javascripts/application.js
- //= require jquery
- //= require bootstrap
- app/assets/javascripts/application.js
- テスト用データをfixture (フィクスチャ) で作成
- Railsのsecure_passwordのソースコードを調べてみると、次の部分でパスワードが生成
- BCrypt::Password.create(string, cost: cost)
- costはコストパラメータ
- コストパラメータの値を高くすれば、ハッシュからオリジナルのパスワードを計算で推測することが困難
- 本番環境ではセキュリティ上重要
- cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST : BCrypt::Engine.cost
- BCrypt::Password.create(string, cost: cost)
- fixture向けのdigestメソッドを追加
- app/models/user.rb
- ユーザーログインのテストで使うfixture
- test/fixtures/users.yml
- password_digest: <%= User.digest('password') %>
- テスト用のfixtureでは全員同じパスワード「password」を使う
- test/fixtures/users.yml
- test/integration/users_login_test.rb
- def setup
- @user = users(:michael)
- test "login with valid information" do
- assert_redirected_to @user
- follow_redirect!
- assert_select "a[href=?]", login_path, count: 0
- def setup
- ユーザー登録中にログインするには、Usersコントローラのcreateアクションにlog_inを追加するだけで済みます
- app/controllers/users_controller.rb
- if @user.save
- log_in @user
- if @user.save
- app/controllers/users_controller.rb
- テスト中のログインステータスを論理値で返すメソッド
- test/test_helper.rb
- def is_logged_in?
- test/test_helper.rb
- test/integration/users_signup_test.rb
- assert is_logged_in?
- ログインの場合 (リスト 8.15とリスト 8.25) と異なり、ログアウト処理は1か所で行える
- app/helpers/sessions_helper.rb
- def log_out
- session.delete(:user_id)
- @current_user = nil
- def log_out
- app/controllers/sessions_controller.rb
- log_out
- redirect_to root_url
- test/integration/users_login_test.rb
- test "login with valid information followed by logout" do
- 本章では、サンプルアプリケーションの基本的なログイン機構 (認証システム) を実装
- 本章のまとめ
- Railsのsessionメソッドを使うと、あるページから別のページに移動するときの状態を保持できる。一時的な状態の保存にはcookiesも使える
- ログインフォームでは、ユーザーがログインするための新しいセッションが作成できる
- flash.nowメソッドを使うと、描画済みのページにもフラッシュメッセージを表示できる
- テスト駆動開発は、回帰バグを防ぐときに便利
- sessionメソッドを使うと、ユーザーIDなどをブラウザに一時的に保存できる
- ログインの状態に応じて、ページ内で表示するリンクを切り替えることができる
- 統合テストでは、ルーティング、データベースの更新、レイアウトの変更が正しく行われているかを確認できる
Ch09 発展的なログイン機構
- ユーザーのログイン情報を記憶しておき、ブラウザを再起動した後でもすぐにログインできる機能 (remember me) を備えていることも一般的
- 永続クッキー (permanent cookies) を使ってこの機能を実現
- Remember me 機能
- 記憶トークンと暗号化
- セッションの永続化の第一歩として記憶トークン (remember token) を生成
- cookiesメソッドによる永続的cookiesの作成や、安全性の高い記憶ダイジェスト (remember digest) によるトークン認証にこの記憶トークンを活用
- パスワードとトークンとの一般的な違いは、パスワードはユーザーが作成・管理する情報であるのに対し、トークンはコンピューターが作成・管理する情報である点
- cookiesを盗み出す有名な方法
- rails generate migration add_remember_digest_to_users remember_digest:string
- 記憶トークンとして何を使うかを決める
- ユーザーを記憶するには、記憶トークンを作成して、そのトークンをダイジェストに変換したものをデータベースに保存
- トークン生成用メソッドを追加する
- app/models/user.rb
- def User.new_token
- SecureRandom.urlsafe_base64
- def User.new_token
- app/models/user.rb
- user.rememberメソッドを作成
- 演習
- selfは、通常の文脈ではUser「モデル」、つまりユーザーオブジェクトのインスタンスを指しますが、リスト 9.4やリスト 9.5の文脈では、selfはUser「クラス」を指す
- def self.digest(string)
- class << self
- selfは、通常の文脈ではUser「モデル」、つまりユーザーオブジェクトのインスタンスを指しますが、リスト 9.4やリスト 9.5の文脈では、selfはUser「クラス」を指す
- ログイン状態の保持
- user.rememberメソッドが動作するようになったので、ユーザーの暗号化済みIDと記憶トークンをブラウザの永続cookiesに保存して、永続セッションを作成する準備ができました
- 実際に行うにはcookiesメソッドを使います
- このメソッドは、sessionのときと同様にハッシュとして扱えます
- 個別のcookiesは、1つのvalue (値) と、オプションのexpires (有効期限) からできています
- cookies[:remember_token] = { value: remember_token, expires: 20.years.from_now.utc }
- ユーザーIDをcookiesに保存
- cookies.permanent.signed[:user_id] = user.id
- User.find_by(id: cookies.signed[:user_id])
- cookies.signed[:user_id]では自動的にユーザーIDのcookiesの暗号が解除され、元に戻ります
- 渡されたトークンがユーザーの記憶ダイジェストと一致することを確認
- BCrypt gemでは,比較に使っている==演算子が再定義されている
- BCrypt::Password.new(remember_digest).is_password?(remember_token)を使っている
- authenticated?をUserモデルに追加する
- app/models/user.rb
- BCrypt::Password.new(remember_digest).is_password?(remember_token)
- app/models/user.rb
- BCrypt gemでは,比較に使っている==演算子が再定義されている
- ログインしてユーザーを保持する
- app/controllers/sessions_controller.rb
- remember user
- app/controllers/sessions_controller.rb
- ユーザーを記憶する
- app/helpers/sessions_helper.rb
- def remember(user)
- app/helpers/sessions_helper.rb
- 永続的セッションのcurrent_userを更新する
- def current_user
- ユーザーを忘れる
- forgetメソッドをUserモデルに追加
- update_attribute(:remember_digest, nil)
- 永続セッションからログアウト
- app/helpers/sessions_helper.rb
- forgetヘルパーメソッドではuser.forgetを呼んでからuser_idとremember_tokenのcookiesを削除
- forgetメソッドをUserモデルに追加
- 2つの目立たないバグ
- 記憶トークンと暗号化
- [Remember me] チェックボックス
- [remember me] チェックボックスをログインフォームに追加
- [remember me] チェックボックスのCSS
- [remember me] チェックボックスの送信結果を処理
- app/controllers/sessions_controller.rb
- params[:session][:remember_me] == '1' ? remember(user) : forget(user)
- コラム 9.2. 10種類の人々
- 「この世には10種類の人間がいる。2進法を理解できる奴と、2進法を理解できない奴だ」
- 三項演算子 (ternary operator)
- 論理値? ? 何かをする : 別のことをする
- app/controllers/sessions_controller.rb
- [Remember me] のテスト
- [Remember me] ボックスをテストする
- [Remember me] をテストする
- テストを忘れている疑いのあるコードブロック内にわざと例外発生を仕込む
- raise
- test/helpers/sessions_helper_test.rb
- assert_equal @user, current_user
- assert is_logged_in?
- @user.update_attribute(:remember_digest, User.digest(User.new_token))
- assert_nil current_user
- テストを忘れている疑いのあるコードブロック内にわざと例外発生を仕込む
- 最後に
Ch10 ユーザーの更新・表示・削除
- Usersリソース用のRESTアクション (表 7.1) のうち、これまで未実装だったedit、update、index、destroyアクションを加え、RESTアクションを完成させます
- 認可モデル (Authorization Model) について
- サンプルデータとページネーション (pagination) を導入
- ユーザーを更新する
- ユーザー情報を編集するパターンは、新規ユーザーの作成と極めて似通っています
- newアクションと同じようにして、ユーザーを編集するためのeditアクションを作成
- POSTリクエストに応答するcreateの代わりに、PATCHリクエストに応答するupdateアクションを作成
- 最大の違いは、ユーザー登録は誰でも実行できますが、ユーザー情報を更新できるのはそのユーザー自身に限られるということ
- beforeフィルター (before filter) を使ってこのアクセス制御を実現
- 編集フォーム
- ユーザーのeditアクション
- app/controllers/users_controller.rb
- @user = User.find(params[:id])
- app/controllers/users_controller.rb
- ユーザーのeditビュー
- Railsはどうやって新規ユーザー用のPOSTリクエストとユーザー編集用のPATCHリクエストを区別するのでしょうか
- Railsは、ユーザーが新規なのか、それともデータベースに存在する既存のユーザーであるかを、Active Recordのnew_record?論理値メソッドを使って区別できる
- form_for(@user)を使ってフォームを構成すると、@user.new_record?がtrueのときにはPOSTを、falseのときにはPATCHを使います
- レイアウトの “Settings” リンクを更新する
- app/views/layouts/_header.html.erb
- <\li><%= link_to "Settings", edit_user_path(current_user) %><\/li>
- app/views/layouts/_header.html.erb
- 演習
- target="_blank"で新しいページを開くときには、セキュリティ上の小さな問題があります。それは、リンク先のサイトがHTMLドキュメントのwindowオブジェクトを扱えてしまう、という点
- 具体的には、フィッシング (Phising) サイトのような、悪意のあるコンテンツを導入させられてしまう可能性があります
- 対処方法は、リンク用のaタグのrel (relationship) 属性に、"noopener"と設定するだけ
- newとeditフォーム用のパーシャル
- <% provide(:button_text, "Save changes") %>
- <%= render 'form' %>
- view以下のパスを書くが,同階層ならファイル名だけでよいようだ
- partialを示す_は不要
- target="_blank"で新しいページを開くときには、セキュリティ上の小さな問題があります。それは、リンク先のサイトがHTMLドキュメントのwindowオブジェクトを扱えてしまう、という点
- ユーザーのeditアクション
- 編集の失敗
- 編集失敗時のテスト
- TDDで編集を成功させる
- より快適にテストをするためには、アプリケーション用のコードを「実装する前に」統合テストを書いた方が便利
- そういったテストのことは「受け入れテスト (Acceptance Tests)」
- ある機能の実装が完了し、受け入れ可能な状態になったかどうかを決めるテスト
- ユーザー名やメールアドレスを編集するときに毎回パスワードを入力するのは不便なので、(パスワードを変更する必要が無いときは) パスワードを入力せずに更新できると便利
- @user.reloadを使って、データベースから最新のユーザー情報を読み込み直して、正しく更新されたかどうかを確認
- test/integration/users_edit_test.rb
- ユーザーのupdateアクション
- flash[:success] = "Profile updated"
- redirect_to @user
- パスワードが空のままでも更新できるようにする
- より快適にテストをするためには、アプリケーション用のコードを「実装する前に」統合テストを書いた方が便利
- 認可
- ウェブアプリケーションの文脈では、認証 (authentication) はサイトのユーザーを識別すること
- 認可 (authorization) はそのユーザーが実行可能な操作を管理すること
- この節では、ユーザーにログインを要求し、かつ自分以外のユーザー情報を変更できないように制御
- こういったセキュリティ上の制御機構をセキュリティモデルと呼びます
- ユーザーにログインを要求する
- 転送させる仕組みを実装したいときは、Usersコントローラの中でbeforeフィルターを使います
- beforeフィルターは、before_actionメソッドを使って何らかの処理が実行される直前に特定のメソッドを実行する仕組み
- beforeフィルターにlogged_in_userを追加
- app/controllers/users_controller.rb
- before_action :logged_in_user, only: [:edit, :update]
- :onlyオプション (ハッシュ) を渡すことで、:editと:updateアクションだけにこのフィルタが適用されるように制限
- unless logged_in?
- flash[:danger] = "Please log in."
- redirect_to login_url
- before_action :logged_in_user, only: [:edit, :update]
- app/controllers/users_controller.rb
- テストユーザーでログインする
- test/integration/users_edit_test.rb
- log_in_as(@user)
- test/integration/users_edit_test.rb
- セキュリティモデルを確認するためにbeforeフィルターをコメントアウト
- editとupdateアクションの保護に対するテストする
- 転送させる仕組みを実装したいときは、Usersコントローラの中でbeforeフィルターを使います
- 正しいユーザーを要求する
- ユーザーが自分の情報だけを編集できるようにする必要
- セキュリティモデルが正しく実装されている確信を持つために、テスト駆動開発で進めていきます
- fixtureファイルに2人目のユーザーを追加
- test/fixtures/users.yml
- log_in_asメソッドを使って、editアクションとupdateアクションをテスト
- 間違ったユーザーが編集しようとしたときのテスト
- 既にログイン済みのユーザーを対象としているため、ログインページではなくルートURLにリダイレクト
- 間違ったユーザーが編集しようとしたときのテスト
- beforeフィルターを使って編集/更新ページを保護
- correct_userというメソッドを作成し、beforeフィルターからこのメソッドを呼び出す
- redirect_to(root_url) unless current_user?(@user)
- correct_userというメソッドを作成し、beforeフィルターからこのメソッドを呼び出す
- current_user?メソッド
- app/helpers/sessions_helper.rb
- def current_user?(user)
- user == current_user
- def current_user?(user)
- app/helpers/sessions_helper.rb
- フレンドリーフォワーディング
- 保護されたページにアクセスしようとすると、問答無用で自分のプロフィールページに移動させられてしまいます
- リダイレクト先は、ユーザーが開こうとしていたページにしてあげるのが親切
- 実際のテストはまず編集ページにアクセスし、ログインした後に、(デフォルトのプロフィールページではなく) 編集ページにリダイレクトされているかどうかをチェックする
- get edit_user_path(@user)
- log_in_as(@user)
- assert_redirected_to edit_user_url(@user)
- フレンドリーフォワーディングの実装
- app/helpers/sessions_helper.rb
- def redirect_back_or(default)
- redirect_to(session[:forwarding_url] || default)
- session.delete(:forwarding_url)
- これをやっておかないと、次回ログインしたときに保護されたページに転送されてしまい、ブラウザを閉じるまでこれが繰り返されてしまいます
- 転送用のURLを削除する動作はredirect文の後に置かれていても実行される
- 明示的にreturn文やメソッド内の最終行が呼び出されない限り、リダイレクトは発生しません
- def store_location
- session[:forwarding_url] = request.original_url if request.get?
- def redirect_back_or(default)
- app/controllers/users_controller.rb
- app/controllers/sessions_controller.rb
- requestオブジェクトも使っています (request.original_urlでリクエスト先が取得できます)
- POSTや PATCH、DELETEリクエストを期待しているURLに対して、(リダイレクトを通して) GETリクエストが送られてしまい、場合によってはエラーが発生します
- if request.get?という条件文を使ってこのケースに対応
- ログインユーザー用beforeフィルターにstore_locationを追加
- デフォルトのURLは、Sessionコントローラのcreateアクションに追加し、サインイン成功後にリダイレクト
- 演習
- test/integration/users_edit_test.rb
- assert_nil session[:forwarding_url]
- test/integration/users_edit_test.rb
- app/helpers/sessions_helper.rb
- すべてのユーザーを表示する
- ユーザーの一覧ページ
- indexアクションのリダイレクトをテスト
- test "should redirect index when not logged in" do
- beforeフィルターのlogged_in_userにindexアクションを追加して、このアクションを保護
- ユーザーのindexアクション
- @users = User.all
- ユーザーのindexビュー
- ユーザーを列挙してユーザーごとにliタグで囲むビューを作成
- eachメソッドを使って作成
- ユーザーを列挙してユーザーごとにliタグで囲むビューを作成
- gravatar_forヘルパーにオプション引数を追加する
- ユーザーのindexページ用のCSS
- ユーザー一覧ページへのリンクを更新
- app/views/layouts/_header.html.erb
- 演習
- test/integration/site_layout_test.rb
- userをセットアップして,セットアップしたユーザでログイン
- routeをgetして各linkをselectする
- test/integration/site_layout_test.rb
- indexアクションのリダイレクトをテスト
- サンプルのユーザー
- ページネーション
- Railsには豊富なページネーションメソッドがあります。今回はその中で最もシンプルかつ堅牢なwill_paginateメソッドを使ってみましょう
- Gemfileにwill_paginate gem とbootstrap-will_paginate gemを両方含め、Bootstrapのページネーションスタイルを使ってwill_paginateを構成する
- indexページでpaginationを使う
- <%= will_paginate %>
- paginateでは、キーが:pageで値がページ番号のハッシュを引数に取ります
- User.paginateは、:pageパラメーターに基いて、データベースからひとかたまりのデータ (デフォルトでは30) を取り出し
- pageがnilの場合、 paginateは単に最初のページを返します
- indexアクションでUsersをページネート
- @users = User.paginate(page: params[:page])
- :pageパラメーターにはparams[:page]が使われていますが、これはwill_paginateによって自動的に生成
- ユーザー一覧のテスト
- 今回のテストでは、ログイン、indexページにアクセス、最初のページにユーザーがいることを確認、ページネーションのリンクがあることを確認、といった順でテスト
- fixtureでは埋め込みRubyをサポート
- fixtureにさらに30人のユーザーを追加する
- <% 30.times do |n| %>
- user_<%= n %>:
- name: <%= "User #{n}" %>
- rails generate integration_test users_index
- paginationクラスを持ったdivタグをチェックして、最初のページにユーザーがいることを確認
- User.paginate(page: 1).each do |user|
- assert_select 'a[href=?]', user_path(user), text: user.name
- User.paginate(page: 1).each do |user|
- 演習
- assert_select 'div.pagination', count: 2
- パーシャルのリファクタリング
- ユーザーのliをrender呼び出しに置き換え
- <%= render user %>
- renderをパーシャル (ファイル名の文字列) に対してではなく、Userクラスのuser変数に対して実行
- Railsは自動的に_user.html.erbという名前のパーシャルを探しにいく
- <%= render user %>
- app/views/users/_user.html.erb
- renderを@users変数に対して直接実行
- ユーザーのliをrender呼び出しに置き換え
- ユーザーの一覧ページ
- ユーザーを削除する
- 管理ユーザー
- 論理値をとるadmin属性をUserモデルに追加
- rails generate migration add_admin_to_users admin:boolean
- default: falseという引数をadd_columnに追加
- サンプルデータ生成タスクに管理者を1人追加
- db/seeds.rb
- Strong Parameters、再び
- 演習
- admin属性の変更が禁止されていることをテスト
- test/controllers/users_controller_test.rb
- patch user_path(@other_user), params: { user: { password: @other_user.password, password_confirmation: @other_user.password,admin: true } }
- assert_not @other_user.reload.admin?
- 論理値をとるadmin属性をUserモデルに追加
- destroyアクション
- Usersリソースの最後の仕上げとして、destroyアクションへのリンクを追加
- まず、ユーザーindexページの各ユーザーに削除用のリンクを追加
- 続いて管理ユーザーへのアクセスを制限
- app/views/users/_user.html.erb
- <% if current_user.admin? && !current_user?(user) %>
- | <%= link_to "delete", user, method: :delete, data: { confirm: "You sure?" } %>
- 必要なDELETEリクエストを発行するリンクの生成は、method: :deleteによって行われている
- <% if current_user.admin? && !current_user?(user) %>
- ブラウザはネイティブではDELETEリクエストを送信できないため、RailsではJavaScriptを使って偽造
- JavaScriptをサポートしないブラウザをサポートする必要がある場合は、フォームとPOSTリクエストを使ってDELETEリクエストを偽造することもできます。こちらはJavaScriptがなくても動作
- destroyアクション (表 7.1) を追加
- 該当するユーザーを見つけてActive Recordのdestroyメソッドを使って削除し、最後にユーザーindexに移動
- User.find(params[:id]).destroy
- flash[:success] = "User deleted"
- redirect_to users_url
- :destroyアクションもlogged_in_userフィルターに追加
- 該当するユーザーを見つけてActive Recordのdestroyメソッドを使って削除し、最後にユーザーindexに移動
- サイトを正しく防衛するには、destroyアクションにもアクセス制御を行う必要
- beforeフィルターを使ってdestroyアクションへのアクセスを制御
- before_action :admin_user, only: :destroy
- redirect_to(root_url) unless current_user.admin?
- beforeフィルターを使ってdestroyアクションへのアクセスを制御
- ユーザー削除のテスト
- fixture内の最初のユーザーを管理者にする
- Usersコントローラをテストするために、アクション単位でアクセス制御をテスト
- 2つのケースをチェック
- ログインしていないユーザーであれば、ログイン画面にリダイレクト
- ログイン済みではあっても管理者でなければ、ホーム画面にリダイレクト
- log_in_as(@other_user)
- assert_no_difference 'User.count' do
- delete user_path(@user)
- assert_redirected_to login_url
- 管理者ではないユーザーの振る舞いについて検証していますが、管理者ユーザーの振る舞いと一緒に確認できるとよさそう
- 2つのケースをチェック
- 削除リンクとユーザー削除に対する統合テスト
- test/integration/users_index_test.rb
- test "index as admin including pagination and delete links" do
- 各ユーザーの削除リンクをテストするときに、ユーザーが管理者であればスキップしている
- first_page_of_users = User.paginate(page: 1)
- first_page_of_users.each do |user|
- assert_select 'a[href=?]', user_path(user), text: user.name
- unless user == @admin
- assert_select 'a[href=?]', user_path(user), text: 'delete'
- unless user == @admin
- assert_select 'a[href=?]', user_path(user), text: user.name
- assert_difference 'User.count', -1 do
- delete user_path(@non_admin)
- test "index as non-admin" do
- assert_select 'a', text: 'delete', count: 0
- test "index as admin including pagination and delete links" do
- test/integration/users_index_test.rb
- 管理ユーザー
- 最後に
- 今は登録もログインもログアウトもできます
- プロフィールの表示も、設定の編集も、すべてのユーザーの一覧画面もあります
- 一部のユーザーは他のユーザーを削除することすらできるようになりました
- この時点で、サンプルアプリケーションはWebサイトとしての十分な基盤 (ユーザーを認証したり認可したり) が整った
- アプリケーションを本番展開したり、サンプルデータを本番データとして作成することもできます (本番データベースをリセットするにはpg:resetタスクを使います)
- 本章のまとめ
- ユーザーは、編集フォームからPATCHリクエストをupdateアクションに対して送信し、情報を更新する
- Strong Parametersを使うことで、安全にWeb上から更新させることができる
- beforeフィルターを使うと、特定のアクションが実行される直前にメソッドを呼び出すことができる
- beforeフィルターを使って、認可 (アクセス制御) を実現した
- 認可に対するテストでは、特定のHTTPリクエストを直接送信する低級なテストと、ブラウザの操作をシミュレーションする高級なテスト (統合テスト) の2つを利用した
- フレンドリーフォワーディングとは、ログイン成功時に元々行きたかったページに転送させる機能である
- ユーザー一覧ページでは、すべてのユーザーをページ毎に分割して表示する
- rails db:seedコマンドは、db/seeds.rbにあるサンプルデータをデータベースに流し込む
- render @usersを実行すると、自動的に_user.html.erbパーシャルを参照し、各ユーザーをコレクションとして表示する
- boolean型のadmin属性をUserモデルに追加すると、admin?という論理オブジェクトを返すメソッドが自動的に追加される
- 管理者が削除リンクをクリックすると、DELETEリクエストがdestroyアクションに向けて送信され、該当するユーザーが削除される
- fixtureファイル内で埋め込みRubyを使うと、多量のテストユーザーを作成することができる
Ch11 アカウントの有効化
- アカウント有効化やパスワード再設定の仕組みと、以前に実装したパスワードや記憶トークンの仕組みにはよく似た点が多い
- 多くのアイデアを使い回すことができます (具体的にはUser.digestやUser.new_token、改造版のuser.authenticated?メソッドなど)
- AccountActivationsリソース
- セッション機能 (8.1) を使って、アカウントの有効化という作業を「リソース」としてモデル化する
- AccountActivationsコントローラ
- AccountActivationのデータモデル
- rails generate migration add_activation_to_users activation_digest:string activated:boolean activated_at:datetime
- add_column :users, :activated, :boolean, default: false
- Activationトークンのコールバック
- before_create :create_activation_digest
- メソッド参照と呼ばれるもので、こうするとRailsはcreate_activation_digestというメソッドを探し、ユーザーを作成する前に実行
- before_create :create_activation_digest
- Userモデルにアカウント有効化のコードを追加
- メールアドレスを小文字にするメソッドも (リスト 6.32)、メソッド参照に切り替え
- サンプルユーザーの生成とテスト
- サンプルデータとfixtureも更新し、テスト時のサンプルとユーザーを事前に有効化
- db/seeds.rb
- test/fixtures/users.yml
- アカウント有効化のメール送信
- Action Mailerライブラリを使ってUserのメイラーを追加
- 送信メールのテンプレート
- rails generate mailer UserMailer account_activation password_reset
- メイラーは、モデルやコントローラと同様にrails generateで生成
- 生成したメイラーごとに、ビューのテンプレートが2つずつ生成されます。1つはテキストメール用のテンプレート、1つはHTMLメール用のテンプレート
- 最初に、生成されたテンプレートをカスタマイズして、実際に有効化メールで使えるようにします
- app/mailers/application_mailer.rb
- default from: "noreply@example.com"
- app/mailers/user_mailer.rb
- def account_activation(user)
- mail to: user.email, subject: "Account activation"
- def account_activation(user)
- app/views/user_mailer/account_activation.text.erb
- アカウント有効化のテキストビュー
- <%= edit_account_activation_url(@user.activation_token, email: @user.email) %>
- app/views/user_mailer/account_activation.html.erb
- アカウント有効化のHTMLビュー
- app/mailers/application_mailer.rb
- rails generate mailer UserMailer account_activation password_reset
- 送信メールのプレビュー
- 送信メールのテスト
- ユーザーのcreateアクションを更新
- アカウントを有効化する
- 今度はAccountActivationsコントローラのeditアクションを書いていきましょう
- アクションへのテストを書き、しっかりとテストできていることが確認できたら、AccountActivationsコントローラからUserモデルにコードを移していく作業 (リファクタリング)
- authenticated?メソッドの抽象化
- メタプログラミング
- sendメソッドの強力きわまる機能です。このメソッドは、渡されたオブジェクトに「メッセージを送る」ことによって、呼び出すメソッドを動的に決めることができます
- app/models/user.rb
- 抽象化されたauthenticated?メソッド
- digest = send("#{attribute}_digest")
- app/helpers/sessions_helper.rb
- if user && user.authenticated?(:remember, cookies[:remember_token])
- test/models/user_test.rb
- assert_not @user.authenticated?(:remember, '')
- メタプログラミング
- editアクションで有効化
- paramsハッシュで渡されたメールアドレスに対応するユーザーを認証
- if user && !user.activated? && user.authenticated?(:activation, params[:id])
- app/controllers/account_activations_controller.rb
- アカウントを有効化するeditアクション
- user.update_attribute(:activated, true)
- user.update_attribute(:activated_at, Time.zone.now)
- app/controllers/sessions_controller.rb
- 有効でないユーザーがログインすることのないようにする
- paramsハッシュで渡されたメールアドレスに対応するユーザーを認証
- 有効化のテストとリファクタリング
- test/integration/users_signup_test.rb
- assert_equal 1, ActionMailer::Base.deliveries.size
- 本当に重要な1点
- deliveriesは変数なので、setupメソッドでこれを初期化しておかないと、並行して行われる他のテストでメールが配信されたときにエラーが発生
- assert_equal 1, ActionMailer::Base.deliveries.size
- app/models/user.rb
- Userモデルにユーザー有効化メソッドを追加
- user.という記法を使っていない
- def activate
- update_attribute(:activated, true)
- update_attribute(:activated_at, Time.zone.now)
- def send_activation_email
- UserMailer.account_activation(self).deliver_now
- app/controllers/users_controller.rb
- ユーザーモデルオブジェクトからメールを送信
- @user.send_activation_email
- app/controllers/account_activations_controller.rb
- ユーザーモデルオブジェクト経由でアカウントを有効化
- user.activate
- 演習
- update_columns(activated: true, activated_at: Time.zone.now)
- @users = User.where(activated: true).paginate(page: params[:page])
- redirect_to root_url and return unless @user.activated?
- まずshow用にintegrationテストを作成する(indexは既存)
- rails g integration_test users_show
- テスト用のactivated: falseユーザーを新しく作る
- test/fixtures/users.yml
- activated: false
- test/integration/users_index_test.rb
- if user.activated?
- else
- assert_select 'a[href=?]', user_path(user), text: user.name, count: 0
- unless user == @admin || !user.activated?
- test/integration/users_show_test.rb
- get user_path(@non_activated_user)
- assert_redirected_to root_url
- test/integration/users_signup_test.rb
- 本番環境でのメール送信
- heroku apps:rename sample-app
- 本番環境からメール送信するために、「Mailgun」というHerokuアドオンを利用してアカウントを検証
- (このアドオンを利用するためにはHerokuアカウントにクレジットカードを設定する必要がありますが、アカウント検証では料金は発生しません)
- 「starter」というプランを使うことにします。これは、(執筆時点では) 1日のメール数が最大400通までという制限がありますが、無料で利用することができます
- config/environments/production.rb
- Railsのproduction環境でMailgunを使う設定
- heroku addons:create mailgun:starter
- MailgunのHerokuアドオンを追加するために、コマンドを実行
- Herokuの環境変数を表示したい場合は、次のコマンドを実行
- Mailgunアカウントのuser_nameとpassword設定を記入する行もありますが、そこには記入せず、必ず環境変数「ENV」に設定する
- 受信メールの認証
- heroku addons:open mailgun
- 最後に
- アカウント有効化を実装したことにより、サンプルアプリケーションの「ユーザー登録」「ログイン」「ログアウト」の仕組みがほぼ完成した
- これを完成させるための最後に1ピースは、ユーザーがパスワードを忘れた時の「パスワード再設定」機能
- 本章のまとめ
- アカウント有効化は Active Recordオブジェクトではないが、セッションの場合と同様に、リソースでモデル化できる
- Railsは、メール送信で扱うAction Mailerのアクションとビューを生成することができる
- Action MailerではテキストメールとHTMLメールの両方を利用できる
- メイラーアクションで定義したインスタンス変数は、他のアクションやビューと同様、メイラーのビューから参照できる
- アカウントを有効化させるために、生成したトークンを使って一意のURLを作る
- より安全なアカウント有効化のために、ハッシュ化したトークン (ダイジェスト) を使う
- メイラーのテストと統合テストは、どちらもUserメイラーの振舞いを確認するのに有用
- Mailgunを使うと、production環境からメールを送信できる
Ch12 パスワードの再設定
- ほとんどは、アカウント有効化で見てきた内容と似通っています
- PasswordResetsリソースを作成して、再設定用のトークンとそれに対応するダイジェストを保存するのが今回の目的
- 全体の流れ
- ユーザーがパスワードの再設定をリクエストすると、ユーザーが送信したメールアドレスをキーにしてデータベースからユーザーを見つける
- 該当のメールアドレスがデータベースにある場合は、再設定用トークンとそれに対応する再設定ダイジェストを生成する
- 再設定用ダイジェストはデータベースに保存しておき、再設定用トークンはメールアドレスと一緒に、ユーザーに送信する有効化用メールのリンクに仕込んでおく
- ユーザーがメールのリンクをクリックしたら、メールアドレスをキーとしてユーザーを探し、データベース内に保存しておいた再設定用ダイジェストと比較する (トークンを認証する)
- 認証に成功したら、パスワード変更用のフォームをユーザーに表示する
- PasswordResetsリソース
- まずはPasswordResetsリソースのモデリング
- 新たなモデルは作らずに、代わりに必要なデータ (再設定用のダイジェストなど) をUserモデルに追加していく形
- PasswordResetsもリソースとして扱っていきたいので、まずは標準的なRESTfulなURLを用意
- パスワードを再設定するフォームが必要なので、ビューを描画するためのnewアクションとeditアクションが必要
- それぞれのアクションに対応する作成用/更新用のアクションも最終的なRESTfulなルーティングには必要になります
- PasswordResetsコントローラ
- rails generate controller PasswordResets new edit --no-test-framework
- config/routes.rb
- resources :password_resets, only: [:new, :create, :edit, :update]
- app/views/sessions/new.html.erb
- パスワード再設定画面へのリンクを追加する
- <%= link_to "(forgot password)", new_password_reset_path %>
- 新しいパスワードの設定
- パスワードの再設定でも、トークン用の仮想的な属性とそれに対応するダイジェストを用意
- 再設定用のリンクはなるべく短時間 (数時間以内) で期限切れになるようにしなければなりません
- reset_digest属性とreset_sent_at属性をUserモデルに追加
- rails generate migration add_reset_to_users reset_digest:string reset_sent_at:datetime
- app/views/password_resets/new.html.erb
- 新しいセッションを作成するためのログインフォーム (リスト 8.4) を使います
- form_forで扱うリソースとURLが異なっている点と、パスワード属性が省略されている点
- 演習
- password_resetはUserモデルに変更を加えるコントローラ
- インスタンス変数を使う場合@user
- form_forに@userを使うと/usersに対応するPOSTとしてsignupとなる
- → :password_reset
- password_resetはUserモデルに変更を加えるコントローラ
- createアクションでパスワード再設定
- app/controllers/password_resets_controller.rb
- パスワード再設定用のcreateアクション
- app/models/user.rb
- Userモデルにパスワード再設定用メソッドを追加する
- def create_reset_digest
- def send_password_reset_email
- app/controllers/password_resets_controller.rb
- まずはPasswordResetsリソースのモデリング
- パスワード再設定のメール送信
- パスワード再設定のメールとテンプレート
- 送信メールのテスト
- test/mailers/user_mailer_test.rb
- パスワード再設定用メイラーメソッドのテストを追加
- test/mailers/user_mailer_test.rb
- パスワードを再設定する
- editアクションで再設定
- app/views/password_resets/edit.html.erb
- パスワード再設定のフォーム
- <%= hidden_field_tag :email, @user.email %>
- editアクションとupdateアクションの両方でメールアドレスが必要
- 前者 (hidden_field_tag) ではメールアドレスがparams[:email]に保存されますが、後者(f.hidden_field :email, @user.email)ではparams[:user][:email] に保存されてしまう
- app/controllers/password_resets_controller.rb
- app/views/password_resets/edit.html.erb
- パスワードを更新する
- app/controllers/password_resets_controller.rb
- before_action :check_expiration, only: [:edit, :update]
- def update
- if params[:user][:password].empty?
- @user.errors.add(:password, :blank)
- elsif @user.update_attributes(user_params)
- else
- render 'edit'
- if params[:user][:password].empty?
- def user_params
- params.require(:user).permit(:password, :password_confirmation)
- def check_expiration
- if @user.password_reset_expired?
- app/models/user.rb
- def password_reset_expired?
- reset_sent_at < 2.hours.ago
- def password_reset_expired?
- app/controllers/password_resets_controller.rb
- パスワードの再設定をテストする
- rails generate integration_test password_resets
- test/integration/password_resets_test.rb
- パスワード再設定をテストする手順は、アカウント有効化のテスト (リスト 11.33) と多くの共通点がありますが、テストの冒頭部分には次のような違い
- 最初に「forgot password」フォームを表示して無効なメールアドレスを送信し、次はそのフォームで有効なメールアドレスを送信
- 後者ではパスワード再設定用トークンが作成され、再設定用メールが送信
- メールのリンクを開いて無効な情報を送信し、次にそのリンクから有効な情報を送信して、それぞれが期待どおりに動作
- assert_select "input[name=email][type=hidden][value=?]", user.email
- inputタグに正しい名前、type="hidden"、メールアドレスがあるかどうかを確認
- パスワード再設定をテストする手順は、アカウント有効化のテスト (リスト 11.33) と多くの共通点がありますが、テストの冒頭部分には次のような違い
- 演習
- def create_reset_digest
- update_columns(reset_digest: User.digest(reset_token), reset_sent_at: Time.zone.now)
- assert_match /expired/i, response.body
- 期限切れをテストする方法
- レスポンスの本文に「expired」という語があるかどうかでチェック
- 期限切れをテストする方法
- @user.update_attribute(:reset_digest, nil)
- パスワードの再設定に成功したらダイジェストをnilになるように変更
- test/integration/password_resets_test.rb
- test "password resets" doの最後
- assert_nil user.reload.reset_digest
- test "password resets" doの最後
- def create_reset_digest
- editアクションで再設定
- 本番環境でのメール送信 (再掲)
- 最後に
- サンプルアプリケーションのユーザー登録・ログイン・ログアウトの仕組みは、本物のアプリケーションと近いレベルに仕上がりました
- 本章のまとめ
- パスワードの再設定は Active Recordオブジェクトではないが、セッションやアカウント有効化の場合と同様に、リソースでモデル化できる
- Railsは、メール送信で扱うAction Mailerのアクションとビューを生成することができる
- Action MailerではテキストメールとHTMLメールの両方を利用できる
- メイラーアクションで定義したインスタンス変数は、他のアクションやビューと同様、メイラーのビューから参照できる
- パスワードを再設定させるために、生成したトークンを使って一意のURLを作る
- より安全なパスワード再設定のために、ハッシュ化したトークン (ダイジェスト) を使う
- メイラーのテストと統合テストは、どちらもUserメイラーの振舞いを確認するのに有用
- Mailgunを使うとproduction環境からメールを送信できる
- 証明: 期限切れの比較
Ch13 ユーザーのマイクロポスト
- Micropostモデル
- まずはMicropostリソースの最も本質的な部分を表現するMicropostモデルを作成するところから
- 基本的なモデル
- Micropostモデルは、マイクロポストの内容を保存するcontent属性と、特定のユーザーとマイクロポストを関連付けるuser_id属性の2つの属性だけを持ちます
- String型ではなくText型
- ある程度の量のテキストを格納するとき
- 投稿フォームにString用のテキストフィールドではなくてText用のテキストエリアを使うため、より自然な投稿フォームが実現
- Text型の方が将来における柔軟性に富んでいて、例えばいつか国際化をするときに、言語に応じて投稿の長さを調節することもできます
- Text型を使っていても本番環境でパフォーマンスの差は出ません
- rails generate model Micropost content:text user:references
- add_index :microposts, [:user_id, :created_at]
- user_idに関連付けられたすべてのマイクロポストを作成時刻の逆順で取り出しやすくなります
- 両方のキーを同時に扱う複合キーインデックス (Multiple Key Index) を作成
- Micropostのバリデーション
- test/models/micropost_test.rb
- 新しいMicropostの有効性に対するテスト
- Micropostモデルのバリデーションに対するテスト
- app/models/micropost.rb
- validates :user_id, presence: true
- validates :content, presence: true, length: { maximum: 140 }
- test/models/micropost_test.rb
- User/Micropostの関連付け
- Webアプリケーション用のデータモデルを構築するにあたって、個々のモデル間での関連付けを十分考えておくことが重要
- 紐付いているユーザーを通してマイクロポストを作成
- @micropost = @user.microposts.build(content: "Lorem ipsum")
- newメソッドと同様に、buildメソッドはオブジェクトを返しますがデータベースには反映されません。
- @user.microposts.buildのようなコードを使うためには、 UserモデルとMicropostモデルをそれぞれ更新して、関連付ける必要
- @micropost = @user.microposts.build(content: "Lorem ipsum")
- app/models/micropost.rb
- belongs_to :user
- app/models/user.rb
- has_many :microposts
- test/models/micropost_test.rb
- @micropost = @user.microposts.build(content: "Lorem ipsum")
- マイクロポストを改良する
- ユーザーのマイクロポストを特定の順序で取得できるようにしたり、マイクロポストをユーザーに依存させて、ユーザーが削除されたらマイクロポストも自動的に削除されるようにしていきます
- デフォルトのスコープ
- test/models/micropost_test.rb
- assert_equal microposts(:most_recent), Micropost.first
- test/fixtures/microposts.yml
- マイクロポスト用のfixture
- created_atカラム
- Railsによって自動的に更新されるため基本的には手動で更新できない
- fixtureファイルの中では更新可能
- app/models/micropost.rb
- default_scope -> { order(created_at: :desc) }
- Procやlambda (もしくは無名関数)と呼ばれるオブジェクトを作成する文法
- ->というラムダ式は、ブロック (4.3.2) を引数に取り、Procオブジェクトを返します
- このオブジェクトは、callメソッドが呼ばれたとき、ブロック内の処理を評価
- default_scope -> { order(created_at: :desc) }
- test/models/micropost_test.rb
- Dependent: destroy
- app/models/user.rb
- マイクロポストは、その所有者 (ユーザー) と一緒に破棄されることを保証する
- has_many :microposts, dependent: :destroy
- test/models/user_test.rb
- assert_difference 'Micropost.count', -1 do
- @user.destroy
- assert_difference 'Micropost.count', -1 do
- app/models/user.rb
- マイクロポストを表示する
- マイクロポストの描画
- rails generate controller Microposts
- app/views/microposts/_micropost.html.erb
- 1つのマイクロポストを表示するパーシャル
- <\li id="micropost-<%= micropost.id %>">
- 将来、JavaScriptを使って各マイクロポストを操作したくなったときなどに役立ちます
- app/controllers/users_controller.rb
- @microposts = @user.microposts.paginate(page: params[:page])
- app/views/users/show.html.erb
- マイクロポストをユーザーのshowページ (プロフィール画面) に追加
- 順序無しリストのulタグではなく、順序付きリストのolタグを使っている
- マイクロポストが特定の順序 (新しい→古い) に依存しているため
- <%= will_paginate @microposts %>
- 明示的に@microposts変数をwill_paginateに渡す必要
- インスタンス変数をUsersコントローラのshowアクションで定義
- マイクロポストのサンプル
- db/seeds.rb
- サンプルデータにマイクロポストを追加
- content = Faker::Lorem.sentence(5)
- users.each { |user| user.microposts.create!(content: content) }
- ループの順序に違和感があるかもしれませんが、これは14.3でステータスフィード (いわゆるタイムライン) を実装するときに役立ちます
- app/assets/stylesheets/custom.scss
- マイクロポスト用のCSS
- db/seeds.rb
- プロフィール画面のマイクロポストをテストする
- rails generate integration_test users_profile
- test/fixtures/microposts.yml
- ユーザーと関連付けされたマイクロポストのfixture
- <% 30.times do |n| %>
- micropost_<%= n %>:
- test/integration/users_profile_test.rb
- 今回のテストでは、プロフィール画面にアクセスした後に、ページタイトルとユーザー名、Gravatar、マイクロポストの投稿数、そしてページ分割されたマイクロポスト、といった順でテスト
- assert_match @user.microposts.count.to_s, response.body
- assert_selectよりもずっと抽象的なメソッド
- assert_selectではどのHTMLタグを探すのか伝える必要がありますが、assert_matchメソッドではその必要がない
- assert_select 'h1>img.gravatar'
- ネストした文法
- h1タグ (トップレベルの見出し) の内側にある、gravatarクラス付きのimgタグがあるかどうかをチェック
- 演習
- assert_select 'div.pagination', count: 1
- マイクロポストの描画
- マイクロポストを操作する
- データモデリングとマイクロポスト表示テンプレートの両方が完成したので、次はWeb経由でそれらを作成するためのインターフェイスに取りかかりましょう
- Micropostsリソースへのインターフェイスは、主にプロフィールページとHomeページのコントローラを経由して実行
- Micropostsコントローラにはnewやeditのようなアクションは不要
- createとdestroyがあれば十分
- config/routes.rb
- resources :microposts, only: [:create, :destroy]
- scaffoldが生成するような複雑なコードはほとんど不要になりました
- マイクロポストのアクセス制御
- test/controllers/microposts_controller_test.rb
- Micropostsコントローラの認可テスト
- ログイン済みかどうかを確かめるテストでは、Usersコントローラ用のテストがそのまま役に立ちます
- 正しいリクエストを各アクションに向けて発行し、マイクロポストの数が変化していないかどうか、また、リダイレクトされるかどうかを確かめればよい
- test "should redirect create when not logged in" do
- test "should redirect destroy when not logged in" do
- Micropostsコントローラの認可テスト
- app/controllers/application_controller.rb
- logged_in_userメソッドをApplicationコントローラに移す
- app/controllers/users_controller.rb
- Usersコントローラ内のlogged_in_userフィルターを削除
- app/controllers/microposts_controller.rb
- before_action :logged_in_user, only: [:create, :destroy]
- test/controllers/microposts_controller_test.rb
- マイクロポストを作成する
- ユーザーのサインアップを実装しました。マイクロポスト作成の実装もこれと似ています
- 主な違いは、別の micropost/new ページを使う代わりに、ホーム画面 (つまりルートパス) にフォームを置くという点
- ユーザーのログイン状態に応じて、ホーム画面の表示を変更
- app/controllers/microposts_controller.rb
- Micropostsコントローラのcreateアクション
- ユーザー用アクションと似ています
- 違いは、新しいマイクロポストをbuildするためにUser/Micropost関連付けを使っている点
- micropost_paramsでStrong Parametersを使っていることにより、マイクロポストのcontent属性だけがWeb経由で変更可能になっている
- params.require(:micropost).permit(:content)
- app/views/static_pages/home.html.erb
- Homeページ (/) にマイクロポストの投稿フォームを追加する
- <% if logged_in? %>
- <%= render 'shared/user_info' %>
- <%= render 'shared/micropost_form' %>
- app/views/shared/_user_info.html.erb
- サイドバーで表示するユーザー情報のパーシャル
- app/views/shared/_micropost_form.html.erb
- マイクロポスト投稿フォームのパーシャル
- app/controllers/static_pages_controller.rb
- @micropost = current_user.microposts.build if logged_in?
- app/views/shared/_error_messages.html.erb
- Userオブジェクト以外でも動作するようにerror_messagesパーシャルを更新する
- @userをobjectにする
- app/views/users/_form.html.erb
- @userをf.objectにする
- app/views/password_resets/edit.html.erb
- <%= render 'shared/error_messages', object: f.object %>
- vim test/integration/site_layout_test.rb
- 修正必要
- 演習
- app/views/static_pages/_home_logged_in.html.erb
- app/views/static_pages/_home_not_logged_in.html.erb
- フィードの原型
- すべてのユーザーがフィードを持つので、feedメソッドはUserモデルで作るのが自然
- app/models/user.rb
- マイクロポストのステータスフィードを実装するための準備
- Micropost.where("user_id = ?", id)
- 上の疑問符があることで、SQLクエリに代入する前にidがエスケープされるため、SQLインジェクション (SQL Injection) と呼ばれる深刻なセキュリティホールを避けることができます
- app/controllers/static_pages_controller.rb
- homeアクションにフィードのインスタンス変数を追加する
- @feed_items = current_user.feed.paginate(page: params[:page])
- app/views/shared/_feed.html.erb
- ステータスフィードのパーシャル
- <%= render @feed_items %>
- <%= will_paginate @feed_items %>
- app/views/static_pages/home.html.erb
- Homeページにステータスフィードを追加する
- <%= render 'shared/feed' %>
- app/controllers/microposts_controller.rb
- createアクションに空の@feed_itemsインスタンス変数を追加
- マイクロポストを削除する
- app/views/microposts/_micropost.html.erb
- マイクロポストのパーシャルに削除リンクを追加する
- <%= link_to "delete", micropost, method: :delete, data: { confirm: "You sure?" } %>
- app/controllers/microposts_controller.rb
- Micropostsコントローラのdestroyアクション
- ユーザーにおける実装 (リスト 10.59) とだいたい同じ
- 大きな違いは、admin_userフィルターで@user変数を使うのではなく、関連付けを使ってマイクロポストを見つけるようにしている点
- destroyメソッドではリダイレクトを使っている
- request.referrer || root_url
- request.referrer
- 一つ前のURLを返します
- request.referrerを使うことでDELETEリクエストが発行されたページに戻すことができるので、非常に便利
- redirect_back(fallback_location: root_url)と置き換えてもうまく動く
- request.referrer
- request.referrer || root_url
- app/views/microposts/_micropost.html.erb
- フィード画面のマイクロポストをテストする
- test/fixtures/microposts.yml
- 別のユーザーに所属しているマイクロポストを追加
- test/controllers/microposts_controller_test.rb
- 間違ったユーザーによるマイクロポスト削除に対してテスト
- log_in_as(users(:michael))
- micropost = microposts(:ants)
- assert_no_difference 'Micropost.count' do
- delete micropost_path(micropost)
- rails generate integration_test microposts_interface
- test/integration/microposts_interface_test.rb
- 演習
- test/integration/microposts_interface_test.rb
- assert_match "#{@user.microposts.count} microposts", response.body
- assert_match "1 micropost", response.body
- test/integration/microposts_interface_test.rb
- test/fixtures/microposts.yml
- マイクロポストの画像投稿
- 基本的な画像アップロード
- CarrierWaveという画像アップローダー
- Rails 5.2から標準となったActive Storage
- GemfileにCarrierWaveを追加する
- mini_magick gemとfog gemsも含めている
- CarrierWaveを導入すると、Railsのジェネレーターで画像アップローダーが生成できるようになります
- rails generate uploader Picture
- CarrierWaveでアップロードされた画像は、Active Recordモデルの属性と関連付けされているべき
- 関連付けされる属性には画像のファイル名が格納されるため、String型
- rails generate migration add_picture_to_microposts picture:string
- app/models/micropost.rb
- Micropostモデルに画像を追加する
- mount_uploader :picture, PictureUploader
- app/views/shared/_micropost_form.html.erb
- app/controllers/microposts_controller.rb
- params.require(:micropost).permit(:content, :picture)
- app/views/microposts/_micropost.html.erb
- マイクロポストの画像表示を追加
- <%= image_tag micropost.picture.url if micropost.picture? %>
- picture?という論理値を返すメソッド
- 画像用の属性名に応じて、CarrierWaveが自動的に生成してくれるメソッド
- picture?という論理値を返すメソッド
- 演習
- CarrierWaveという画像アップローダー
- 画像の検証
- app/uploaders/picture_uploader.rb
- app/models/micropost.rb
- 画像に対するバリデーションを追加
- validate :picture_size
- 独自のバリデーションを定義するために、今まで使っていたvalidatesメソッドではなく、validateメソッドを使っている
- ファイルサイズに対するバリデーションはRailsの既存のオプション (presenceやlengthなど) にはありません
- def picture_size
- if picture.size > 5.megabytes
- errors.add(:picture, "should be less than 5MB")
- if picture.size > 5.megabytes
- app/views/shared/_micropost_form.html.erb
- ファイルサイズをjQueryでチェック
- <%= f.file_field :picture, accept: 'image/jpeg,image/gif,image/png' %>
- <\script type="text/javascript">
- $('#micropost_picture').bind('change', function() {
- var size_in_megabytes = this.files[0].size/1024/1024;
- if (size_in_megabytes > 5) {
- alert('Maximum file size is 5MB. Please choose a smaller file.');
- $('#micropost_picture').bind('change', function() {
- ユーザーはアラートを無視してアップロードを強行する、といったことが可能
- 画像のリサイズ
- sudo apt install -y ImageMagick
- MiniMagickというImageMagickとRubyを繋ぐgemを使って、画像をリサイズ
- app/uploaders/picture_uploader.rb
- 画像をリサイズするために画像アップローダーを修正
- include CarrierWave::MiniMagick
- process resize_to_limit: [400, 400]
- storage :file
- def store_dir
- "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
- 演習
- config/initializers/skip_image_resizing.rb
- テスト時は画像のリサイズをさせない設定
- config/initializers/skip_image_resizing.rb
- 本番環境での画像アップロード
- 基本的な画像アップロード
- 最後に
- 本章のまとめ
- Active Recordモデルの力によって、マイクロポストも (ユーザーと同じで) リソースとして扱える
- Railsは複数のキーインデックスをサポートしている
- Userは複数のMicropostsを持っていて (has_many)、Micropostは1人のUserに依存している (belongs_to) といった関係性をモデル化した
- has_manyやbelongs_toを利用することで、関連付けを通して多くのメソッドが使えるようになった
- user.microposts.build(...)というコードは、引数で与えたユーザーに関連付けされたマイクロポストを返す
- default_scopeを使うとデフォルトの順序を変更できる
- default_scopeは引数に無名関数 (->) を取る
- dependent: :destroyオプションを使うと、関連付けされたオブジェクトと自分自身を同時に削除する
- paginateメソッドやcountメソッドは、どちらも関連付けを通して実行され、効率的にデータベースに問い合わせしている
- fixtureは、関連付けを使ったオブジェクトの作成もサポートしている
- パーシャルを呼び出すときに、一緒に変数を渡すことができる
- whereメソッドを使うと、Active Recordを通して選択 (部分集合を取り出すこと) ができる
- 依存しているオブジェクトを作成/削除するときは、常に関連付けを通すようにすることで、よりセキュアな操作が実現できる
- CarrierWaveを使うと画像アップロードや画像リサイズができる
- 本章のまとめ
Ch14 ユーザーをフォローする
- 他のユーザーをフォロー (およびフォロー解除) できるソーシャルな仕組みの追加と、フォローしているユーザーの投稿をステータスフィードに表示する機能を追加
- Relationshipモデル
- ユーザーをフォローする機能を実装する第一歩は、データモデルを構成すること
- データモデルの問題 (および解決策)
- followingテーブルと has_many関連付けを使って、フォローしているユーザーのモデリング
- 正しいモデルを見つけ出す方法の1つは、Webアプリケーションにおけるfollowingの動作をどのように実装するかをじっくり考えること
- まずは、フォローしているユーザーを生成するために、能動的関係に焦点を当てていきます
- followingテーブルをactive_relationshipsテーブルと見立ててみましょう
- 能動的関係も受動的関係も、最終的にはデータベースの同じテーブルを使うことになります。したがって、テーブル名にはこの「関係」を表す「relationships」を使いましょう
- rails generate model Relationship follower_id:integer followed_id:integer
- db/migrate/[timestamp]_create_relationships.rb
- relationshipsテーブルにインデックスを追加
- add_index :relationships, :follower_id
- add_index :relationships, :followed_id
- add_index :relationships, [:follower_id, :followed_id], unique: true
- あるユーザーが同じユーザーを2回以上フォローすることを防ぎます
- User/Relationshipの関連付け
- フォローしているユーザーとフォロワーを実装する前に、UserとRelationshipの関連付けを行います
- app/models/user.rb
- 能動的関係に対して1対多 (has_many) の関連付けを実装
- has_many :active_relationships, class_name: "Relationship", foreign_key: "follower_id", dependent: :destroy
- app/models/relationship.rb
- リレーションシップ/フォロワーに対してbelongs_toの関連付けを追加
- belongs_to :follower, class_name: "User"
- belongs_to :followed, class_name: "User"
- Relationshipのバリデーション
- 先に進む前に、Relationshipモデルの検証を追加して完全なものにしておきましょう
- test/models/relationship_test.rb
- Relationshipモデルのバリデーションをテスト
- def setup
- test "should be valid" do
- test "should require a follower_id" do
- test "should require a followed_id" do
- app/models/relationship.rb
- Relationshipモデルに対してバリデーションを追加
- Rails 5から必須ではなくなりました
- validates :follower_id, presence: true
- validates :followed_id, presence: true
- Relationshipモデルに対してバリデーションを追加
- test/fixtures/relationships.yml
- Relationship用のfixtureを空にする
- フォローしているユーザー
- Relationshipの関連付けの核心、followingとfollowersに取りかかります
- 今回はhas_many throughを使います
- app/models/user.rb
- Userモデルにfollowingの関連付けを追加する
- has_many :following, through: :active_relationships, source: :followed
- :sourceパラメーター (リスト 14.8) を使って、「following配列の元はfollowed idの集合である」ということを明示的にRailsに伝えます
- フォローしているユーザーを配列の様に扱えるようになりました
- followingメソッドで配列のように扱えるだけでも便利ですが、Railsは単純な配列ではなく、もっと賢くこの集合を扱っています
- following.include?(other_user)
- 実際にはデータベースの中で直接比較をするように配慮
- following.include?(other_user)
- 次に、followingで取得した集合をより簡単に取り扱うために、followやunfollowといった便利メソッドを追加
- 今回は、こういったメソッドはテストから先に書いていきます
- Webインターフェイスなどで便利メソッドを使うのはまだ先なので、すぐに使える場面がなく、実装した手応えを得にくいから
- 一方で、Userモデルに対するテストを書くのは簡単かつ今すぐできます
- test/models/user_test.rb
- “following” 関連のメソッドをテスト
- app/models/user.rb
- "following" 関連のメソッド
- 可能な限りself (user自身を表すオブジェクト) を省略している
- def follow(other_user)
- following << other_user
- def unfollow(other_user)
- active_relationships.find_by(followed_id: other_user.id).destroy
- def following?(other_user)
- following.include?(other_user)
- 今回は、こういったメソッドはテストから先に書いていきます
- フォロワー
- リレーションシップというパズルの最後の一片は、user.followersメソッドを追加すること
- user.followingメソッドと対
- app/models/user.rb
- 受動的関係を使ってuser.followersを実装
- この実装はリスト 14.8とまさに類似
- 参照先 (followers) を指定するための:sourceキーを省略してもよかった
- :followers属性の場合、Railsが「followers」を単数形にして自動的に外部キーfollower_idを探してくれるから
- test/models/user_test.rb
- followersに対するテスト
- assert archer.followers.include?(michael)を追加
- リレーションシップというパズルの最後の一片は、user.followersメソッドを追加すること
- [Follow] のWebインターフェイス
- この節では、モックアップで示したようにフォロー/フォロー解除の基本的なインターフェイスを実装
- また、フォローしているユーザーと、フォロワーにそれぞれ表示用のページを作成
- フォローのサンプルデータ
- 先にサンプルデータを自動作成できるようにしておけば、Webページの見た目のデザインから先にとりかかることができ、バックエンド機能の実装を後に回すことができます
- db/seeds.rb
- サンプルデータにfollowing/followerの関係性を追加
- users = User.all
- user = users.first
- following = users[2..50]
- followers = users[3..40]
- following.each { |followed| user.follow(followed) }
- followers.each { |follower| follower.follow(user) }
- 統計と [Follow] フォーム
- config/routes.rb
- Usersコントローラにfollowingアクションとfollowersアクションを追加する
- resources :users do
- member do
- get :following, :followers
- member do
- この場合のURLは /users/1/following や /users/1/followers のようになる
- memberメソッドを使うとユーザーidが含まれているURLを扱うようになります
- idを指定せずにすべてのメンバーを表示するには、次のようにcollectionメソッド
- collection do
- get :rabbits
- /users/rabbitsに対応
- collection do
- ルーティングを定義したので、統計情報のパーシャルを実装する準備が整いました
- app/views/shared/_stats.html.erb
- フォロワーの統計情報を表示するパーシャル
- このパーシャルでは、divタグの中に2つのリンクを含めるようにします
- <% @user ||= current_user %>
- このパーシャルはプロフィールページとHomeページの両方に表示
- @user.following.count
- @user.followers.count
- <\strong id="following" class="stat">
- こうしておくと、14.2.5でAjaxを実装するときに便利
- 一意のidを指定してページ要素にアクセス
- app/views/static_pages/_home_logged_in.html.erb
- Homeページにフォロワーの統計情報を追加する
- <%= render 'shared/stats' %>
- app/assets/stylesheets/custom.scss
- Homeページのサイドバー用のSCSS
- app/views/users/_follow_form.html.erb
- フォロー/フォロー解除フォームのパーシャル
- <% unless current_user?(@user) %>
- <% if current_user.following?(@user) %>
- <%= render 'unfollow' %>
- <% else %>
- <%= render 'follow' %>
- config/routes.rb
- Relationshipリソース用のルーティングを追加する
- resources :relationships, only: [:create, :destroy]
- app/views/users/_follow.html.erb
- ユーザーをフォローするフォーム
- <%= form_for(current_user.active_relationships.build) do |f| %>
- <%= hidden_field_tag :followed_id, @user.id %>
- <%= f.submit "Follow", class: "btn btn-primary" %>
- app/views/users/_unfollow.html.erb
- ユーザーをフォロー解除するフォーム
- <%= form_for(current_user.active_relationships.find_by(followed_id: @user.id), html: { method: :delete }) do |f| %>
- <%= f.submit "Unfollow", class: "btn" %>
- これらの2つのフォームの主な違いは、リスト 14.21は新しいリレーションシップを作成するのに対し、リスト 14.22は既存のリレーションシップを見つけ出すという点
- app/views/users/show.html.erb
- プロフィールページにフォロー用フォームとフォロワーの統計情報を追加
- <%= render 'shared/stats' %>
- <%= render 'follow_form' if logged_in? %>
- 演習
- test/integration/site_layout_test.rb
- test/integration/users_profile_test.rb
- assert_match @user.active_relationships.count.to_s, response.body
- assert_match @user.passive_relationships.count.to_s, response.body
- [Following] と [Followers] ページ
- test/controllers/users_controller_test.rb
- フォロー/フォロワーページの認可をテスト
- 前回のアクセス制御と同様に、まずはテストから
- app/controllers/users_controller.rb
- app/views/users/show_follow.html.erb
- フォローしているユーザーとフォロワーの両方を表示するshow_followビュー
- 現在のユーザーを一切使っていない
- 他のユーザーのフォロワー一覧ページもうまく動きます
- rails generate integration_test following
- HTML構造を網羅的にチェックするテストは壊れやすく、生産性を逆に落としかねない
- 基本的なテストだけに留めており、網羅的なテストにはしていません
- 今回は、正しい数が表示されているかどうかと、正しいURLが表示されているかどうかの2つのテストを書きます
- test/fixtures/relationships.yml
- following/followerをテストするためのリレーションシップ用fixture
- test/integration/following_test.rb
- following/followerページのテスト
- assert_not @user.following.empty?
- 次のコードを確かめるためのテスト
- @user.following.each do |user|
- assert_select "a[href=?]", user_path(user)
- もし@user.following.empty?の結果がtrueであれば、assert_select内のブロックが実行されなくなるため、その場合においてテストが適切なセキュリティモデルを確認できなくなることを防いでいます
- [Follow] ボタン (基本編)
- rails generate controller Relationships
- フォローとフォロー解除はそれぞれリレーションシップの作成と削除に対応しているため、まずはRelationshipsコントローラが必要
- 最初にテストを書き、それをパスするように実装することでセキュリティモデルを確立
- test/controllers/relationships_controller_test.rb
- リレーションシップの基本的なアクセス制御に対するテスト
- コントローラのアクションにアクセスするとき、ログイン済みのユーザーであるかどうかをチェック
- もしログインしていなければログインページにリダイレクトされるので、Relationshipのカウントが変わっていないことを確認
- app/controllers/relationships_controller.rb
- logged_in_userフィルターをRelationshipsコントローラのアクションに対して追加
- リレーションシップのアクセス制御
- Relationshipsコントローラ
- フォーム (リスト 14.21とリスト 14.22) から送信されたパラメータを使って、followed_idに対応するユーザーを見つけてくる必要
- その後、見つけてきたユーザーに対して適切にfollow/unfollowメソッド (リスト 14.10) を使います
- [Follow] ボタン (Ajax編)
- Ajaxを使えば、Webページからサーバーに「非同期」で、ページを移動することなくリクエストを送信することができます
- form_forをform_for ..., remote: trueに置き換えるだけ
- app/views/users/_follow.html.erb
- <%= form_for(current_user.active_relationships.build, remote: true) do |f| %>
- app/views/users/_unfollow.html.erb
- <%= form_for(current_user.active_relationships.find_by(followed_id: @user.id), html: { method: :delete }, remote: true) do |f| %>
- JavaScriptを前面に出すべからずという哲学
- 対応するRelationshipsコントローラを改造して、Ajaxリクエストに応答できるようにしましょう
- リクエストの種類によって応答を場合分けするときは、respond_toメソッド
- app/controllers/relationships_controller.rb
- ブラウザ側でJavaScriptが無効になっていた場合 (Ajaxリクエストが送れない場合) でもうまく動くようにします
- config/application.rb
- config.action_view.embed_authenticity_token_in_remote_forms = true
- Ajaxリクエストを受信した場合は、Railsが自動的にアクションと同じ名前を持つJavaScript用の埋め込みRuby (.js.erb) ファイル (create.js.erbやdestroy.js.erbなど) を呼び出す
- これらのファイルではJavaScriptと埋め込みRuby (ERb) をミックスして現在のページに対するアクションを実行する
- JS-ERbファイルの内部では、DOM (Document Object Model) を使ってページを操作するため、RailsがjQuery JavaScriptヘルパーを自動的に提供
- 純粋なJavaScriptと異なり、JS-ERbファイルでは組み込みRuby (ERb) が使えます
- app/views/relationships/create.js.erb
- JavaScriptと埋め込みRubyを使ってフォローの関係性を作成する
- $("#follow_form").html("<%= escape_javascript(render('users/unfollow')) %>");
- $("#followers").html('<%= @user.followers.count %>');
- app/views/relationships/destroy.js.erb
- Ruby JavaScript (RJS) を使ってフォローの関係性を削除する
- $("#follow_form").html("<%= escape_javascript(render('users/follow')) %>");
- $("#followers").html('<%= @user.followers.count %>');
- フォローをテストする
- test/integration/following_test.rb
- [Follow] / [Unfollow] ボタンをテスト
- assert_difference '@user.following.count', 1 do
- post relationships_path, params: { followed_id: @other.id }, xhr: true
- xhr (XmlHttpRequest) というオプションをtrueに設定すると、Ajaxでリクエストを発行するように変わります
- assert_difference '@user.following.count', -1 do
- delete relationship_path(relationship), xhr: true
- それぞれ従来どおりのテストと、Ajax用のテストの2つ
- ステータスフィード
- 現在のユーザーにフォローされているユーザーのマイクロポストの配列を作成し、現在のユーザー自身のマイクロポストと合わせて表示
- このセクションを通して、複雑さを増したフィードの実装に進んでいきます
- これを実現するためには、RailsとRubyの高度な機能の他に、SQLプログラミングの技術も必要
- 動機と計画
- この目的は、現在のユーザーによってフォローされているユーザーに対応するユーザーidを持つマイクロポストを取り出し、同時に現在のユーザー自身のマイクロポストも一緒に取り出す
- まずはテストから書いていく
- このテストで重要なことは、フィードに必要な3つの条件を満たすこと
- 1) フォローしているユーザーのマイクロポストがフィードに含まれている
- 2) 自分自身のマイクロポストもフィードに含まれている
- 3) フォローしていないユーザーのマイクロポストがフィードに含まれていない
- このテストで重要なことは、フィードに必要な3つの条件を満たすこと
- test/models/user_test.rb
- ステータスフィードのテスト
- assert michael.feed.include?(post_following)
- assert michael.feed.include?(post_self)
- assert_not michael.feed.include?(post_unfollowed)
- フィードを初めて実装する
- micropostsテーブルから、あるユーザー (つまり自分自身) がフォローしているユーザーに対応するidを持つマイクロポストをすべて選択 (select) する
- SQLがINというキーワードをサポートしていることを前提にしています (大丈夫、実際にサポートされています)。このキーワードを使うことで、idの集合の内包 (set inclusion) に対してテストを行えます
- フォローされているユーザーに対応するidの配列が必要
- これを行う方法の1つは、Rubyのmapメソッドを使うことです。このメソッドはすべての「列挙可能 (enumerable)」なオブジェクト (配列やハッシュなど、要素の集合で構成されるあらゆるオブジェクト) で使えます
- [1, 2, 3, 4].map(&:to_s)
- アンパサンド (Ampersand) & と、メソッドに対応するシンボルを使った短縮表記 (4.3.2) が使えます
- [1, 2, 3, 4].map(&:to_s).join(', ')
- User.first.following_ids
- following_idsメソッドは、has_many :followingの関連付けをしたときにActive Recordが自動生成したもの
- user.followingコレクションに対応するidを得るためには、関連付けの名前の末尾に_idsを付け足すだけで済みます
- User.first.following_ids.join(', ')
- 実際にSQL文字列に挿入するときは、このように記述する必要はありません。実は、?を内挿すると自動的にこの辺りの面倒を見てくれます
- さらに、データベースに依存する一部の非互換性まで解消
- Micropost.where("user_id IN (?) OR user_id = ?", following_ids, id) ← これでOK
- app/models/user.rb
- とりあえず動くフィードの実装
- Micropost.where("user_id IN (?) OR user_id = ?", following_ids, id)
- サブセレクト
- フォローしているユーザー数に応じてスケールできるように、ステータスフィードを改善
- app/models/user.rb
- Micropost.where("user_id IN (:following_ids) OR user_id = :user_id, following_ids: following_ids, user_id: id)
- 疑問符を使った文法も便利ですが、同じ変数を複数の場所に挿入したい場合は、置き換え後の文法を使う方がより便利
- following_ids = "SELECT followed_id FROM relationships WHERE follower_id = :user_id"
- Micropost.where("user_id IN (#{following_ids}) OR user_id = :user_id", user_id: id)
- 演習
- test/integration/following_test.rb
- assert_match CGI.escapeHTML(micropost.content), response.body
- test/integration/following_test.rb
- 最後に
- ステータスフィードが追加され、Railsチュートリアルのサンプルアプリケーションがとうとう完成しました
- このサンプルアプリケーションには、Railsの主要な機能 (モデル、ビュー、コントローラ、テンプレート、パーシャル、beforeフィルター、バリデーション、コールバック、has_many/belongs_to/has_many through関連付け、セキュリティ、テスティング、デプロイ) が多数含まれています
- サンプルアプリケーションの機能を拡張する
- この項で提案する拡張機能 (検索、返信、メッセージ機能など) のほとんどは、Webアプリケーションでは一般的な機能
- 一般的なアドバイス
- Ruby/Railsのバージョンを上げてみよう
- 返信機能
- @replyは受信者のフィードと送信者のフィードにのみ表示されるようにします
- これを実装するには、micropostsテーブルのin_reply_toカラムと、追加のincluding_repliesスコープをMicropostモデルに追加する必要がある
- スコープの詳細については、RailsガイドのActive Record クエリインターフェイスを参照
- このサンプルアプリケーションではユーザー名が重なり得るので、ユーザー名を一意に表す方法も考えなければならない
- 1つの方法は、idと名前を組み合わせて@1-michael-hartlのようにすること
- もう1つの方法は、ユーザー登録の項目に一意のユーザー名を追加し、@replyで使えるようにすること
- メッセージ機能
- ダイレクトメッセージを行える機能
- Messageモデルと、新規マイクロポストにマッチする正規表現が必要
- フォロワーの通知
- ユーザーに新しくフォロワーが増えたときにメールで通知する機能
- メールでの通知機能をオプションとして選択可能にし、不要な場合は通知をオフにできるようにしてみましょう
- メール周りで分からないことがあったら、RailsガイドのAction Mailerの基礎にヒントがないか調べてみましょう
- RSSフィード
- REST API
- 検索機能
- ユーザーを検索
- マイクロポストを検索
- まずは自分自身で検索機能に関する情報を探してみましょう。難しければ、@budougumi0617 さんの簡単な検索フォームの実装例を参考にしてください
- 他の拡張機能
- 読み物ガイド
- 本章のまとめ
- has_many :throughを使うと、複雑なデータ関係をモデリングできる
- has_manyメソッドには、クラス名や外部キーなど、いくつものオプションを渡すことができる
- 適切なクラス名と外部キーと一緒にhas_many/has_many :throughを使うことで、能動的関係 (フォローする) や受動的関係 (フォローされる) がモデリングできた
- ルーティングは、ネストさせて使うことができる
- whereメソッドを使うと、柔軟で強力なデータベースへの問い合わせが作成できる
- Railsは (必要に応じて) 低級なSQLクエリを呼び出すことができる
- 本書で学んだすべてを駆使することで、フォローしているユーザーのマイクロポスト一覧をステータスフィードに表示させることができた
- あとがき
おまけ
Ch15 React
- webpackの導入
- Webpacker gemの追加
- Asset Pipelineでも良いのではと思われると思いますが、webpackを利用すると他にも色々な機能が利用できます
- JavaScript以外で書かれたコードを、ブラウザが解釈できるJavaScriptに変換してあげる必要があり、その役割もwebpackが担ってくれます
- Reactなどをnodeモジュールとして管理したいという場合にも、webpackを利用することで簡単に管理できるようになります
- gem 'webpacker', '~> 3.0'
- Asset Pipelineでも良いのではと思われると思いますが、webpackを利用すると他にも色々な機能が利用できます
- webpackerのインストール
- rails webpacker:install
- webpackerのインストールタスクで生成されたファイルの説明
- git status
- package.json
- .babelrc
- app/javascript/*
- bin/*
- config/*
- webpackの利用
- app/javascript/packs/application.jsをload
- bin/webpackでtranspile
- トランスパイルされたファイルの読み込み
- app/views/layouts/_head.html.erb
- <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
- webpack-dev-serverを立ち上げておくことで、ファイルの変更の度にコンソールから$ bin/webpackを実行しなくても自動でトランスパイルが実行
- app/views/layouts/_head.html.erb
- Webpacker gemの追加
- Reactの導入
- Reactのインストール
- rails webpacker:install:react
- bin/webpack
- Reactコンポーネントの利用
- app/views/layouts/_head.html.erb
- <%= javascript_pack_tag 'hello_react', 'data-turbolinks-track': 'reload' %>
- ReactDOM.renderは2つの引数をとり、1つ目の引数がコンポーネント、2つ目の引数がそのコンポーネントが表示されるDOM要素
- app/views/layouts/_head.html.erb
- Reactコンポーネントを自由に表示する
- react-railsというgemを導入
- rails generate react:install
- spring stopしてからやらないとエラーになった
- 依存するライブラリがインストールされたり、app/javascript/packs/application.jsに新しくreact-railsの設定が追記
- Helloコンポーネントをapp/javascripts/components/hello_react.jsxに移動
- ReactDOM.render()はreact-railsが実行してくれるのでコメントアウトして、export default Helloを追記
- export default Hello
- app/javascript/packs/application.jsに追記されたreact-railsの設定で、コンポーネントを明示的にimportする必要はありません
- react-railsが用意したヘルパメソッドに、app/javascripts/components以下に置かれたコンポーネントのファイル名を指定してあげることでインポート
- app/views/layouts/_head.html.erb
- hello_reactの行を削除
- app/views/layouts/application.html.erb
- <%= react_component("hello_react", name: "React") %>
- Reactのインストール
- カウンターコンポーネントを作る
- app/javascript/components/counter.jsx
- app/views/layouts/application.html.erb
- <%= react_component("counter") %>
- フォローボタンのコンポーネントを作る
- フォローボタンのコンポーネント雛形作成
- app/views/users/_follow_form.html.erb
- <%= react_component("follow_button", user: @user.attributes, relationship: current_user.active_relationships.find_by(followed_id: @user.id)&.attributes) %>
- app/javascript/components/follow_button.jsx
- app/views/users/_follow_form.html.erb
- コンポーネントから通信する
- チャタリングを防止する
- リクエスト中は、ボタンの操作を行えないように、stateにリクエスト中のフラグを用意
- app/javascript/components/follow_button.jsx
- constructor(props) {
- this.state = {
- loading: false,
- this.state = {
- follow = () => { // unfollowも同じ
- loading: true
- }).then((response) => {
- loading: false,
- <\button className={ className } onClick={ this.handleClickFollowButton } disabled={ this.state.loading }>
- constructor(props) {
- ボタンの見た目を良くする
- yarn add classnames
- classnamesというモジュールを導入して、状態によるクラス名の変更を簡単にしましょう
- yarn add classnames
- フォローボタンのコンポーネント雛形作成
- Reduxの紹介
- Reduxとは
- 準備
- yarn add redux react-redux
- Reduxでカウンターを作成する
- Action
- ActionCreator
- Reducer
- Store
- ContainerComponent
- Component
- Storeとコンポーネントを接続する
- Storeを渡すのはContainerComponentではなく普通のComponentがいい
- 最後に
- 本章のまとめ
- webpackergemを利用することで、簡単にReactを利用した開発を行える
- JavaScriptは進化し続けていて、最新の記法で記述したJavaScriptをブラウザが理解できる形式に変換するためにトランスパイラを利用する ― JavaScriptの最新の記法は謎めいているが、慣れると非常に協力である
- Reactを利用すると、HTMLとJSをコンポーネントという単位でまとめるためて記述することができる
- Reactは渡されるpropsの変化や内部のstateが更新される度にrenderを呼び出し、propsやstateに応じたHTMLを返す
- JavaScriptからAjax通信を行う場合は、CSRFトークンをリクエストに含める必要がある
- ReactコンポーネントのHTMLのクラスを条件によって変えたい場合は、classnamesを利用する
- Reduxは登場人物(ファイル)が多いだけで、各登場人物の役割は単純である
- Reduxを利用するとstateがStoreで一元管理されるので、状態の見通しが良くなる
- Reduxは必ず利用する必要はなく、コンポーネントが複雑なstateを持ち、それらが他のコンポーネントにも影響を与える場合に利用を検討すべき
- 本章のまとめ
- <% if current_user.following?(@user) %>
- config/routes.rb
『フロントエンド開発入門: プロフェッショナルな開発ツールと設計・実装』学習メモ
Part 1 導入編 何故使うかを知る
Ch01 frontend history
- static UI → dynamic UI
- DOM utilityを提供したjQuery
- cross browserや下位互換性を吸収したAPI
- Ajax
- XMLHttpRequest objectを隠蔽し冗長な通信記述を簡略化できる
- Shorthand
- 簡易的なanimationの活用
- pluggable API
- 自身のobjectを拡張できる
- → 周辺のlibraryやpluginを充実させるecosystemを築く
- 自身のobjectを拡張できる
- browserで動作する画面を取り扱う技術者にとって,JavaScriptはinteractionや機能実現のための手段として重要な言語となってきた
- SassといったCSS meta language
- DOM utilityを提供したjQuery
- frontend_engineerという専門職
- single_page_applicationの誕生
- 内部では,applicationの状態管理やroutingのみならず,routingごとのcontrollerやdata_modelingなどが盛り込まれるようになる
- → AngularJSやBackbone.jsなどのsingle_page_applicationを実現するためのall_in_one frameworkが使われだす
- client MVCやrouting, data_modelingにはsoftwareのdesign_patternへの理解が求められる
- → frontend engineer
- single_page_applicationの誕生
- Node.jsによる開発基盤の構築
- server_side JavaScriptとしてNode.jsが発表
- → bundled package managerであるnpmによって開発におけるecosystemが急速に充実する
- → frontendでのlibraryのversion_controlや開発におけるさまざまなタスクの自動化が可能になる
- CommonJS: module system
- ECMAScript規格更新に伴う周辺事情の活性化
- ES6からES5への変換tool: 6to5 → Babelに名称変更
- 新しい仕様をcompileするためのpluginを開発することで,健全にcodebaseに最新仕様を持ち込むことができる
- 現時点でfrontendが誇るすばらしいecosystem
- 止まらないfrontend
- focus
- 昨今の技術要素が何のために必要なのか,何を解決するのか
- 実践的な内容で技術要素を取り扱い必要なことを必要なタイミングで学ぶ
- focus
Ch02 frontend_engineerに求められるskill
- frontend_engineerが取り組む実務
- JavaScriptの成長と要求の変化
- 変容する中で維持すべきdeveloperの姿勢
Ch03 frontendにおける一般的なtools
- Node.jsとその周辺のecosystem
- Node.js: asyncのevent_driven_modelを採用したserver_side向けのJavaScript
- server_sideのscript_languageであることに加えて,開発環境において必要なtoolやlibraryを動作させるために必要不可欠
- core
- V8と,libuvというasync I/OをsupportするC言語libraryなどから構成され,binary化されて配信される
- libuv → async event_driven JavaScript runtimeを実現
- JavaScriptはsingle thread
- main processを進めながら,callback queueとしてasync processを積み上げる
- async/await
- JavaScriptはsingle thread
- package_manager
- npm: JavaScript用のpackage_managerのCLI
- packageを格納するregistryを提供・開発する会社名でもある
- Yarn: npmと同類.Facebookが開発.
- version lock file
- workspace
- 別applicationどうしの依存関係を単一にするarchitectureであるmonorepoを実現する仕組みを提供する
- npm: JavaScript用のpackage_managerのCLI
- Node.jsがもたらす恩恵
- Node.js: frontendのtool_chain, 開発環境構築を支える根幹そのもの
- server_side languageとしても有能
- event_loopやnon_blockingという特性を活かして,高traffic requestをさばいてもperformanceが低下するcaseが少ない
- Backends_For_Frontends layerとbrowserとで同じ言語であるJavaScriptを扱うこともできる
- 共通言語 → 一気通貫のcontextをdevelopment_memberに提供し,言語別に切り替えるストレスなく,継続的なdevelopment processを可能にする
- 技術選定の主軸となるcaseもいくつか存在し,組織的な戦略として候補になりえる言語
- Node.jsにより,JavaScriptが戦術・戦略として価値のある言語になるまで発展
- Node.js: frontendのtool_chain, 開発環境構築を支える根幹そのもの
- Node.js: asyncのevent_driven_modelを採用したserver_side向けのJavaScript
- compiler, module_bundler
- 言語仕様を吸収し,解釈可能な状態で展開・連結する変換器
- Babel
- 構文解析で下位仕様の構文へと変換
- webpack
- file間のmodule解決を行い1ファイルに連結させる機能を提供
- Babel
- compiler: Babel
- 新しいJavaScript構文を環境に合わせて解釈可能な下位構文へとdown_compileするRole
- ECMAScriptが年次策定していく新しい仕様と大きく関係
- T39という団体により策定
- 構成
- 構文解析のためのparserやcompilerとしてのcore機能,それらを補うhelperや変換のためのpluginなど細かなpackage群による
- babel/preset-envという主要な変換用pluginがpreinstallされたpresetを使うcaseが多い
- module_bundler: webpack
- 言語仕様の一部であるmodule機構を実装していない下位環境においての再現(emulate)を担当
- 設定ファイルの柔軟性やloader, pluginというAPIを提供し強力な結合・連結機能を提供することで,周辺のecosystemも充実し,activeに開発が続けられている
- ParcelやRollup.jsなども同じ役割
- module仕様であるES Modulesの実装がすべてのbrowser環境で整うには時間がかかることも,module_bundlerが利用される理由の1つ
- JavaScript module_system
- Node.jsが採用したCommonJSとES2015で採択承認されたES Modulesの2つがあるが,Node.jsはES Modulesへの移行を試験的に実装している
- Can I use: browserでの実装状況を調べるためのDBのdefacto standard
- cross browserの課題解決を目的としたJavaScript toolが利用していることも多い
- Babelは,webpackにおいてbabel-loaderとして変換のRoleを担う
- → ほとんどはwebpackをfrontend code compile環境として選択する
- Babel, webpackが解決すること
- Babelには正式な仕様以外のpluginが作成・使用されてしまったという経緯があることに注意
- → babel/preset-envは,ECMAScriptと歩みを合わせ,developerが実際に取り組むようなcross platformやbrowser要件の問題に対して最適な機能を提供
- optionで互換性をどこまで保って変換するかのtargetを指定可能にする
- → babel/preset-envは,ECMAScriptと歩みを合わせ,developerが実際に取り組むようなcross platformやbrowser要件の問題に対して最適な機能を提供
- まとめ
- 開発において,年次策定される言語仕様の進化と並行しながら開発できる環境を提供できる
- JavaScriptにおけるnative module仕様をemulateし現実的に課題を解決できる
- → 開発において変更可用性,scalingの担保をもたらす恩恵となる
- Babel, webpackを使う場面がなくなっても,codebaseが外的環境の変化に柔軟である点が重要
- Babelには正式な仕様以外のpluginが作成・使用されてしまったという経緯があることに注意
- 言語仕様を吸収し,解釈可能な状態で展開・連結する変換器
- JavaScript代替言語: TypeScript
- AltJSと呼ばれる
- CoffeeScript (with Ruby on Rails) → あまり使われなくなった
- Dart (by Google, with Flutter) → JavaScriptにcompile可能
- Reason (by Facebook)やElmは,TypeScriptと同じく静的型付言語としての特性を持つ
- TypeScript: あくまでJavaScriptの構文を維持し,型annotationなどで拡張された静的型付言語
- 自身が持つ型systemを省いた記述も可能
- JavaScriptにとってのsuper_setとなる存在
- TypeScriptの特徴
- 静的型付言語で,型checkとJavaScriptへのcompileを担う
- 型定義の場所
- interface.typeと呼ばれる型宣言
- 型checkとcompile errorによって,applicationの堅牢性とcodebaseの健全性に恩恵がある
- compilerとしてのTypeScript
- JavaScriptへの出力
- Babelのecosystemとは違った,独自の形でECMAScriptの新しい構文をfollowする
- あくまでもJavaScriptへの変換
- decoratorsは技術的負債になりうる
- ECMAScriptとRoleが重複するものもある
- TypeScriptによって解決できること
- 3 points
- compile errorによって,JavaScriptではfollowできない.script実行前の未然検知を可能にする
- IDEと組み合わせることで型補完やerrorを表示させ開発体験を向上させられる
- ECMAScriptとの歩みの中でBabelとは別のecosystemを築いている.早期的に取り込まれた構文利用には注意する
- applicationの堅牢性を高めたり,team developmentを行う上でcodebaseの健全性を維持することに役立つ
- 意図しない記述を許容しないという強力な安全策になる
- 3 points
- AltJSと呼ばれる
- framework/view library: Vue.js, Angular, React
- 中規模以上のapplication開発においてbackend_APIとの連携を主体に据えたviewの構築が必要であったり,user_experienceを高めるためにstressない画面遷移が求められたり,開発序盤における技術選定の中でframeworkやview libraryの選択が求められることがある
- 中長期的な運用や保守の中でproduct, applicationの特性を踏まえながら技術選定をすることは開発の現場では特に重要
- 混とんとしていた変化の時期と比べ,背後を支えるecosystemが盤石となったことも開発者の学習コストを下げている
- frameworkのbuild機構にはおよそwebpackが存在する
- 過渡期はRequireJS, SystemJSをruntimeで実現する手法もあれば,CommonJSの記法を使いながらBrowserifyがWebのためにバンドルを行う手法もあった
- → 今はapplicationと無関係なことに頭を使う必要がない
- ↑ ECMAScriptによって規格化されたES Modulesの記法で,webpackがそれをemulateするようbundleすれば十分
- frameworkのbuild機構にはおよそwebpackが存在する
- Vue.js
- PHPのbackend frameworkであるLaravelの作者が気に入って,LaravelのCLIによって新規applicationが作成される際,file setにVue.jsが内包されるようになった
- 公式サイトの説明
- UIを構築するためのprogressive framework
- monolithic frameworkと異なり,Vueは少しずつ適用していけるように設計されている
- Single_File_Componentという単一のcomponentにDOM template, style, scriptをまとめて記述しcompileする仕組みを持つことで,すべてJavaScriptのcontextに載せ替えることも可能になる
- 少しずつ適用し,小さなcomponent群を集約させながらscaleできるframework
- HTML template: \<template>
- script template: \<script>
- CSS: \<style>
- scoped
- CSSのscopeをcomponent単位で閉じるapproachは,JavaScriptのcontextで完結するlibraryにはよく見られる
- scoped
- まとめ
- communityの支えるlibraryが数多く存在し実績も多くあるので手に取りやすい
- 状態管理libraryとしてのVuexや,SSRやroutingなどを網羅したNuxt.jsなどもある
- component単位で責務をまとめSingle_File_Componentというファイルに集約することで,視覚的・機能的にcomponentが果たすべき領域を捉えられるようになっている
- 別々に分離されたlayer・責務をcomponentという単位で1つのlayerに統合している点が大きな特徴
- Angular
- by Google
- HTMLとTypeScriptでsingle_page_client_applicationを開発するためのplatform, framework
- version 1.xとして開発されたAngularJSとは互換がない
- 開発におけるひととおりのものがそろっている
- SPAのためのclient routingからfetchしresourceを取得するHttpClientなどapplication開発に必要となるものをmoduleとして最初から持っている
- command line toolによる開発一式も完備し,codebaseの足場を作るscaffoldやtest runner/unit testの仕組みまで網羅
- frameworkの概念やarchitectureは盤石 → file 構成や管理方法,細かなtool chainを組み合わせ選定し調査するといった考慮はほとんど不要
- 公式documentにより指南されたfile構成,style guideが一番参考になる
- software designやarchitectureの文脈で多少前提知識が必要
- decoratorによるDependency Injection
- RxJSでの外部通信など非同期の扱い Observablesといったarchitectureやstreamというsequenceについての理解も求められる
- webpackとの組み合わせで起点となるfile: entry fileと呼ぶ
- 例ではmain.ts
- 起点として階層別にcomponent群を集約する
- NgModule: app.modules.ts
- applicationに必要な外部moduleを整理・集約し,下層layerに分配するRole
- applicationには少なくとも1つのroot moduleが必要になる
- Component: app.component.ts
- Angular componentのinstanceを作成しそれが扱う変数やmethodが指定されるほかdecoratorによる記述がある
- Vueにおけるdata modelやevent handlerなどをobjectとして定義した形と似ている
- HTML: app.component.html
- 一部にMustache構文
- CSS: app.component.css
- 明示なしでstyle定義がcomponent内にとどまる
- まとめ
- Vue.jsと同じく,component baseのUI構築といった構成
- style guideで理想となる構造を示している
- 初手から学習領域が広くなる
- DI, HttpClientなど
- React
- by Facebook
- UI構築のためのJavaScript library
- UI構築にのみ関心
- simple view library
- minimum
- JSX: JavaScriptの拡張としての表現
- virtual_DOMという考え方
- 宣言的な記述がlogicによってのみ更新される
- Vue.js, Angularとの違い・共通点
- ReactはあくまでJavaScriptの文脈上ですべてを成立
- 木構造という概念を意識することなく宣言的なUI表現であるJSXを記述
- 実際にbrowserで表現される際のUIの一貫性を保てる
- 3 points
- all_in_one frameworkではなくUI表現にのみ関心を寄せたlibrary
- HTML templateではなくJavaScript構文で記述
- JavaScript context内で完結し変更差分を意識せずにUIに集中できる
- component指向のframework/libraryであること
- framework/libraryによりfrontend developmentはcodebaseの一貫性を保てる
- frameworkの選定には,user数,communityがactiveであることも重要だが,teamの学習状況やmember のskillにあったlibraryを選定することも考慮が必要
- 捨てやすさ
- 技術的な潮流に求められる柔軟性
- componentを疎結合に構成しいつでも変更できる状態に保つ → 技術的な環境の変化に耐えうる
- frontend developmentに求められる要件
- speed感をもって継続的に変更に対応しながらも,障害やバグを生みにくい堅牢性の高い状態を保つ
- 変更につぐ変更への耐久性のある,捨てやすさ・変更容易性が強く求められる
- component指向であることで解決できること
- framework/view libraryを利用することでteam_developmentにおけるcodebaseの一貫性や保守性を持つことができる
- 疎結合なcomponentであることで技術的な変更に耐えうる,時間とともに古びたり腐ったりしても変更可能な状態を保てる
- speed感あるrelease cycleを求められるfrontend developmentにおいて十分な堅牢性と持続性を発揮できる
- 中規模以上のapplication開発においてbackend_APIとの連携を主体に据えたviewの構築が必要であったり,user_experienceを高めるためにstressない画面遷移が求められたり,開発序盤における技術選定の中でframeworkやview libraryの選択が求められることがある
- state_management/data_layer: Redux
- componentをまたぐような横断的な関心ごとは不可欠
- browserにおけるstate_managementは煩雑
- browserの状況・動作を考慮しながらbrowserで画面遷移を完結させる動的な実装で考慮するcase
- user面で起きうることはなかなか網羅しづらい
- 主たる目的以外に考慮すべき項目が多い
- user event以外にも,deviceや周辺環境の考慮が必要
- user actionのlifecycleが長くなるほど,扱うデータもUIの状態も多岐にわたる → solution必要
- client_MVC
- 極端に抽象化すると,Model: application data model, View: output, Controller: input
- contextごとにRoleが多少異なる
- GUI application
- Model: 監視しViewに通知
- View: Modelの値をuserに適切に表示するdisplay interface
- Controller: user actionを受けてmodelへ変更を伝える
- server_side framework
- Model: data structure(DB entityと同義), data操作method
- View: browserに流すためにModelの値を埋め込んだHTML
- Controller: requestを受けて処理を振り分けデータ操作methodを呼び出す
- GUI application
- 簡易的なclient_MVC
- objectから構成され,singleton instanceが変更される,object_orientedのcode構成
- event_driven, broadcastを利用したapproach
- observer pattern
- View, Controllerがほとんど同義になることも多い
- frontendで抽象化されるModel, 扱う上での課題
- Model: GUI applicationがもつ画面に表示・plotするdata modelであることが多い
- 特にSPAなどでは,一意のendpointから得られるdata source(JSON object)を指したり,それを加工しfrontend用のobjectに整形したりするのが一般的
- client, frontend固有の表現を実現しなくてはならないdata modelも含まれる
- server_side frameworkにおけるMVCと違ってDBのentityだけでなく,UIを表現・操作するための抽象化された固有のdata modelもfrontendには必要になる
- ModelとViewは強く紐づく
- frontendに転写されるModelと対をなすようなserver_sideのDB entityやAPI endpointの改修がapplicationの基幹的なRoleを担うことが多いため,frontendに課題解決がシフトしがち
- 双方向のdata_flow
- 各layerが単一責務を果たすための制約
- 以上の制約は,複雑になったときにどこでどんな変更があったのか・なぜViewが影響を受けたのか認知の範囲を超える危険性がある
- 規模が大きくなった際に抽象化されたdataがlayerをまたいで双方向に行ったり来たりすることは懸念が増える要因の1つ
- Model: GUI applicationがもつ画面に表示・plotするdata modelであることが多い
- RoleがあいまいになるController
- frontendにおいてはRoleがserver_side以上のものになりうる
- Controller ≒ Viewのようになり責務があいまいになって,制御しづらく複雑化しがち
- client_MVCの課題
- Modelにはserver_sideのdata_modelをAPIを通して投射したmodel, frontend固有のUIを抽象化したmodelが含まれ,Viewにおける関心がModelをまたいで複雑に絡み合っている
- ModelとViewの変更において密に相互利用しているため,変更起点が不明瞭になりdata_flowが制御しづらくなる
- ModelとViewは関連性が強い一方で変更が望まれやすく,可用性や変更可能性が求められる
- Controllerの担う責務が多くRoleがあいまいで,管理や制御が困難
- → Flux pattern → Redux
- Fluxというapplication architecture pattern
- by Facebook
- frontendに主軸を置くapplication architecture
- 単一のdata_flowを採用した,MVCとはまったく考え方が異なるarchitecture
- Action → Dispatcher → Store → View → Action
- Actionを起点にして一方向にdata_flow
- 各layer
- Action: Storeに存在するデータを更新するうえで必要な識別子.合わせて関連するデータも送出.
- Dispatcher: データを伴って発行されたActionを補足しStoreに送り出すためのAction hub
- Store: applicationが扱うdata/state. Storeの変更はAction → Dispatcherにおいてのみ変更される.基本的にはJavaScriptにおけるobject
- View: UIそのもの.Storeの変更通知を受けるための接合点が必要.ViewからもActionを発行する.
- Redux: dataの一極管理
- Fluxはarchitecture patternのため,多くの実装が存在する
- Fluxは複数のStoreを許容するが,Reduxは1つのStoreのみ許容する
- single source of truth
- 複数のdata_modelやstateを扱うStoreこそがapplicationが唯一依存すべきtree状のobject
- DispatcherがStoreに大きく紐づいている
- Reducerがいる
- Reducer: 実際にStoreの変更を受け持つ
- 発行されたActionと現State=今のapplication stateを引数にした関数
- Reduxの2つの重要な原理を持つ
- 状態を読み取りのみとして,更新する場合は新しい状態を作成して送出
- : immutable
- debug, test容易
- Reducerは純粋な関数
- 引数によって新しいStateを返却する冪等な処理の関数
- Storeを1つのReducerで管理させるところからスタートしても,applicationの成長とともに適切に切り出すことでscaling〇
- 状態を読み取りのみとして,更新する場合は新しい状態を作成して送出
- ReducerからapplicationのStoreを作成
- Reducer 1つがState 1つとなる対の構成となるケースがほとんど
- combineReducersという複数のReducerを束ねるhelper functionがある
- Reducer 1つがState 1つとなる対の構成となるケースがほとんど
- ViewがどのModelと紐づくかより,application全体における状態の一部を参照し,変更によって再実行されるという構成
- Viewと紐づくrender functionがStoreの値を監視し反映する
- button click時の挙動もModelの直接的な変更ではない
- 識別されるuniqueなActionを発行しStoreに送り出しているだけ
- Storeの変更は監視するrender functionの再呼び出しにより順次Viewに反映していく
- MVC patternのModel/Controllerというlayerの責務・Roleは,Flux/ReduxにおいてはStoreに内包されている
- Reduxは,messageによるbroadcastをより強固にしたevent_driven architecture
- Reduxが解決できること
- 3 points
- ViewをまたがるModelの変更検知という煩わしさはStoreという唯一のstateをViewにbindすることで解決
- Storeの変更は,Actionからしか変更できないという制約により状態変更の見通しが立ちやすい
- Model/Viewの密結合を避けることが可能であるため,可用性や変更容易性に優れている
- ViewであるcomponentはStoreの値にのみ集中できる点から,疎結合で構成されるcomponent指向のlibraryとの親和性が高い
- Redux周辺のecosystemを支えている
- 採用を検討するケース
- 複数のUI componentをまたぐdata_model, stateの共有が増えてきた
- release後に変更が多く発生するにもかかわらず開発コストが大きく感じる
- 3 points
- CSS: CSS meta_language, design手法, CSS-in-JS
- CSSを取り巻く現状
- W3Cがまとめている
- 1~2年単位でmoduleの使用状況をsnapshotとして文書化している
- browser vendorや開発者communityとどう関わりあっているかがECMAScriptよりも追いかけづらい
- 各browserの対応状況について
- Can I use
- 全体として大きな機能を扱う
- CSS Grid Layout Moduleなど機能レベル
- 全体として大きな機能を扱う
- MDN
- 仕様や詳細なproperty levelを扱う
- grid-column, grid-templateなど
- 仕様や詳細なproperty levelを扱う
- npmにlibraryとして公開されている
- Can I use
- CSSの表現力を高めたSass,CSS meta_language
- JavaScriptで作成されたPostCSS
- 構文をparseしAST(Abstract Syntax Tree)を操作するAPIを提供し最終的にCSSを出力するtool
- pre-processだけでなく最適化などを行える
- plugin群で,変数化やネスト構造の実現などの機能をそれぞれ単体で提供
- pluggable, 拡張性が高い
- Sassの一部の機能だけを取り出し個別に組み合わせたいといったケース,CSS module levelがfixしてbrowser実装を完了したが対応していない下位browserに対してfall backしながら変換したいケースなどに有効
- e.g.
- Sassにおける@extendを利用してstyleの継承を行いたい
- postcss-extend-rule
- Sassにおけるnest構造によって親selectorの疑似クラスを記述したい
- postcss-nested
- 新しい仕様を後方互換性をもって下位browserへfall backしながら実装
- postcss-preset-env
- babel/preset-envと名前も役割も似ている
- vendor prefixの問題も解決する
- Autoprefixerというlibraryで,通常のstyle propertyに合わせて,vendor prefixのついたpropertyを追加する変換を担当する
- libraryはCan I useのDBを利用している
- Autoprefixerというlibraryで,通常のstyle propertyに合わせて,vendor prefixのついたpropertyを追加する変換を担当する
- 具体的に対応させたいbrowser, OS versionはBrowserlistのqueryを書く
- postcss-preset-env
- Sassにおける@extendを利用してstyleの継承を行いたい
- CSS design手法
- CSSの大きな特徴であり弱点
- styleをいつでも(後方から)上書きできること
- cascadingと詳細度により意図しないstyle崩れが起きる
- styleをいつでも(後方から)上書きできること
- BEM
- componentという概念とselectorの命名規則を組み合わせたCSS design手法
- uniqueな名前空間とuniqueなselectorによるstyling
- Block
- 独立した構成要素の粒度で1番大きいもの
- Element
- Blockを構成する粒度の細かい構成要素1つ1つ
- Modifier
- Elementを拡張・装飾するvaliation
- 厳格なrule
- styleにはclass selectorを使うことがほとんど
- ID selectorを使わない
- uniqueなsingle selectorを要素のclassに付与しstyleを組み上げる
- HTMLが冗長という負担はあるものの,無記名のHTML要素を廃しそれぞれの要素がuniqueな名前空間を持つことでBlockでまとめたcomponentのscope管理を実現可能にする
- JavaScript framework/libraryとの組み合わせで解決する手法もある
- CSSの大きな特徴であり弱点
- CSSの弱点を補うためには
- postcss-preset-envで利用できるoption: stage
- ECMAScriptの策定におけるステージ制と類似したもの
- postcss-preset-envで利用できるoption: stage
- CSSを取り巻く現状
- static_analysis_tool
- 不整合をsource code実行前に検出し問題を提案するtool
- 実行せずにcodeの欠陥を予測する
- あらかじめrule/coding規約をteamに持つ
- → 無駄な指摘を減らし機械的な指摘ができる
- → source codeに一貫性をもたらし可読性が上がる
- JavaScriptはruntimeでのみerrorを検出するため事前に検出が必要
- Prettier
- 多言語をfollowするJavaScript製のformatter
- EditorConfigでformatする場合もある
- trailingComma, printWidthなど個人のcoding styleによるところが大きい部分をformat
- printWidthは多言語・記述をフォローしている
- この強力な機能がPrettierを選択する理由になる
- codebaseにおける規約やstyleは,teamで合意・納得している状態が,開発を進める中で健全
- ESLint
- codebaseにおける細やかなruleをpluggableに追加可能なlinter
- Lint: 潜在的なerrorを未然に検知し警告すること
- no-cond-assign
- より強力
- .eqeqeq
- prefer-const
- より強力
- no-fallthrough
- typescript-eslint, eslint-plugin-reactなどの追加ができる
- 実行前に警告できる + team ruleを投射
- teamとcodeの成長に合わせて追加できることが重要
- codebaseにおける細やかなruleをpluggableに追加可能なlinter
- ほかのlinter/checker
- npx htmlhint https://example.com
- HTMLの規格への準拠や規格とは別のruleのチェックもできる
- 厳密にはlinterというよりvalidator
- stylelint
- CSSにおけるlinter
- BEMの設計手法に頼らず,cascading/詳細度による意図しないstyleの上書きを防ぐために,stylelintのselector-max-specificityといったruleで詳細度を制御することもできる
- npx htmlhint https://example.com
- static_analysis_toolが可能にすること
- 構文のformatをrule化
- bugの未然検知に役立つような記述ruleやcode styleのrule化が可能になる
- 3 points
- 人間による指摘以前に機械的にcode styleやteamのcoding styleを指摘できる
- code styleの衝突はいつでも起こるもの,事前にteamで取り決めておくことで不要な議論を避けられる
- static_analysisによってscript実行前にbugの芽を摘み取れる
- 本質的な作業に集中することを助ける
- unit_test: Mocha, Jest, Karma
- テストの種類
- unit_test
- programの正常性をmodule単位で検証する
- 多くは言語に合わせてprogrammable toolやtest frameworkを使う
- by developer
- integration_test
- module同士を結合し動作させ,usecaseからtest項目を作成し実施,正常動作を担保する
- by developer, test engineer
- acceptance_test
- Web frontendのGUIからの操作におけるテスト項目を作成し実施し,正常動作を担保する
- by QA, test engineer, tester
- unit_test
- unit_testとfrontend development
- unit_testは品質の担保という側面より,developerが心理的負担のない状態で安全に開発するために存在しているという考え
- 安全に変更できることがメインで,おまけで品質がついてくる
- unit_testは品質の担保という側面より,developerが心理的負担のない状態で安全に開発するために存在しているという考え
- Mocha, Jest, Karmaそれぞれの特性
- range
- Mocha
- test environmentの提供
- test framework
- 監視機能
- Jest
- test environmentの提供
- test framework
- assertionの提供
- snapshot test
- test double
- 監視機能
- coverage計測
- Karma
- test environmentの提供
- 監視機能
- Mocha
- Mocha
- 別にassertion libraryが必要
- assertion: testの合否を確認するためのmethod
- Chaiなど
- describe: test grouping
- it: testcase
- Node.jsのassertion libraryを使った例
- test frameworkは出力結果にテストが通ったことを表示する仕組みや関数を持つ
- 別にassertion libraryが必要
- Jest
- Jestを使うと,実行環境はNode.jsだがbrowser側のWeb APIをfollowすることができる
- 必ずしもこれがJestを選択する目的ではない
- jsdomを利用してWeb APIをfollowしbrowserでの実行をemulateしているため
- expect
- matcherの引数に期待する値を設定したり,引数なしのmatcherで期待する値を表現したりするassertionが一般的
- matcher: chainするmethod
- mock
- spy
- 呼び出された際の引数や結果,呼び出し回数などの情報を格納している
- matcherの引数に期待する値を設定したり,引数なしのmatcherで期待する値を表現したりするassertionが一般的
- 小回りが利きやすくNode.jsのみで実行しやすい
- 便利なall_in_one test framework
- Jestを使うと,実行環境はNode.jsだがbrowser側のWeb APIをfollowすることができる
- Karma
- unit_testでbrowserを変えたい,browserのJavaScript engineで実行したいという需要のため
- Angularが提供するcommand line tool
- AngularはKarma + jasmine
- test frameworkではなく,unit_testの実行環境を起動するためのrunnerのRoleのみ果たす
- test frameworkの変更や実行環境の変更が設定により柔軟
- range
- unit_testやtest frameworkが解決できること
- release後に変更可用性やspeedを求められるのがfrontend
- 変更に耐えうるcodebaseや環境を作るためにunit_testのtest frameworkが必要になってくる
- 必要な部分から必要なだけ始めてみるのもfrontendにおけるunit_test導入のための戦略の1つ
- 変更が頻出する部分など
- テストの種類
Ch04 開発の現場における仕事の進め方
- agileといった考え方
- technologyやlifestyleが刻々と変容していくなかで,柔軟な対応が必要になっている
- source codeやapplicationを変更に耐えうる状態にすること,変更容易性を保った状態にすることが重要
- → agile
- scrumという開発手法
- 個人との対話と他者との協調
- → 学習メモ参照
- communication hubとして
- 誰もが触れることができるbrowserを主務としているengineerであり,操作や機能要望を一手に担うことが多い
- frontend_engineerの責務
- 画面デザインをbrowserで具現化するために実装する
- server_sideから渡ったdataを使ってbrowserにGUIを組み上げる
- browser上で正常にapplicationが動作するようにする
- → team_memberとの合意を経て,browserという職種に依存しないfieldの上で形にしていく必要がある
- → publicに情報を明確にしてteam_memberと相談しながら開発を前に進める,communication hubのような責務を持つ
- 変化に対応しながら提供するサイクルを速くする
- ここまでのまとめ
- 動くproductを速いサイクルで変化に対応しながら提供するために必要な解決策を持っていることがimportant
Part 2 どう使うかを学ぶ
Ch05 development_environment
- 既存applicationのdevelopment_environment構築
- Dockerのinstall
- Node.jsのinstall
- Yarnのinstall
- API serverの起動
- docker-compose build
- docker-compose up -d
- -d: backgroundで実行
- clientの起動
- 既存機能の把握
- どんなapplicationなのかを知る
- まずdata_structureを見る
- 機能を見る
- applicationが抱える課題を探る
- jQueryのcode
- DOMへの影響を常に考慮する必要がある
- すべてのcodeがbrowser event紐づき複雑に絡み合っていく
- → React, Fluxで解決できる
- 文字列内に変数を挿入することで書きづらいcodeになっている
- communityがどれだけ活動しているかやrelease頻度なども技術選定において重要な指標になる
- jQueryのcode
- どんなapplicationなのかを知る
Ch06 設計と実装
- branchの切り替え: git checkout \<branch>
- frontend環境の構築
- TypeScriptの導入
- TypeScriptのinstall
- yarn add typescript --dev
- yarn run tsc --init
- 公式referenceで必要な設定を選択していく
- Babel経由でTypeScriptのcompile
- yarn add @babel/preset-typescript --dev
- 既存コードをTypeScriptで書き換える
- compile errorを解消する
- TypeScriptのinstall
- codeの分割
- 処理を別のファイルに切り出す
- export, import
- 処理を別のファイルに切り出す
- Jestを利用したunit_test
- Jestのinstall
- yarn add jest @types/jest ts-jest --dev
- jest.configの設定
- yarn run jest --init
- 描画されたDOMの検査
- id, classで判別か,それができなければselectorでDOMをたどる
- 仕様をテストコードに入れておく → 改修時の確認コストが大きく下がる
- Jestのinstall
- Reactの導入
- Reactのinstall
- yarn add react react-dom
- yarn add @types/react @types/react-dom --dev
- JSXのためのcompile設定
- yarn add @babel/preset-react --dev
- JSXで要素を表示
- webpack-dev-serverのinstallと設定
- yarn add webpack-dev-server --dev
- fileに変更を加えた際に自動でbrowserに反映
- jQueryで書いたcodeをReactに置き換える
- APIとの通信 → stateに保存 → 画面が再描画
- event_handlerの記述
- yarn add react-transition-group
- yarn add @types/react-transition-group
- react-transition-groupはReact Componentでanimationを実装するためのpackage
- useState(), useEffect()などのHookを使うことで,大量のコードを書かずに動的な画面を構築できる
- 独自のHookの作成もできる → 複数のComponentで再利用したい処理の切り出しもできる
- Reactのinstall
- Enzymeを使ったcomponentのtest
- Enzyme: React_Componentのテストを補助するためのutility
- Enzymeのinstall
- yarn add --dev enzyme @types/enzyme enzyme-adapter-react-16 @types/enzyme-adapter-react-16
- Jestの設定
- jest.config.jsにEnzymeの設定を追加
- setupEnzyme.tsをJestが読み込むように設定
- Jest実行時にJSXのcompileができるよう修正
- yarn add babel-jest --dev
- yarn remove ts-jest
- babel-jestはbabel.config.jsを見にいく
- jest.config.jsにEnzymeの設定を追加
- React_Componentをテストする
- Enzymeのrendering方法
- shallow(): child componentは描画せず,引数に与えたcomponentのみを描画する
- mount(): jsdomなどのDOM emulatorを利用してchild componentを含んだ状態でDOMにmount.React lifecycle methodも呼ぶ
- render(): child componentを含んだ状態で描画するがlifecycle methodは実行されない
- → 表示結果の確認だけならshallow()/render(), lifecycle内で処理した結果をもとにDOMを構築しているようなcomponentではmount()
- mock機能
- mocks/(filename)にmock用のfileを置く
- APIとの通信などにmockを用意する
- act()でEnzymeの描画実行をラップ
- Hookをテスト対象のcomponentが利用していた場合にテストが実行できなくなる問題の回避方法
- Enzymeのrendering方法
- styled-componentsの導入
- componentとstyleをセットで扱う
- styled-componentのinstall
- yarn add styled-components
- yarn add @types/styled-components babel-plugin-styled-components --dev
- babel.config.jsに追加
- CSSからstyled-componentsへの移行
- 目的
- 開発効率を上げてreleaseまでのlead timeを短くするために開発基盤を整備し,開発者が安全にコードを変更し運用していくために型の導入やunit_testを作成している
- まずは足場を固めて自由度の高いcodebaseを作る
- 開発効率を上げてreleaseまでのlead timeを短くするために開発基盤を整備し,開発者が安全にコードを変更し運用していくために型の導入やunit_testを作成している
Ch07 CI/CDによって受けられるmerit
- workflowに自動検出を導入し安定したapplicationを提供しながらどのようなmeritを受けられるか
- CI/CDによって受けられるmerit
- lint, test, compileなどを自動化
- 理由
- developerの手動負担を抑える
- development_processのあり方の変化
- agile
- 安定した定期的なreleaseのためには自動化された安全性の確認が非常に重要
- agile
- 理由
- CI/CDについて
- 各tools
- GitHub Actionsを始める
- ESLintを導入し動作させる
- CIで自動化するmerit
- 手動操作やreviewでの疲弊を回避
- development_processが健全になる
- product code自身も健全性を維持できる
- 安心感
- CI: 安定したapplicationを効率よくuserに届ける仕組みであるとともに,本質的なdevelopmentに集中できる
- lint, test, compileなどを自動化
- performanceと改善
- backendでHTML documentのresponseを返すlatencyが大きい
- ↑ Performance/Network tabで確認
- frontendでJavaScriptファイルのサイズ肥大化やscriptがUI構築のblock要素になっている
- Performance tabで計測後Long taskの警告数が多いかどうか確かめる
- 50ms以上で警告
- performanceの問題とは
- 新規開発
- developerの関心の薄さや非機能要件の認識不足に起因する
- release済
- userの問い合わせで発覚することが多い
- 定期的な計測が必要
- performanceだけを意識する必要はない
- 新規開発
- 基礎的なperformance知識: critical_rendering_path
- 早い段階での画面描画のspeedが重視されている
- conversion rate, 直帰率の低下, 滞在時間の長さ, 同セッションでのpage viewの多さにつながる
- critical_rendering_path: 初期描画に必要なresource/step
- Reactを使ったclient_side renderingの実践では,loading中のUI提供をする限り初期描画が問題になることはあまりない
- critical_rendering_path: CSS
- critical_rendering_path: JavaScript
- 初回画面構築に不要なら,headではなくbody tagを閉じる手前に移す
- async: download後のscript実行時はblock
- defer: 画面構築に影響がないときに〇
- 初回画面構築に不要なら,headではなくbody tagを閉じる手前に移す
- Lighthouseを利用した定期的なperformance計測
- 根本原因を探ることが難しくなることがある
- → 定期的な計測・検知の仕組みを整えて計測結果を得ることが重要
- CIで定期的な計測を実行する
- Core Web Vitals
- Largest Contentful Paint(LCP)
- First Input Delay(FID)
- Cumulative Layout Shift(CLS)
- 指標の判断は各々
- Lighthouseを利用する
- npx lighthouse ...
- goal: outputを知り目標とする数値を超えていないかを検知して知らせること
- lighthouse-ciを使ったaction: treosh/lighthouse-ci-action
- 新しいworkflowを追加する
- GitHub Actionsではworkflowを物理ファイルで分けられる
- .github/workflows/lighthouse.ymlを作成
- 特定のmetricsや閾値の設定
- uploadArtifacts: true
- 結果reportをdownloadできる
- Developer_Toolからtrace可能なreportがほしいときは,lighthouseを使うのも〇
- --save-assets optionが必要
- puppeteerというChroniumをheadlessで動かすtoolとlighthouseを使って簡単にscriptの実装ができる
- Datalogという監視やmonitoringのためのSaaSで数値をplotして可視化もできる
- GitHub Actionsではworkflowを物理ファイルで分けられる
- 早い段階での画面描画のspeedが重視されている
- 強力な武器はない,人には人のperformance
- performanceは1つの指標に過ぎない
- backendでHTML documentのresponseを返すlatencyが大きい
Ch08 analysis, monitoring
- serviceの成長とともに開発する
- 保守・運用: product, serviceを成長させていくphase
- 数字として可視化して目標と比較するために,計測する仕組みとしてanalysis_toolやAB_testが導入されることが多い
- 計測のための実装やAB_testの実施もfrontend_engineerの責務
- 仮説検証,AB_testの目的
- 仮説検証
- personaを使って施策の議論や価値提供の優先度を考えることも多い
- analysis_toolの導入により,目標値に向かうためのuser actionをある程度可視化できる
- AB_test
- personaから推察される行動を考え仮説を立てて,短いiterationで立証するため
- marketingにおける調査・試験の一種
- GUIにおけるpatternや組み合わせで立証することが多い
- UIや使い勝手,操作性などclient接点における価値をすばやくuserに届け仮説検証を行うことが,analysis, AB_testの目的になる
- 仮説検証
- toolの導入: Google_analytics
- 計測のためのsnippetを導入するだけでデータの可視化や計測がいちはやく手に入れられる
- Google_marketing_platformの1 product
- analyticsの利用登録
- 計測のためのtracking code
- eventの計測,属性の追加
- ラップしたようなevent送信用の関数を作る
- service 1つに対してproperty 1つ
- 重要
- action nameやcategory, label nameなどをteamで設計し,memberが共通理解を得ること
- toolの導入: Google_optimize
- AB_test用のtool
- html code → p.211
- 振り分けの完了は,Cookieの_gaexpの値の末尾を見るとわかる
- product codeに組み合わせる
- ReactのHooks APIを利用した実装方法
- UIと振り分けを宣言的にする
- Reactが実行される段階ではtest patternは決定している
- third_party scriptとの兼ね合い
- client performanceにはいくらか影響が出ることに注意する
- AA_testで導入によって数値に影響が出ないことや実際に振り分けが行われることを確認する
- toolの導入における懸念事項の洗い出しや組み込んだ場合の結果の提供もfrontend_engineerの責務
- user_monitoring, error_event_monitoring
- 障害や問題をrealtimeで検知する仕組みが必要
- userを取り巻く環境を知る
- Developer_Tool
- network状況をemulate
- specをemulate
- analysis_toolでまずuserの環境・状況を知ることがimportant
- Developer_Tool
- browserで起きるerror_eventなどからuserを知る
- error_event_monitoringのため,Sentryを導入
- yarn add @sentry/react
- 設定できること
- error_eventへの緊急度levelの設定
- error_eventの閾値超えによるalert(各種tool連携)
- release versionやuser definitionのmeta information付与
- sourcemapを利用した影響のあったsource codeの表示
- documentが豊富
- team全体で導入する
- Sentryの動作とcodeへの組込
- global scopeにおける例外handler
- top levelに繰り上がった例外をさばいている
- 事前に用意できるものは個別に準備しておく
- global scopeにおける例外handler
- React Error Boundaryを使う
- fallback UIをuserに提供できる
- error_eventのlogも取れる
- 昨今のReactでの開発は関数としてcomponentを実装するが,Error Boundary componentはclass baseの componentである必要がある
- 収集したerror_eventを役立てる
- errorとして収集して対応したものも,情報として収集を続ける必要があることもある
- Cookieの使用状況など
- error_eventのlevelづけ
- error_eventの発生ログは,大きな判断材料になる
- この数字がteamでの対応優先度を決めるのに一番良い判断材料になる
- product_backlogに追加も〇
- errorとして収集して対応したものも,情報として収集を続ける必要があることもある
Part 3 応用編 より深く学ぶために知る
Ch09 team_developmentとWebへの貢献
- teamで働く
- Scrumという開発手法について(あらためて)
- → 学習メモ参照
- Scrumを採用したteamに入ったら
- story: sprint_planning
- story: sprintの開始
- story: sprintの終わり
- team_developmentとはtechnical skillではない
- Scrumという開発手法について(あらためて)
- communityへの貢献活動
- OSSへの貢献はcode_commitだけではない
- bug report, document修正, 簡単なpull_requestから始めてもよい
- communityをのぞいてみる,issueを立てる
- CONTRIBUTION.mdのようなdocumentに,貢献の方法や何から始めるのが良いかが書いてある
- CODE_OF_CONDUCT.mdで行動規範を掲げているものもある
- 機能報告・bug報告の方法
- まずは,OSSのrepositoryにあるissueを検索し,類似した内容の有無を確認
- bugの再現状況の追記なども〇
- templateを使う
- できることからOSSへcommitする
- 各issueにはlabelがある
- 寄付する,翻訳するといった違ったapproach
- Webというplatformに貢献する
- Webというplatform: Web site/applicationを開発するためのHTML,CSS,JavaScriptの標準化されたtechnology群
- Webは維持し続けることがimportant
- product_codeを健全に保ち周知していくことも,小さな規模だがteamへの貢献になる
- OSSへの貢献はcode_commitだけではない
- Web platformにかかわるfrontend_engineerとして
- 仕様を知るには
- lightにcatch_upする
- frontend technologyを楽しむために
- 動くproductを早いcycleで変化に対応しながらuserへ届けるために,必要な解決策を持ち実現できることがより重要
- 実現できることよりさらにproductに反映できることが重要
- Webへの興味関心を持ち続ける
- 変化が早く感じるときは,情報を見過ぎている可能性を考える
- enduserはdeveloperではない
- userの課題を解決するために,userへの価値提供のためにWeb platformを使ってdevelopmentを進めていく・課題を解決するということに,意識的であることや楽しむことがfrontend開発において大切
『入門React: コンポーネントベースのWebフロントエンド開発』学習メモ
Part 1 基礎
Ch01 Introduction
- 背景
- React: XHPというPHPのframeworkのJavaScriptへの移植
Ch02 JSX
- Separation of concernsのために,Componentを使う ←→ templateと表示logicといった分離
- Component: Markupとそれを生成するcodeが強く結びついたもの.Reactの中心的な概念.
- What's JSX
- JSX: JavaScript XML
- ReactのComponent内でMarkup言語を記述するためのXML風のsyntax
- 特徴
- JSXはsyntax拡張.(あとでJavaScriptの関数呼び出しに変換される)
- JSXはruntime libraryが不要
- JSXはただの関数呼び出し
- JSXのmerit
- 既知の構文
- 意味的な分かりやすさ
- JavaScriptのcodeをよりsemanticで意味のあるMarkup言語で記述できる
- Componentの構造と情報のflowはHTML風なsyntaxで宣言的に記述
- applicationで定義されたcustom Componentをタグ名として使用〇
- 構造の可視化
- 抽象化
- projectの進展に伴うcodeの変更を減らす
- Separation of concerns
- Component内のMarkupとBusiness Logicを分離
- Component合成
- custom Componentの定義
- dynamic value
- child node
- this.props.children: start-end tag内の全子nodes
- JSXとHTMLの違い
- JSXなしでReactを使用したい場合
- ReactElementの作成
- 簡略化
- 参考文献
Ch03 Componentのlifecycle
- Component: state machine → inputに対して常に同じoutput
- Component作成時に呼ばれるmethod
- Componentのinstance作成~初回描画
- Componentを利用可能にするための設定
- getDefaultProps
- Componentにつき1回のみ
- 非scalar値は共有
- getInitialState
- instance作成事
- ここからthis.propsが利用できる
- componentWillMount
- 初回の描画前
- renderの前にComponentのstateを変える最後の機会
- render
- Virtual DOM(Componentのoutput)を作成
- ReactのComponent中で唯一省略できないlifecycle method
- rules
- componentDidMount
- Componentのinstance作成~初回描画
- Component作成後に呼ばれるmethod
- componentWillReceiveProps
- 親Componentがpropを変更したとき
- shouldComponentUpdate
- optimezeのためにある
- develop時には使わない.tuningで使う.
- PureRenderMixinでperformance tuneも〇
- componentWillUpdate
- state, propsの更新は×
- componentDidUpdate
- 変更されたDOMのreferenceへのaccess
- componentWillReceiveProps
- Component破棄時に呼ばれるmethod
- 不要になったらReact DOMから切り離してdelete
- componentWillUnMount
- deleteの直前
- 後処理できる
- timer, event listenerなど
- anti pattern: 加工された値をstateに保存する
- a single source of truthの原則 @React
- → 単一のinformationのcopyが可能になる
- e.g.
- initialValueはOK
- a single source of truthの原則 @React
- まとめ
- lifecycle method: applicationがComponentのeventにeffectiveに反応できるようdesign
- 各Component: state machine.一貫して安定した予測可能なmarkupをoutput
Ch04 data flow
- 基本は親から子への一方通行
- Component: 親からのpropを描画
- propの変更 → propを参照する全Componentが再描画
- 内部state: Componentの内部でのみ変更可能
- Component: propsとstateをinput, Virtual DOMをoutputする一種のfunction
- props
- props: Componentに渡される任意のdata
- setProps: 子Componentに対して呼ぶか,Component treeの外側で呼ぶのみ
- Componentは自身のpropsを変えない
- JSXでのpropsの指定
- ...: 後に続くObjectをその場に展開
- event handlerの格納も〇
- propTypes: propsとして渡された値をvalidate
- Componentの仕様が明確になるため,使用推奨
- getDefaultProps
- React.createClass時にキャッシュ
- state
- Componentの内部でのみ使用
- 要素の表示内容の決定に使う
- getInitialStateっでinitialize
- setState
- 必ずrender()を伴う
- directにthis.stateの変更は絶対×
- なるべく一部のComponentのみ
- 複雑になるため
- stateとpropsの使い分け
- stateはComponentの機能にdirectに必要な単純なdataのみに〇
- propsの値をstateにcopy×
- まとめ
- prop経由でCompにdataを渡し,tree全体に伝播
- propsはimmutableとして扱う(Component内)
- 子Componentとのやりとりのために,event handlerをpropsに指定
- 表示内容を決定するためのsimpleなdataのみstateに保存可能
- this.setStateを必ず使う
Ch05 event_processing
- UIの半分は表示,残りの半分はuserのinputに反応すること
- JavaScriptで,userのinput eventに応じたevent_handlerを記述する
- Reactでのevent_processing
- まずevent_handlerを登録
- handlerが呼び出されたときにcomponentのstateを変更する処理を記述
- render method内でstateを参照しさえすれば,変更は自動的に描画に反映される
- 追加の情報は,event_handlerに引数として渡されるevent objectが追加の情報を含んでいるので,それをもとにstateを適切に更新できる
- techniqueとReactの高性能なRenderingの仕組みにより,容易にuser inputに反応してUIを更新できる
- event_handlerの登録
- onClick
- eventとstate
- たいていはsetStateで既存のObjectにマージする
- Object変更時は必ずsetState/replaceState
- event_object
- event.target.valueからformのinputを取得
- 特にonChangeでよく使われる
- event.target.valueからformのinputを取得
- まとめ
- Reactでuser inputをUIに反映させる手順
- React componentにevent_handlerを登録する
- event_handler内でcomponentのstateを変更する.stateを変更するとcomponentは再描画される.
- componentのrender method内でthis.stateを参照するように変更する
- Reactでuser inputをUIに反映させる手順
Ch06 componentの合成
- HTMLではpageは要素から構成されるが,Reactではcomponentから構成される
- ReactのcomponentはHTMLの要素にJavaScriptの表現力が追加されたもの
- component: propsとstateをinputとして受け取り,HTMLを出力として返す一種の関数のようなもの
- 各componentはapplication内のデータの一部を表示するために設計されている → React componentはHTMLの拡張といえる
- HTMLの拡張
- lifetimeを通じて,React_componentは振る舞いをcontrolできる
- React.createClass
- 継承より合成
- 小さくて単純なcomponentを組み合わせて,より大きくて複雑なapplicationを構築する
- 合成の例
- has aで表現できる
- 汎用的なinput要素から初めて,radiobuttonにcustomizeし,多肢選択の機能を提供する例
- original form controlのcomponentの作成
- 親子間の関係
- 親child_componentでの通信の最も簡単な方法: propsを使う.親がcallback functionをpropsとして渡して,子がそれを呼び出す
- child_componentの変更に対する振る舞いを定義
- 親child_componentでの通信の最も簡単な方法: propsを使う.親がcallback functionをpropsとして渡して,子がそれを呼び出す
- まとめ
- Reactにおいて合成のpatternを使用する手法
- HTMLの要素や自信のcustom componentをwrapして,必要に応じてそれらの機能をcustomizeすることで,componentを作成する
- 汎用的なcomponentは,合成のたびに特化され意味を持ったものになる
- Mixin: 合成以外の方法 → 次章
- Reactにおいて合成のpatternを使用する手法
Ch07 Mixin
- Mixin: 複数のcomponent間で共有可能なmethodを定義できる
- Mixinとは
- Mixinを使ったTimer componentの実装
- componentにmixinsというpropertyを定義する
- 制限
- 複数のタイマーは持てない
- Timer eventのhandler methodの名前はonTickに固定
- タイマーを止めるには内部の__interval propertyにアクセスする必要がある
- → コードは増えるが修正できる
- 適用例
- eventを監視してstateを更新するMixin(e.g. FluxのStore Mixin)
- XHRのuploadを行いprogressをstateに反映するMixin
- child_componentをの末尾に表示するためのlayer Mixin(e.g. modal dialog)
- まとめ
- Mixin: codeの重複を避けるために有効な手段
- componentは自身に固有の責務に集中できる
- 強力な抽象化の手段を提供
- ある特別なふるまいや役割をcomponentと切り離して記述できるので,codeはより理解しやすいものになる
- Mixinとして定義できないか常に検討する
- Mixin: codeの重複を避けるために有効な手段
Part 2 応用
Ch08 DOM操作
- 通常はReactの提供するAPIだけでUIを構築できるので,実際のDOMに直接アクセスすることはほとんどない
- componentを合成することで,複雑なUIをまとまった全体としてuserに提示できる
- Reactとともに使用することを想定していないthird partyのlibraryを導入する場合や,Reactで非supportのDOM操作を行いたいときがは,DOMへのアクセスが必要になる
- componentのlifecycleの特定の局面でしか利用できないという制限あり
- DOM_nodeへのaccess
- まずそのnodeを表現するcomponentにアクセス
- componentにref属性を設定して実現する
- ref属性はcomponentのすべての子nodeの中でuniqueである必要あり
- ほかの子nodeのref属性と重複は×
- ref属性はcomponentのすべての子nodeの中でuniqueである必要あり
- → componentDidMountの中で,React.findDOMNode()を使って実際のDOM_nodeにアクセス
- event_handler中でも〇
- componentにref属性を設定して実現する
- どうしても必要な場合以外は使わない
- Reactが内部で行う最適化の妨げになるうえ,applicationが複雑化する要因になる
- まずそのnodeを表現するcomponentにアクセス
- React friendlyでないlibraryの使用例
- オートコンプリートlibraryの例
- 行儀の悪いlibrary
- 自身がアタッチされた要素の内容を,カスタムイベント経由で変更してしまうpluginの例
- 親の要素まで変更する場合は対応できないので,Reactとの併用はできない
- Reactを守るために,pluginの使用するDOM_nodeを完全にcontrolする必要がある
- componentDidMountの中で独自に要素を作成し,renderで作成された要素の子nodeとして追加する
- 不要になった際は自分自身で削除が必要 @componentWillUnMount
- pluginのdocumentをみて,ほかに必要な手順がないか確認する
- global event_listenerやtimerやAJAX requestなど
- pluginのdocumentをみて,ほかに必要な手順がないか確認する
- Reactに管理されていないdivの描画の更新
- 確実: componentを疑似的にpageから削除して追加する
- 簡潔でperformance〇: pluginの更新用のAPIを呼び出す
- ごく単純なpluginの場合は,同じ機能を提供するReact componentを作ってしまった方が早いこともある
- 自身がアタッチされた要素の内容を,カスタムイベント経由で変更してしまうpluginの例
- まとめ
- Reactが提供するAPIだけでは不十分で,どうしても実際のDOMにアクセスしなければいけない場合がある
- ref属性を設定して,特定のcomponentにアクセスし,そのcomponentの実際のDOM_nodeへの参照をReact.findDOMNode()経由で取得する
- componentDidMountが呼び出されてからでないとアクセスできない
- → ReactがサポートしていないDOM操作や,Reactとともに使われることを考慮されていないthird partyのlibraryの使用が可能になる
- ref属性を設定して,特定のcomponentにアクセスし,そのcomponentの実際のDOM_nodeへの参照をReact.findDOMNode()経由で取得する
- Reactが提供するAPIだけでは不十分で,どうしても実際のDOMにアクセスしなければいけない場合がある
Ch09 form
- single_page_applicationでは,formを正しく扱うのは至難
- user inputにより扱うべき状態が一気に増えるため
- formの複雑な状態の管理のために,Reactを使える
- React_componentの主要な側面
- 動作が予測可能であること
- testの容易さ
- ↑ 同じ内容のpropsとstateで描画を行った場合,常に同じ描画結果を出力するというReact_componentの特徴のため
- Reactのformのcomponentには,管理された/されていないcomponentの2種類がある
- 複雑なformを扱う場合などは,DOMにアクセスが必要になる
- 管理されていないcomponent
- 複雑なformに向いている
- 直接DOM_nodeにアクセス
- ある意味アンチパターン
- formはほかのReact_componentと異なる動作
- 値は要素が管理し,formは管理しない
- DOM_nodeに直接アクセスが必要
- ref: React独自の特性
- validationや入力制御を行う必要がないformに向いている
- 管理されたcomponent
- 通常のReact_component patternを踏襲する
- valueをcomponentのstateとして保持することで,React_component自身がform stateを管理する
- formを完全にcontrolしたい場合
- dataflowが明確になる
- codeは増えるが,データが入力されるたびにstateが変化するため,dataflowを完全にcontrol〇
- data input中にほかのcomponentの値の参照も可能
- form event
- event: form stateのcontrolに欠かせない
- HTMLで発生するすべてのeventに対応
- target property経由で,発生元のDOM_nodeにアクセスできる
- targetを参照することで,管理されたcomponentにおいて,user inputにアクセスできる
- label
- radiobuttonやcheckboxの意味をuserに伝える
- htmlFor @JSXがHTMLのfor属性
- classをclassNameと扱うのと同じ理由(予約語)
- textareaとselect
- 統一性と使いやすさのために,original HTMLの仕様から変更されている
- select
- 複数選択が有効だと,child_componentのoption要素のselected propertyのみ変更される
- checkboxとradiobutton
- checked属性をcontrolする
- form要素のname属性
- 基本はあまり重要ではない
- 欠かせない場面
- formをseriarizeするthird_partyのpluginをReactとともに使うとき
- submit eventを横取りせずにbrowserに処理を任せるとき
- browserのauto complete機能はuserのmail addressなどを提示するためにname attributeを使う
- 管理されていないradiobuttonの場合,同じname属性を持つ要素同士は同時に1つしか選べないのがdefault動作
- 管理されたradiobuttonでは,event_handlerで処理を行い同様の動作を実現
- 複数のform要素とchange event_handler
- change event_handlerの再利用の方法・例
- bind経由でevent_handlerに追加の引数を渡す
- event_handler内でDOM_nodeのname attributeを参照する
- React.addons.LinkedStateMixinも異なる方法で解決する
- linkState method
- form input valueを親componentのstateとして保持する場合は,とてもsimpleになる
- dataflowを細かく制御したい場合は,かえって複雑になる
- change event_handlerの再利用の方法・例
- custom form component
- checkbox, radiobuttonなどの複雑なcomponentのinterfaceを改善するためにも使われる
- ほかのform componentとinterfaceを合わせる
- 可読性のため
- focus
- formへのfocus設定: userが次に取るべき行動を明確に伝える良い方法
- 使いやすさも〇
- autoFocus attribute
- focus()
- formへのfocus設定: userが次に取るべき行動を明確に伝える良い方法
- usability
- 要求を明確に伝える
- label: formの要素が何を期待しているのか伝えるRole
- radiobutton, checkbox
- placeholderよりも,labelやpopupが〇
- label: formの要素が何を期待しているのか伝えるRole
- inputに即座に反応する
- validation
- 最善のtiming: input要素のblur event発生時
- 処理中であることを示す
- transition, animation
- validation
- performance
- transition, network delayなどによる動作の遅延について
- 速度は相対的かつ体感的
- 速いように見せることがimportant
- predictable
- platformのdefault 動作に準拠するか,userの操作をまったく異なったものにする
- 一貫性
- platformの標準動作に準拠
- accessibility
- 試しに単一のinput deviceでapplicationを操作してみる
- Accessible Rich Internet Applications(ARIA)
- Accessibility Developer Tools
- input項目数の削減
- usability〇
- 実際の項目数よりも,userがどう感じるか
- input項目を複数の小さいformに分割
- autocomplete
- autoFocus
- 要求を明確に伝える
- まとめ
- Reactでは,form state managementはDOMではなくcomponentで行う
- form要素の動作を完全に制御
- application固有の複雑なcomponentを作成できる
- formはapplication内で最も複雑な操作を必要とする
- form design時にはusabilityに配慮が重要
- Reactでは,form state managementはDOMではなくcomponentで行う
Ch10 animation
- applicationを洗練されたものにするための方法
- animation: user_experienceを滑らかで自然なものにする効果がある
- CSSTransitionGroupというアドオンをCSS3とともに使用する → 簡単にanimationを追加
- 初期はimperative(命令的)だった
- DOM要素に直接accessして座標値などのstyleを絶え間なく更新
- → ReactではCSS animationを利用した,よりdeclarativeな手法
- CSSTransitionGroup
- CSSを使わずにJavaScriptのtimerを使ってanimationを行うこともできる
- CSSを用いたanimation
- CSSTransitionGroup
- transitionの適用対象となるcomponentのgroupを表し,componentが追加/削除されるたびに自動的にCSS_styleを切り替えてanimationを実現する
- transitionのclassごとにstyleを記述する
- componentのtransitionName attributeの値に基づいて,4つのCSS class名が作成される
- -enter, -enter-active, -leave, -leave-active
- componentのtransitionName attributeの値に基づいて,4つのCSS class名が作成される
- transitionのlifecycle
- -enter classがchild_componentが追加されたときに適用され,すぐに(次のanimation frameで) -enter-active classが適用される
- transitionの開始時と終了時のstyleおよびそれらの間をどのように遷移するか定義できる
- transitionEnter={false}とすると無効にできる(transitionLeaveも同じ)
- 上位のcomponentからprop経由で設定もok
- -enter classがchild_componentが追加されたときに適用され,すぐに(次のanimation frameで) -enter-active classが適用される
- よくある過ち
- leaveのanimationが完了するまでchild_componentは削除されない
- child_componentのkey attributeはtransition group内でunique要
- CSSTransitionGroup
- timerを用いたanimation
- 古いbrowserに対応したり,ScrollのanimationやCanvas描画などを扱いたい場合
- CSS3のanimationよりperformance ×
- componentのstateが定期的に更新される
- stateはanimationのtimelineの中での現在位置を保持する
- render method中でこのstateの値を参照することで,applicationは適切なanimationのframeを描画できる
- 頻繁な再描画を行うので,timerのAPIとして一般的に効率的とされているrequestAnimationFrameを使う
- 代替: 効率で劣るsetTimeoutを使うしかない
- requestAnimationFrameを使ったanimation
- position: absolute
- 要素のstyleのleftとtopの値を時間の経過とともに更新する
- componentWillUpdate handler内で呼び出す例
- position: absolute
- setTimeoutを使ったanimation
- これらのときはsetTimeout
- requestAnimationFrameは少ないoverheadで滑らかなanimationの効果が得られるが,古いbrowserでは非support
- 絶え間なく呼び出されるのが煩わしいときもある
- React Tween State
- Open Source Library
- 汎用的なanimationのAPIを提供
- これらのときはsetTimeout
- まとめ
- Reactを使った以下のanimationを見た
- CSSTransitionGroup addon
- requestAnimationFrame
- setTimeout
- Reactを使った以下のanimationを見た
Ch11 performance_tuning
- Reactの差分描画のalgorithmは優れているので,UI全体を入れ替えたとしても実際のDOMの描画は最小限に抑えられる
- 深くnestしたcomponentなど,delicate tuneが必要な場面では,virtual_DOMの不要な入れ替えを避けることでperformance改善〇
- React componentの簡単な設定により,applicationのperformanceを改善する方法を見る
- shouldComponentUpdate
- shouldComponentUpdateで,Reactにcomponentのlifecycle methodを呼び出す必要があるか教える
- falseならrender methodを呼び出さず,描画済みのvirtual_DOM objectをそのまま使う
- defaultはtrue
- 初回描画時は呼び出されない
- 新しいpropsとstateを受け取るので,それをもとに決定できる
- componentがpure(同一のpropsとstateに対して常に同じ内容のDOMを描画する)であれば,React.addons.PureRenderMixinを使える
- shouldComponentUpdateを上書きする
- Mixinを定義するだけでいい
- propsやstateが複雑なobjectである場合は,比較そのものが重くなりうる
- → immutable data_structureを使うか,helper_functionを使う
- shouldComponentUpdateで,Reactにcomponentのlifecycle methodを呼び出す必要があるか教える
- immutablility helper_function
- React.addons.updateというhelper_functionにより,簡単にimmutableなdata_structureを使用できる
- first argument: object/array, second argument(option): hash object
- bottleneckを調べる方法
- React.addons.Perfを使うとわかる
- Developer Toolsのconsoleから,React.addons.Perf.start(); (終わるときは~.stop();)
- → ~.printWasted();でprofile結果を表示
- Wasted time: renderが呼び出されたにもかかわらず,実際のDOM内容が変わらなかった処理にかかった時間をcomponentごとに集計したもの
- React.addons.Perfを使うとわかる
- key attribute
- componentのclassよりさらに細かくinstanceを識別できる
- 不要な処理を避けるために使う
- key attributeの値が変更された場合,差分計算を行わずに即座にchild_componentを含めたすべてのvirtual_DOM objectを破棄し,一から再描画する
- listの要素の順番が変わったことをReactに伝える
- → 移動だけで済むようになる
- こちらの目的で使われることが多い
- 制限事項
- 同一階層のcomponent, 同一の親を持つcomponent間でunique要
- 要素が現在のparent_componentのlistと別のlistに移動したときは,同一性の追跡不可能
- component内部からpropsの値として参照できない
- 同一階層のcomponent, 同一の親を持つcomponent間でunique要
- まとめ
- shouldComponentUpdateを使用してperformanceを改善
- React.addons.Perfを使用して不要なrender呼び出しを特定
- key attributeを使用してReactの処理を最小化
Ch12 server_side_rendering
- 以下の問題への解決策
- search_engineのindexの対象にならない
- JavaScriptのロードを完了してからページを作成するので,初回のページ表示が遅い
- virtual_DOMのため,server_side_renderingが可能
- virtual_DOM: 単なるmemory上のdata_structure
- server_sideでvirtual_DOMを実際のDOMに反映する代わりにHTML文字列として出力する
- → 同じReact_componentをclient_serverで共用できる
- React.renderToString/React.renderToStaticMarkupの2関数
- server_sideにおける描画関数
- renderToString
- ほとんどはこっち
- 第2引数のtarget DOMが不要で,戻り値としてHTML文字列を返す
- sync_callで,非常に速く動作
- data-reactid
- browserでDOM_nodeを特定するために使う
- data-react-checksum
- server_side_renderingの場合にのみ追加される
- server_sideで作成されたDOMが再利用できるかどうかを判断する
- DOMの作成処理とそれをdocumentに挿入する処理を省略
- → 特に複雑なサイトではページの初期表示までの応答時間が著しく改善
- 作成するReact_componentの内容がserver_clientの間でまったく同じであることが特に重要
- root要素のみ
- renderToStaticMarkup
- 独自のdata attributeなし
- server_sideで描画したReact_componentをbrowserで再描画しない場合のみ
- HTML mailの作成
- 作成したHTMLを後でPDFに変換する
- componentのテスト
- renderToString
- server_sideにおけるcomponentのlifecycle
- renderより後のlifecycle methodは呼び出されない
- 特に,componentDidMount, componentはserver_sideでは呼び出されない
- → event_listenerの定義において,componentの終了を通知するlifecycle methodは存在しない前提で設計する
- → event_listenerやtimerは常にcomponentDidMountで登録して,componentWillUnMountで解除する
- componentWillMountはserver_clientいずれでも呼び出される
- renderより後のlifecycle methodは呼び出されない
- client_server両方で使えるcomponentの設計
- 同一のpropsとstateが与えられた場合,常に同じ内容を出力するようにcomponentを設計する
- → テストしやすく,server_clientで同じoutputを保証できる
- randomな値は外部からpropsとして受け取るようにする
- applicationの初期値をどのようにしてclient側に渡すか
- 最も簡単: JavaScriptのobjectとして初期値を渡す
- 同一のpropsとstateが与えられた場合,常に同じ内容を出力するようにcomponentを設計する
- async_data
- まずdataを取得して,そのcallback_functionでcomponentを描画する
- e.g.
- async_dataを取得する処理をstatics methodとして定義する
- props.initialStateとしてclientに渡す
- lifecycle method経由でasync_dataの変更を監視し,変更があった場合statics methodを使ってdataを再取得
- async_dataを取得する処理をstatics methodとして定義する
- Isomorphic(同型の) routing
- react-router
- Isomorphic JavaScript(: client_server両方のcodeをJavaScriptで記述することでコードを共有すること)を可能にする
- server_clientで同じAPIを提供するrouterを使ってroutingすること
- 既存のrouterでserver_sideでReact_componentを描画する場合,そのrouterはDOMのAPIに依存していないことが重要
- SEOの観点から重要なデータを取得するときは,routerのhandlerで非同期に取得して,top levelのcomponentから末端のcomponentへ順番に渡す必要がある
- 重要でないdataは,componentのcomponentDidMount methodでAJAXでdataを取得すればよい
- 常にclientでデータを取得
- Reactと併用するrouterを選定するときは,routerのhandlerでasyncにデータを取得してrenderingできるか注意する
- react-router
- singleton_object
- async_dataがsingleton_objectの状態に依存している場合,描画時にasync_dataが予期せぬ値になってしまう可能性がある
- 描画のたびにsingleton_objectをresetではなく,applicationを隔離されたcontextで実行する
- ContextifyのようなNodeのpackageなど
- Web Workersによく似ている
- 与えられたcodeをV8の異なるinstanceで実行する
- performanceは犠牲になる
- 実行contextをcomponent間で共有することは,非推奨
- componentの移植性が低下するため
- tradeoffがある
- ContextifyのようなNodeのpackageなど
- 描画のたびにsingleton_objectをresetではなく,applicationを隔離されたcontextで実行する
- async_dataがsingleton_objectの状態に依存している場合,描画時にasync_dataが予期せぬ値になってしまう可能性がある
- まとめ
- server_side_renderingは,Web applicationのSEOとperformanceの観点から有効な手段になる
- React_componentはserver_client両方でのrenderingが可能
- application全体がserver_side動作するように設計されている必要がある
Ch13 React_family
- Jest
- test runner
- Jasmineがベース
- Nodeのrequire()関数を書き換えて,すべてのCommonJS style moduleをmockに置き換える
- → すべてのcodeをテストできる
- Immutable.js
- React, Fluxと相性〇
- applicationのperformanceとsimplicityが改善
- 通常のJavaScript objectまたは配列を受け取り,独自のデータ構造に変換したものを返す
- 戻すこともできる
- object, arrayの比較が早くなる
- ===を使える
- Flux
- design pattern
- 厳密に単一のdataflowを規定
- 何にも依存していないので,気に入った部分だけ取入れられる
- まとめ
- Jestにより,Unit test時に依存moduleをmockに置き換える方法
- Immutable.jsを通常のデータ構造の代わりに使用する方法
- FacebookのFlux patternの概要
Part 3 tool
Ch14 build, debug
- Reactはcomponent単位の開発を容易にするため,いくつかの抽象的なlayerを設ける
- debug, release作業は複雑になる
- build_tool
- Reactでのapplication developmentにおいて,最も繰り返し実行される処理はJSXのパース処理
- そのほかに大部分を占める処理: 複数のmoduleをbrowserで実行するために単一もしくは少数のfileにまとめる(bundle)処理
- Browserify
- JavaScriptをpackagingするためのtool
- Node.js styleのrequire()呼び出しをbrowserでも使えるようになる
- requireされたすべてのmoduleを単一のJavaScript fileに連結(bundle)するのみ
- 単一のfile → browserで簡単にloadできる
- installと設定
- React projectの作成
- Watchify
- 変更時に自動でbuild
- buildの実行
- Webpack
- Browserifyと同じく,JavaScriptをpackaging + たくさんの機能
- BrowserifyがGruntやGulpと併用して実現する機能を単体で実現できる
- WebpackとReact
- 自身が依存するassetを内包するcomponentを作成できる
- assetがcomponentに紐づけられる → componentの移植性が高まる
- あるcomponentが使われなくなったときにその依存assetも削除
- どこからも参照されていないCSSや画像がなくなる
- Browserifyと同じく,JavaScriptをpackaging + たくさんの機能
- Reactでのapplication developmentにおいて,最も繰り返し実行される処理はJSXのパース処理
- debug_tool
- React Developer Tools
- Chrome拡張機能
- displayName
- JSXがJavaScriptに変換されるときに,componentの定義に自動で追加される
- そのほか多くの情報を見ることができる
- JSBinとJSFiddle
- online demo site
- React Developer Tools
- まとめ
- React developmentにおいて,build, debug toolがいかに有用であるか
Ch15 test
- TDDのworkflowでコードをmodule化し,変化に強くし,より安全にコードベースを変更できるようにする
- はじめに
- testにより,よりよいコードを書けるようになる
- 単一責任の原則や,デメテルの法則にしたがうことになる
- testの種類
- unit_test
- E2E_test
- test_tool
- testにより,よりよいコードを書けるようになる
- 初めてのtest: render method
- testでは,React.renderの代わりにReact.addons.TestUtils.renderIntoDocumentを使う
- ReactにおけるHTMLの検査
- Reactでは,componentのrender methodで指定したHTMLは,実際のDOMとは異なる
- componentのmock
- 対象のcomponentの機能だけをテストする
- 前処理,後処理
- beforeEach, afterEach
- 要約
- mock componentを定義する
- componentのローカル変数の値を保存する
- ローカル変数の値を書き換える
- testを実行する
- ローカル変数の値を元の値に戻す
- Jasmineのhelper moduleでspec間の共通の処理を記述できる
- (mockの内容はp.147の下段)
- npmを使わないvanilla projectのcase
- functionのstub化
- stub化の目的
- unit_testの対象範囲の限定
- 関数内で使われているAPIやthird_partyのserviceなどの処理がテスト中に実行されるのを避ける
- その関数が正しい呼び出し元から正しい引数で呼び出されていることをチェックしたい
- jasmine-react-helpers
- spyOnClass
- stub化のmethod例
- ランダム
- serverからdataを取得
- testで再現困難なstateを参照する
- 副作用の範囲が大きい
- 現在時刻やtimezoneを扱う
- callback_functionのテスト
- test→実装の流れ
- stub化の目的
- event simulation
- test→実装の流れ
- testにおけるcomponentのselector API
- Mixinのテスト
- Mixinを直接テストする
- Mixinの内部で呼ぶReactのmethodをすべてスタブ化必要
- とても細かいテストになる
- 機能が複雑な場合は利点もあるが,機能ではなく実装そのもののテストになってしまう状況になる
- Object.createでテストごとにMixinを新たにcopyする方法
- Reactの機能はスタブ化する
- Reactのlifecycle methodの呼び出し順を忠実に再現が必要になる
- dummy_component経由でMixinをテストする
- 非常にsimpleなcomponentであり,テストの意図がより明確になる
- 唯一の難点: Reactの制限によりrender methodを必ず実装する必要がある
- componentが画面から削除されたときにclearIntervalが呼び出されることを保証したい
- → componentWillUnMountを直接呼び出すのではなく,componentを画面から削除してReactに適切なcallbackを実行させることが重要
- まずは直接の方式でテストを書いてみて,困難な場合はdummy_component経由に書き直すのが〇
- 共有スペックを記述する
- testの記述先がMixinのspec_fileではなくMixinを使用するcomponentのspec_fileになる
- 複数のcomponentから使われるので,共通部分は共有スペックに記述する
- Mixin内の呼び出しをテストするためであり,Mixinを使うcomponentからの呼び出しのテストではないので,idを分ける
- ほかからの影響を受けやすく,より複雑になる
- 適している場面
- Mixinを使うときにcomponentが特別なmethodを実装する必要があるとき
- componentがそれらのmethodを正しく実装しているか,共有スペックでテストできる
- Mixinとcomponentの間のinterfaceをテストできる
- Mixinの提供する機能がcomponent側で上書きされることで破壊される可能性がある場合,共有スペックによりそれを検出できる
- Mixinを使うときにcomponentが特別なmethodを実装する必要があるとき
- Mixinを直接テストする
- \<body>に対する描画
- renderIntoDocumentを使う意図: pageと切り離されたDOM_nodeに対して描画を行うため
- 可能な限りこちらを使う
- 実際にcomponentがpageに描画される必要があるときは,renderを使う
- 親のDOM_nodeに対s手行った変更を注意深く元に戻す必要がある
- jasmin-react-helpersを使うと,テスト終了時に自動でcomponentを削除してくれる
- renderIntoDocumentを使う意図: pageと切り離されたDOM_nodeに対して描画を行うため
- server_sideのテスト
- Mochaを使う
- Nodeのecosystem内でとても有名で,非同期処理をうまく扱える
- spec_fileのtemplate
- npm run test-server
- doneでassertionの完了を知らせる必要
- chain上で呼び出す
- SuperTest
- CheerioでHTMLとしてパースしてassertする
- 404のテスト
- Mochaを使う
- browserを使ったテストの自動化
- E2E_testについて
- end_userの観点からapplicationの機能の正しさを検証するテスト
- 基礎
- browserを操作してWeb pageの内容が正しいか検査する
- CasperJS
- browserを簡単に操作するためのtool
- 内部的にPhantomJSを使っている
- PhantomJS
- JavaScriptから操作可能なheadless browser
- rendering engineとしてWebkitを使っている
- headless browser
- 画面に何も表示しない
- terminalからcommand経由で操作できる
- testで行うbrowser操作
- linkのclick, formの記入, URLの入力, drag and dropなど,userが通常行うbrowser操作
- Webkit
- Safariで使用されるrender engine
- ChromeはWebkitをforkしたBlinkというrender engineを使っている
- FirefoxはGeeko
- Internet ExplorerはTrident
- CasperJSでのテストがどのようなものか
- end_userの視点でbrowserを操作 → Reactの文字は出てこない(Reactを使っているかどうか関知しない)
- testが始まる前にnpm startでapplicationを起動する必要がある
- リンクのクリック
- thenのコールバック関数の中でテストを実行する
- serverの起動
- 自動的にserverを起動するためのscript
- spec_fileのポート番号を変更する
- E2E_testについて
- まとめ
- テストに関連するさまざまな概念について
- 描画のテスト
- 関数のスタブ
- componentのmock
- Mixinのテスト
- componentの検索
- server_sideでのテスト
- E2E_test
- テストに関連するさまざまな概念について
Part 4 実践
Ch16 architecture pattern
- Reactをほかのframeworkやarchitecture patternと併用してapplicationを構築する方法を見る
- routing library
- single_page_applicationにおいて,routerはURLをハンドラにマッピングするためのモジュール
- Backbone.Router
- Backbone: single_page_applicationを開発するためのlibrary
- MVW(Model-View-Whatever)の形態
- Backbone: single_page_applicationを開発するためのlibrary
- Aviator
- router機能のみを持つstand alone library
- 複数のtargetを指定できる
- react-router
- routerの定義自体がReact_componentで構成される
- path属性に基づいてrouting, 該当するRouteのhandler属性に指定されたcomponentを描画
- Link component
- page navigationに使える
- Om(ClojureScript)
- 永続データ構造を提供
- → Omはapplication全体を高速にrendering
- snapshotを容易に取得
- → undoのような機能を簡単に実装できる
- 永続データ構造を提供
- Flux
- Facebookにより考案されたarchitecture pattern
- 単一方向のdataflowを強制
- → Reactとともに使うことでapplicationの動作を推測しやすくなる
- 大きな変更が不要.容易に導入
- Store/Dispatcher/View(React_component_tree)で構成
- Actionを4つ目のパーツとみなせる
- Action: 補助的なmethodを提供しDispatcherのinterfaceとして機能する
- Actionを4つ目のパーツとみなせる
- 最上位のReact_componentはController-ViewとしてのRoleを担う
- Storeとchild_componentとの仲介役として機能する
- (iOSのViewControllerとは全く別物)
- Storeとchild_componentとの仲介役として機能する
- 各パーツは独立 → 関心の分離を強いられる → 個別テスト容易
- dataflow
- 情報が一方向に流れる
- merit
- applicationの動作が推測しやすい
- application stateを1か所に集めて管理できるようになる
- application stateはStoreに集約され,すべてのdataの取得/更新はStoreで管理
- Fluxを構成するパーツ
- Dispatcher
- applicationの中心的なRole
- すべてのデータがいったんDispatcherを通る
- singleton_object
- callback経由でStoreにイベントを通知
- 複数のcallback_functionの依存関係の管理はDispatcherの責務
- userやnetworkからの入力を登録しているすべてのStoreに通知し,各Storeがそれをチェックして自身に関係のあるinputのみを処理する
- Action
- user視点での入口
- userがUIに対して行った操作はAction経由でDispatcherに届けられる
- DSLとして機能する
- 1段階高いレベルの言語へと翻訳され,Storeが意味を理解できるActionに変換される
- userの操作はcomponent_treeをさかのぼってDispatcherに届けられる
- その過程で,userの操作はapplication固有のActionに変換される
- Store
- applicationのデータのやりとりを一手に引き受け,developerはここにbusiness_logicを記述する
- Dispatcherに登録したcallback_function経由でActionの通知を受け,自身の管理するデータに関係のあるActionの場合のみ応答する
- dataが更新された場合,change eventを発行してReact_component_treeに更新を通知する
- applicationのほかの部分からは厳密に分離
- Storeはapplicationのすべてのデータを保存
- applicationのほかの部分はデータの操作方法を知らない.Storeはapplicationで唯一データの変更が行われる場所になる
- Storeはgetter methodのみサポートし,setter methodを持たない
- dataの変更はすべてDispatcherからのcallback経由で行われる
- Controller-View
- Viewのtreeの最上位のcomponentがStoreとのやりとりを受け持つ
- componentはController-Viewと呼ばれる
- 規模が大きくなると複数のController-Viewとなることもある
- Viewのtreeの最上位のcomponentがStoreとのやりとりを受け持つ
- Dispatcher
- 複数のStoreを管理する
- 変更内容
- DispatcherはActionをqueueに格納するように変更
- DispatcherはActionの処理が完了するまで処理を停止できるように変更
- Dispatcherにcallbackを登録する際に,どのActionに対して待ち受けるか指定できるようにする
- Dispatcherの改善
- Dispatcherの返すidに対してWaitFor
- 変更内容
- まとめ
- Reactをほかのframework/architecture patternと併用してapplicationを構築する方法
- MVC framework ~ Flux patternまで,Reactの高い順応性
- Reactをほかのframework/architecture patternと併用してapplicationを構築する方法
Ch17 そのほかのusecase
- React: interactiveなUIを構築するための非常に強力なrendering library
- Web application以外の分野にも適用可能
- Reactが提供するデータおよびuser inputを処理する方式や,再利用可能でテスト容易な小さいcomponentを複数組み合わせてapplicationを構築するやり方
- desktop application
- atom-chellやNW.jsなどによる
- desktop上のinteractiveなapplicationを構築するためにReactを使える
- game
- gameは本質的にstate machineで,以下の基本的なRoleを持つ
- 画面の更新
- eventへの反応
- Reactは以下の2点に対してのみ責任を果たす
- DOMの更新
- eventへの反応
- Reactのvirtual_DOMの方式は,高性能のgame engineから着想を得たもの
- 2048の例
- immutable method
- dataの共有
- 不要な状態更新を避ける
- 柔軟な構造 → undoを簡単に追加できる
- ReactによりViewの動機を気にすることなくgame_logicとplayerとのやり取りに集中できる
- gameは本質的にstate machineで,以下の基本的なRoleを持つ
- HTML_mail
- React.renderToStaticMarkupを使う
- 与えられたcomponent_treeをHTML文字列に変換
- 独自のデータ属性を出力しない ←→ renderToString
- 実装例
- CSSファイルの追加
- 再利用・テスト可能なcomponentを組み合わせてinteractiveなUIを構築できるという利点をHTML_mailでも得られる
- React.renderToStaticMarkupを使う
- data_visualization
- まとめ
- 以下を見た
- Reactはbrowserで動作するWeb applicationだけでなく,desktop applicationやHTML_mailの作成にも使える
- game developmentでReactを利用する方法
- D3のようなlibraryと併用してdata_visualizationの用途で使用する方法
- 以下を見た
Appendix A 開発環境構築について
- Reactは開発版と製品版がある
- & アドオンありなし
- Nodeの使用
- Nodeのpackage management toolであるnpmを中心に,frontend developmentのための様々なtoolのecosystemが形成されているため
- Nodeを使わない開発環境構築手順,react-tools
- JSXTransformer/react-tools
Appendix B API reference
- 3種類
- 用語の整理
- component仕様
- React.createClass()に引数として渡すobject
- render methodなどのlifecycle methodを定義する
- component class
- React.createClass()の戻り値として得られるobjectか,またはES6 class.
- JSXにタグ名として直接記述するか,React.createElement()の引数として渡す
- ReactElement
- React.createElement()の戻り値として得られるobject
- 内部的にはすべてのcomponent classはReactElementに変換される
- React.render()の第一引数やrender methodの戻り値において使用される
- component instance
- ReactElementをもとにReactが生成するobject
- component仕様で定義したmethod内で,this経由で参照できる
- 外部からcomponent instanceにaccessするために,React.render()の戻り値もしくはref属性経由で参照を取得する
- component仕様
- top level API
- React
- libraryとしてのentry point
- React.createClass
- React.createElement
- React.createFactory
- React.render
- React.unmountComponentAtNode
- React.renderToString
- React.renderToStaticMarkup
- React.isValidElement
- React.findDOMNode
- React.cloneElement
- React.DOM
- React.PropTypes
- React.initializeTouchEvents
- React.Children
- React.Children.map
- React.Children.forEach
- React.Children.count
- React.Children.only
- React
- component API
- setState
- replaceState
- forceUpdate
- getDOMNode
- isMounted
- setProps
- replaceProps
- component 仕様
- object
- propTypes
- mixins
- statics
- displayName
- lifecycle method
- render
- getInitialState
- getDefaultProps
- componentWillMount
- componentDidMount
- componentWillReceiveProps
- shouldComponentUpdate
- componentWillUpdate
- componentDidUpdate
- componentWillUnMount
- object
『エッセンシャル スクラム: アジャイル開発に関わるすべての人のための完全攻略ガイド』学習メモ
まえがき
- Essential Scrum
- 基礎に,coreとなる価値,原則,practiceの小さなまとまりがある( → 集まってframeworkになる )
- どのようなapproachを組み合わせるべきかわかるまでは,Scrum frameworkを忠実に守った方が良い
Ch01 Introduction
- What's Scrum
- Scrum: 革新的なProduct, Serviceを開発するためのapproach
- Agile approach
- product backlog
- iteration
- 機能横断型チーム
- Scrumの起源
- all-at-once product development(団結型product開発): scalable チーム based approach
- Object Oriented developmentの概念・経験にもとづくprocess control
- iterative and incremental development
- softwareのprocessと生産性に関する研究
- 複雑で適応力のあるsystem
- Scrumの革新的な価値と原則は,softwareとは異なる種類のproduct開発や,さまざまな仕事のflowを生み出すためにも使える
- Why Scrum
- 事前の設計を創発的な設計とjust_in_timeで組み合わせられるapproach
- より機能横断的
- ゲノミカ社の顛末
- Scrumは役に立つか
- 頻繁なrelease
- 組織的な機能不全や無駄を晒す
- iterationのたびに動作・統合・テスト・business的に価値のあるfeatureのdeliveryに注力
- Cynefin framework
- 複雑な領域
- 創発的
- 検査と適応
- Scrum〇
- 探索(調査)・理解(検査)・反応(適応)
- 込み入った領域
- 単純な領域
- 正統的なbest practiceの領域.既知のsolution.
- 適切に定義され,反復可能な手順が組み合わせられたprocessを用いたほうが,Scrumより上手くいく
- chaosな領域
- 状況と行動に対して誰かが責任を負う
- Scrumは×
- 無秩序
- 状況を構成要素に分解して,それぞれをそのほかの4つの領域に当てはめる必要がある
- 割り込み駆動の作業
- Scrumは×
- カンバンが〇
- 推奨
- 作業がsystemをどのように流れているか可視化
- 各手順で仕掛け中の作業(WIP)を制限.こなせる以上の作業はしない.
- system内の作業の流れを計測,最適化して,継続的に改善
- softwareの保守とsupportに〇
- systemごとにScrumとカンバンの使い分けもよい
- 推奨
- 複雑な領域
- まとめ
- Scrumは複雑なproduct開発という営みの変化を受け入れられる
- 組織のpotentialを妨げている機能不全や無駄を可視化
- Scrumは複雑なproduct開発という営みの変化を受け入れられる
Part 1 Core Concept
Ch02 Scrum framework
- Scrum frameworkの概要
- practiceに焦点を合わせ,役割,activity,作成物について見る
- 概要
- Scrum: 作業をまとめあげ,管理するためのframework
- value,principle,practiceに基づき,組織に合ったengineering practiceや,Scrumのpracticeを実践するための具体的なapproachの追加ができる
- → originalのScrumができあがる
- 建物の土台や壁のようなもの.備品や機能の付加だけが可能.
- 人間中心主義
- 正直,開放性,勇気,尊敬,集中,信頼,権限付与,協力といった価値に基づく
- practice: role, activity, 作成物, それらに関連するruleで構成
- Scrum: 作業をまとめあげ,管理するためのframework
- Scrumの役割
- 1つ以上のScrum_teamから構成
- product_owner
- productのleadershipの権限
- どのfeatureや機能をどの順番で構築するか判断する責任
- チームの目標に明確なvisionを持ち,関係者全員に伝える
- 開発や保守を含めたsolution全体を成功させる責務
- 最も価値の高い作業が行われるようにする義務
- Scrum_masterや開発チームと協力.質問にすぐ答えられるようその場にいる必要.
- Scrum_master
- Scrumのvalue, principle, practiceを関係者全員が理解し,受け入れられるよう手助けする
- as a coach, processについてleadershipを持ち,チームがoriginal Scrum approachを育てられるようにする
- 組織変革のmanagementを支援
- as a facilitator
- impediment(ゴルフでのコース上の小石など)を取り除くときにleadershipを発揮
- not a manager, but a leader
- development_team
- productをdesign, develop, testする責任
- product_ownerが設定した目標を達成するための最善の方法を決定
- 5人~9人
- Scrumのactivityと作成物
- flow
- vision → groomingでfeatureに分解 → product backlog
- sprint planningの結果: forecast/ commitment
- sprint backlog
- sprintの実施 with daily_Scrum
- daily_Scrum: 作業の流れの管理のため,検査と同期を行う適応型planning activity
- → product increment
- sprint review: productをstakeholderとScrum_teamが検査
- sprint_retrospective: processをScrum_teamが検査
- product backlog
- product_ownerが,stakeholderと協力して収集,定義し,正しい順序にする
- 順序: value, cost, knowledge, riskから
- grooming
- prioritized/orderedはどちらでもよい
- 各itemの大きさ・コストを認識
- 相対基準
- product_ownerが,stakeholderと協力して収集,定義し,正しい順序にする
- sprint
- timeboxing
- product planning
- product_ownerと開発チームがsprint goalを合意
- 持続可能なpace
- sprint backlog: 第2のbacklog.product backlog itemごとに細かいtaskに分解したもの.
- just_in_timeのplanning
- 期間: 2週間~1か月.sprint planningは4時間~8時間
- simple cycle: product backlog itemを1つ選び,itemをtaskに分解し,選んだitemがsprintに収まるかjudge
- taskは1時間単位で見積もる
- sprintの実施
- 完成: 高品質な機能を達成するのに必要な作業をすべて終えたという強い自信があること
- チーム memberがtask levelの作業を定義.自己組織化して実施.
- daily_Scrum
- sprint期間中の毎日・同じ時間に,15分以内のtimeboxで実施
- a.c.a. daily standup
- 検査と適応のactivity
- Scrum_masterがfacilitatorとして,各memberがほかのmemberに3つの質問
- 前回のdaily_Scrumのあと,自分が何をしたか
- 次回のdaily_Scrumまでの間,どんな作業をするか
- 進捗を妨げる障壁・impedimentは何か
- → 全員が今の状況の全体像を理解
- sprintで作業をすばやく柔軟に実施するうえで欠かせない
- sprint backlog itemの状況を開発チーム member間で共有するためのもの
- 同期および検査と適応の毎日のplanning activity
- 完成
- sprint の成果: 出荷判断可能なproduct increment
- 完成の定義: 品質・出荷判断可能であることへの,自信の度合い
- 出荷判断可能: sprintで構築したものに対する自信の度合い
- 終わらせるべき重要な作業で,完了していないものがないということ
- sprint review
- 構築中のproductの検査と適応
- 参加者同士が会話
- 開発activity全体へのreview
- 今起きていることを可視化,次の開発をガイド,ビジネス上最も適切なsolutionが確実に作り出されるようにするための機会を得る
- チーム内外で情報が双方向に流れるようになる
- sprint_retrospective
- processの検査と適応
- Scrumと関連する技術的なpracticeについて議論
- 継続的なprocess改善
- 現実的な数のprocess改善actionを特定し,実施をcommit
- 次のsprintで取り組む
- 現実的な数のprocess改善actionを特定し,実施をcommit
- processの検査と適応
- flow
- まとめ
- Scrum frameworkの役割,activity,作成物のすべてに焦点を当てて,Scrumのcoreとなるpracticeを見た
Ch03 Agile principle
- Agile principleをtraditional 計画駆動のproduct developmentのprincipleと比較
- Scrumとの違いの理解の下準備
- 概要
- a.c.a. 順次的なprocess
- 計画駆動の開発
- 基礎となる信念が,product開発に伴うUncertaintyと見合わない
- 変動性とUncertainty
- 役立つ変動性を受け入れよ
- product developmentは製造業とは全く異なる
- 製造業: 要件を固定し,よく理解された工程に従うことで,(定義された振れ幅の中で)常に同じ完成品を作り出す
- product development: ほかにないproductをただひとつだけ作り出す
- 1つだけのproduct: 料理のrecipeに似ている
- product developmentは製造業とは全く異なる
- iterativeでincrementalなdevelopmentを採用せよ
- iterative: 計画された手戻り戦略
- 構築しているものを改善するために,いくつもの道を辿る
- 優れたsolutionに辿り着く
- 最大の欠点: Uncertaintyがあるため,どれだけの改善があるか事前に判断・計画することが難しい
- 構築しているものを改善するために,いくつもの道を辿る
- incremental: 全部作る前に少し作ってみる
- productを小さい部品に分割し,その一部を構築して,実際の環境でどのように動くか学習し,その学習に基づいて適応し,続きを構築する
- developmentを調整し,やってきたことを変更するうえでの重要な情報を獲得する
- 最大の欠点: 部品ごとに構築すると,全体像を失ってしまうriskがある
- 併用することで,個別に使う場合の欠点を埋められる
- Scrumではsprintと呼ばれるtimeboxingされたiterationで適応を繰り返すことで,iterative and incrementalという考え方を実現する
- Water Scrumは×
- featureに対して作業をし,統合・テストを経ることで,全体的な視点でproductを見られる
- sprintの結果についてfeedbackを受け取ることで,適応ができるようになる
- iterative developmentと継続的な改善に対するcommitmentの一部として,将来のsprintでやり直しを計画できる
- → どの程度の改善が必要か事前に正確に知ることができないという課題を克服
- 適切で経済的にも意味のある回数のiterationを行いつつ,productをincremental development
- iterative: 計画された手戻り戦略
- 検査,適応,透明性(経験的なprocess control)を通じて変動性を活用せよ
- 計画駆動とScrumの違い
- processが定義される度合い
- 成果のばらつき
- 使われるfeedbackのtiming, 量
- Scrumの核心が,検査,適応,透明性
- 構築するものだけでなく,構築する方法も検査と適応を行う
- このために透明性が必要
- 透明性: productを作り出すための重要な情報はすべて,productの構築にかかわる人が入手できること
- communicationの基礎: 透明性により,観察・理解が可能になる
- → 信頼関係
- このために透明性が必要
- 構築するものだけでなく,構築する方法も検査と適応を行う
- 計画駆動とScrumの違い
- あらゆる形のUncertaintyを同時に削減せよ
- 目的(what)のUncertainty: 最終的なproductのfeatureを取り巻くUncertainty
- 手段(how)のUncertainty: product developmentに用いられるprocessやtechnologyを取り巻くUncertainty
- 顧客(who)のUncertainty
- iterative and incremental developmentにより容易に同時の削減ができる
- 検査と適応,透明性を定常的に行うことで実現
- 未知の未知を学習できる
- 役立つ変動性を受け入れよ
- 予見と適応
- 予見への願望と適応の必要性との間のバランスをとる
- 選択肢を広げておく
- 最終責任時点(LRM)
- 拙速な判断は×
- 判断しないcostが判断するcostを上回るときに決定する
- 判断に関する理解が深まれば,判断のcostが下がっていく
- 最終責任時点(LRM)
- 事前に正しく行うことは不可能だと認める
- 重要な知識がない状態では,精度の低い要件を大量に作ってしまう
- 目的のUncertaintyが見えなくなる
- 無駄になる
- Scrumでは,ある程度は事前に要件を集め,計画を立てるが,必要な分だけ
- 詳細は,構築中のproductの学習に合わせて埋めていく
- 重要な知識がない状態では,精度の低い要件を大量に作ってしまう
- 適応型で探索型のapproachを好む
- 適応: 何らかのactivityで知識を得ること
- prototypeの構築,概念実証(POC),勉強,実験
- 調査によって情報を買う
- tool, technologyが,調査のcostを大きく下げる
- 適応: 何らかのactivityで知識を得ること
- 経済的に妥当なやり方で変化を受け入れる
- 予見のし過ぎは逆効果
- 自己成就的な予言pattern
- Scrumでは変更cost曲線をできるだけ平たんにする
- processにおける作業の量と流れを管理し,時間の影響を変更costに与えないようにする
- 多くの作業の成果物(詳細な要件,設計,testcaseなど)をjust_in_timeで作り出す
- processにおける作業の量と流れを管理し,時間の影響を変更costに与えないようにする
- 予見のし過ぎは逆効果
- 予見的な事前の作業と適応型のjust_in_timeの作業とのbalanceを取る
- 経済的に妥当なやり方で,素早いfeedbackに基づく適応の量を最大化し,事前の予見を最小化する
- かつ,complianceや法規制,企業の目的から外れない
- 革新的なproductを素早く開発するためには,適応すべきことに対して,chaosにならない程度の予見が必要
- chaos → 作業が非効率
- 経済的に妥当なやり方で,素早いfeedbackに基づく適応の量を最大化し,事前の予見を最小化する
- 検証による学び
- 想定を確認したり反証したりする知識を得たとき
- 重要な想定をまず検証する
- 想定: 推測や信念.検証による学びがまだのもの
- 開発に重大なrisk
- Scrumでは最小化かつ残る期間を短くする
- iterative and incremental developmentをlow cost researchと組み合わせる
- 想定: 推測や信念.検証による学びがまだのもの
- 複数の学習loopを並行して活用
- Scrumにおける継続的な学習
- feedback loop
- 予め定義された複数の学習loop
- daily_Scrum, sprint review
- pair programming(秒単位のfeedback), test driven development(分単位のfeedback)
- Scrumにおける継続的な学習
- 素早いfeedbackのためのworkflowをまとめる
- Scrumでは作業の直後にfeedback
- 経済的恩恵が大きい
- 学習loopをすばやく閉じる
- riskを小さくできる
- Scrumでは作業の直後にfeedback
- 仕掛け中の作業(WIP)
- 作業は経済的に妥当なサイズにまとめよ
- 計画駆動: all-before-any型approach
- batch サイズ: 100%
- 規模の経済をproduct developmentに当てはめたもの
- Scrum: batch サイズを小さくする
- 規模の経済の当てはめは重大な経済的損失を呼ぶ
- 恩恵
- cycle time削減
- flowの変動性の低減
- feedbackの推進
- riskの低減
- overheadの低減
- 士気と危機意識の向上
- cost, scheduleの肥大の抑止
- 1個流しにこだわることはない
- 作業の流れ,全体の経済性も重要
- 計画駆動: all-before-any型approach
- 在庫を認識し,適切な流れで管理
- 在庫: WIP
- 大量の在庫があるときの変更は,重大な無駄につながる
- → just_in_timeで在庫管理を健全に行う
- 要件が多すぎると,要件の変更時に在庫の無駄が生じる
- 要件が十分にないと,作業をすばやく流せない
- → ちょうどよい在庫と多すぎる在庫との間で適切なbalanceを見出すことが目的となる
- 在庫は要件だけではない.ほかのものも管理要
- 作業者の手待ちではなく,作業の手待ちに注目
- 作業の手待ち: 実施したいのに何らかの原因でできない作業
- 作業者の手待ち: capacityに余裕があり,もっと作業ができる人
- 走者ではなくバトンを見よ
- queue理論のgraph
- 作業の手待ち(遅延した作業)は,いったん高稼働率になると,指数関数的に積みあがる
- → 作業の流れの中でbottleneckを見つけ削減する,という経済的に意味のあるactivityに注力する
- delay_costに考慮
- delay_cost: 作業の遅延やmilestoneの達成の遅延に関連した,経済的cost
- どの種類の無駄が経済的に損失を与えるか算出できる
- 例
- delay_cost: 作業の遅延やmilestoneの達成の遅延に関連した,経済的cost
- 作業は経済的に妥当なサイズにまとめよ
- 進捗
- 自分たちがdeliveryし,検証したものによって測る
- realtimeの情報に適応してreplan
- plan: 物事がどう進むか事前に予見しただけのもの
- 経済的に重要な情報の流れに適応
- 開発期間中に継続的に行う
- 動作する資産を検証することで進捗を測る
- 動作する資産を検証 → 重要な想定の検証 → 次のstepへのfeedback
- 顧客にとって価値のある作業をどれだけ終わらせたかがimportant
- valueを主眼に置いたdeliveryに集中
- 中間成果物は顧客が認識できる価値をもたらさない.目的のための手段に過ぎない.
- それ自体が重要なfeedbackを生み出したり,重要な知識を得るために役立つことに意味がある
- 作業効率
- 素早く進む,しかし,あわてない
- 機敏 & 状況に適応 & speed
- 機敏 & 持続可能なpace & 品質
- 品質を作りこむ
- 機能横断型のScrum_teamに責任
- 継続的に作りこんで,sprintごとに確認
- valueのincrement: 強い自信をもって完成され,productに組み込んだり出荷しようと思えばできるようになっている
- 必要最低限の儀式
- 不要な形式主義の儀式 ←→ Scrumは価値中心的
- processのためのprocess
- documentを書くcase
- productの一部
- 重要な議論や意思決定,合意を記録して,何があったのか将来分かるようにするという目的
- 新しいmemberが追い付くうえで役に立つ,価値の高い方法
- 規約上の要件
- 不要な形式主義の儀式 ←→ Scrumは価値中心的
- 素早く進む,しかし,あわてない
- まとめ
- 核心的なAgile principle
- Scrumの方法を導く基本的な信念
- 計画駆動との違い
- 核心的なAgile principle
Ch04 sprint
- sprint: 1週間~最大1か月までのcycleの繰り返し
- timeboxing
- 短く固定された期間に,一度設定した目標をなるべく変えず,チームで合意した完成の定義を達成した出荷判断可能なproduct incrementを生み出す
- 概要
- sprint: Scrum frameworkの枠組み
- goal, memberは変えない
- 特性は,あらゆるチームのあらゆるsprintに当てはまる(一部例外がある)
- sprint: Scrum frameworkの枠組み
- timeboxing
- 時間管理のtechnique.
- 仕事の能率を高め,scope managementを支援
- sprint goalに向けて選択した作業を完了するために,持続可能なpaceで働く
- merit
- WIPの上限設定
- 優先順位づけを強制
- 重要な少量の仕事が優先される
- 集中力を研ぎ澄ます??
- 進捗状況の可視化
- sprintの最終日という締め切りまでに重要な作業が完了し,検証されているかどうかという観点で進捗を可視化できる
- 不必要な完璧主義を避ける
- 期限がある
- 締めくくりの促進
- 切迫感
- 予測可能性を改善
- 予測の範囲がはっきりする
- 短期間
- 計画の立てやすさ
- すばやいfeedback
- 投資収益率の改善
- 損失の限定化
- 失敗しても,短い期間を失うだけで済む
- 熱狂を取り戻す
- 待ち時間が少なくなる
- 目に見える進捗,終わりがある
- 頻繁なcheckpoint
- 動作するfeatureを実際に見て,誰もが意思決定できる
- immutable sprint term
- 例外
- feedback speed up
- 休暇,会計年度末
- release span
- 一定のrhythmのmerit
- 予測可能,計画が立てやすい
- 安定した健康的な鼓動
- 高速で柔軟なbusiness valueのflowを実現するため
- zoneに入りやすくなる,roleを果たせるようになる,波に乗りやすくなる
- 平凡だが必要不可欠なactivityが習慣化される
- 短いrhythmで,仕事への集中力を一定に保てる
- 調整のためのoverheadが大きく減少
- 簡潔なplanning
- velocityへの信頼度up
- sprint単位で平準化
- releaseまでのsprintの回数がわかる
- velocityへの信頼度up
- 例外
- immutable goal
- sprint goal: sprintを要約したもの.取り組むbusiness上の目的とvalue.
- 明確で1つの事柄に焦点を当てる
- 複数のgoalとなることもある
- development_teamが洗練・合意し,sprintで完成できるproduct backlog itemを特定する
- sprint goalをさらに精緻化
- 相互commitment
- 変更と明確化
- 変更は×,明確化は〇
- 変更の影響
- あらゆる点でloss大きい
- 実利的であるべし
- 経営状況や障害など実利的に問題があれば,ruleより実利を優先する
- 変更の必要性を,経済的な観点でproduct_ownerがチームと議論できれば,やる気と信頼は高まる
- sprintの中止
- 経済的な事情
- 奥の手
- sprint goal: sprintを要約したもの.取り組むbusiness上の目的とvalue.
- 完成の定義
- 出荷判断可能な
- ≠ 出荷しなければならない
- sprintで構築したものが完成しているという自信
- 出荷するために必要な仕事(重要度の高いテストや統合など)が確実に完了している
- 概念的には仕事のcheck listのようなもの
- 変動要因
- 成果物の性質
- 成果物に使われている技術要素
- 成果物を構築した組織
- 現在の可能性に影響しうるimpediment
- 変動要因
- sprint期間より長くなりうるテスト
- 自動化に取り組む
- 完成の定義は時間とともに改善
- 完成の定義と受入条件
- 受入条件: product_ownerが規定した満足条件
- 受け入れテストで確認
- product backlog itemは,個別の受入条件とsprint levelの完成の定義の両方を達成して,完成したとみなされる
- 受入条件: product_ownerが規定した満足条件
- 完成と完全に完成
- 出荷判断可能な
- まとめ
- Scrum frameworkにおけるsprintの果たす重要な役割
- sprint: ほかのactivityや作成物を配置するEssential Scrumの枠組み
- 短い,timeboxing,期間は固定
- sprint goalで定義
- 経済的な妥当な理由以外は変更不可
- 出荷判断可能なproduct incrementを生み出す
- 全員で合意した完成の定義を満たす
- sprint: ほかのactivityや作成物を配置するEssential Scrumの枠組み
- Scrum frameworkにおけるsprintの果たす重要な役割
Ch05 要件とuser_story
- 要件の扱いが伝統的なprojectとどう違うか
- user_story: business valueを表現する共通形式
- 概要
- Scrumでは,要件の詳細はprojectを通じて継続的に対話しながら調整
- 要件はjust_in_time.チームが開発を始める必要なときに,必要な分だけ
- business goalを達成するために操作できる重要な自由変数
- Product Backlog Item(product_backlog_item): 要件の置き場所.それぞれが何らかのbusiness valueを表す
- 会話の中で,より詳細なproduct_backlog_itemに分解し,最終的にsprintに入る大きさまで小さく・詳細化される
- 設計・構築・テストができる
- Product Backlogはproduct_backlog_itemの集合のsnapshot
- product_backlog_itemの標準形式はない
- user_storyやusecaseなど
- 会話の中で,より詳細なproduct_backlog_itemに分解し,最終的にsprintに入る大きさまで小さく・詳細化される
- 対話する
- 要件: 構築しようとしているproductに欠かせない事柄についての共通理解を促進するcommunication手段
- 音声によるcommunicationは,帯域が広く,feedbackもすばやく,安くて簡単に共通理解が得られる
- 双方向communicationが問題解決のためのideaの発見を促す
- Product Backlogは生きているdocument
- 改良し続ける
- 段階的洗練戦略
- what's user_story
- 様々な種類のproduct_backlog_item(特にfeature)に期待されるbusiness valueを表現する便利な形式
- business側と技術側ともに理解しやすくなっている
- 単純な構造,会話のために優れたplaceholder
- 段階的改良もしやすい
- Agile development principleとneedsを効率的に結びつける軽量approach
- 要件の詳細化として,重要・役立つ情報の記録のために使う
- Card
- userの分類(役割),目的,求める理由(利益)を具体的に書く
- 要件の意図や本質を捉えた数行の文章にする
- 関係者をより詳細な議論に導く
- 対話(Conversation)
- Conversationは,要件の詳細を適切な時期に具体化・共有するためのstart point
- user_storyはConversationのための約束事
- user_storyが,書くことに向けていた注意を対話に向ける
- 豊かな情報のやり取りと協調をもたらす
- 正確な要件を誰にも理解できる表現にする
- 文書と相互補完的
- Conversationは,要件の詳細を適切な時期に具体化・共有するためのstart point
- 確認(Confirmation)
- 満足条件,受入条件
- 期待される振る舞いを示す
- 受入テスト
- 実例による仕様,ATDD(受入テスト駆動開発)
- 具体例を精緻化し,user_storyを作成・改善するprocessを駆動できる.user_storyに自動化された受入テストも用意できる
- 満足条件,受入条件
- 様々な種類のproduct_backlog_item(特にfeature)に期待されるbusiness valueを表現する便利な形式
- 詳細化のlevel
- うまく書けたuser_storyとINVEST
- INVEST: user_storyが書き手の意図に合うかどうかを評価するために有用なprinciple
- Independent
- 依存性を最小化
- Negotiable
- user_storyは内容と理由を書く
- 規制や制約がなければ,方法がNegotiableとなるようにする → Innovation
- user_storyは内容と理由を書く
- Valuable
- 顧客と利用者双方にとって → product_ownerから見てすべてのstoryはValuableが要
- technical storyはProduct Backlogに入れない.business storyの関連タスクとする
- Estimatable
- storyの規模,労力,costが目安
- Small
- 近いうちに取り掛かりたいstoryは小さくする必要がある
- sprintで扱うため
- 近いうちに取り掛かりたいstoryは小さくする必要がある
- Testable
- 確認 → 5.4
- 必須ではないし不可能なこともある
- 非機能要件
- system levelの制約
- できる限り多く完成の定義に含める
- → すばやいfeedback
- 学習のためのstory
- prototype, POC, 実験, 学習, spike
- 探索的activity
- cost, valueの算出・比較
- fail first戦略もある
- storyの収集
- userは作家というより批評家
- userにチーム memberとして関与してもらう
- user_story記述workshop
- 目的: business valueについて協調的にbrain storming,想定するproduct/serviceのためのuser_storyの類型を作る
- 初回はuser role analysis
- personaというprototype的人物描写も作成する
- 進め方は上手くいくやり方でいい
- story mapping
- user中心指向の観点からuser_storyを作成
- 抽象的なuser actionを,詳細taskによって組み立てられるworkflowに分解する
- epic/theme/sprint可能なstory
- userにとってのvalueから優先順位づけ
- storyの分解とuset中心設計の考え方の結びつけ
- user目線での行為の流れを示す
- 顧客の価値という大きな観点からの,個々のstoryの関連性が分かる
- workflow: contextとなり,関係するstoryに注目できる
- user_story記述workshopに,優先順位を補完する
- まとめ
- Scrumでの要件の扱いを見た
Ch06 product_backlog
- 概要
- product_backlog: productに求められる機能を,優先順位をつけてリスト化したもの
- 何を作るか,どの順番で作るかについての認識が一か所にまとまり,共有できる
- Scrum frameworkの肝
- product_backlog: productに求められる機能を,優先順位をつけてリスト化したもの
- product_backlog_item
- だいたいはfeatureや機能項目を表し,userや顧客の目に見える価値をもたらす
- user_story形式で書かれることが多いが,書式は不定
- good product_backlogの特徴
- Detailed appropriately(適切な詳細度)
- 取り掛かる時期に合った詳細度.近いほど細かい.
- 適切なtiming
- Emergent(創発的)
- 時間とともに姿を変える
- 対応可能になっている
- Estimated
- story_point, 理想日を使う
- 近いものは小さめで正確な見積もり
- 遠いものは見積もりはしなかったり,Tシャツのサイズ程度の見積もりになることもある
- Prioritized
- 遠いものは細かい優先順位不要.全体のlevelでの優先順位程度でいい
- Detailed appropriately(適切な詳細度)
- grooming
- DEEPなproduct_backlogのために,product_backlogの中身を整理
- product_backlog_itemの作成と改良(詳細の追加)/product_backlog_itemの見積もり/product_backlog_itemの優先順位づけ の総称
- product_ownerが主導する全員での共同作業
- 開発チームは,各sprintの作業時間の最大1割程度までgrooming用に時間を確保
- timing
- 最初はrelease planningのときに,stakeholderとともに
- 週に1度かsprintに1度程度で,development_teamとともにgrooming workshop
- daily_Scrumのあとにincremental groomingを行うこともある
- 全員でやる必要はない
- daily_Scrumのあとにincremental groomingを行うこともある
- sprint reviewの際にも行う
- きちんとprocessに組み込みさえすれば,timingは重要ではない
- business valueを柔軟かつ高速にdelivery
- 準備完了の定義
- grooming: Backlogの最初のitemを準備完了にする
- 作業のchecklist
- product_backlog_itemについて必要なことのリスト
- 厳しく設定すると,sprint goalの達成可能性が大きく上がる
- flow management
- product_backlog: 不確実性がある状態でも高速かつ柔軟にvalueを提供する流れを実現できる
- 重要な情報は絶え間なく少しずつやってくる
- → 作業を整理して管理し,頻繁にやってくる情報をすばやく効率的に処理しながら対応する必要
- release flow management
- release planningに備えたgrooming
- 必須/できれば含める/含めない の3 エリアに分ける
- sprint flow management
- groomingにより,feature群をsprintに流し込める
- product_backlogの先頭のitemは,明確に定義されていてテスト可能な状態
- product_backlogを要件のpipelineと考えてgroomingが〇
- pipelineからsprintに流し込まれた要件を,設計・開発・テスト
- product_backlogを上がっていく間にgroomingで要件が見直されていき,準備完了な状態になる
- development_teamが理解できるほど詳細で,1回のsprintで完了できる規模になる
- 必要十分なだけのproduct_backlog_itemを在庫として抱え,一定のflowを作ることがimportant
- sprint 2回から3回分程度のstoryを準備完了状態で抱えておくとうまくいく
- groomingにより,feature群をsprintに流し込める
- product_backlog: 不確実性がある状態でも高速かつ柔軟にvalueを提供する流れを実現できる
- どんなproduct_backlogをいくつ用意するか
- 基本原則: Productごとにproduct_backlogを1つ用意
- 注意が必要なcaseある
- Productとはなにか
- さまざまな捉え方
- goal: component_teamの数をできるだけ減らす
- → packagedで,デリバリー可能であり,利用者に価値をもたらすものに合わせて,product_backlogを並べる
- 大規模product -階層化backlog
- できるだけ1つのproduct_backlogがよいが,現実的に不可能なこともある
- → 階層化backlog
- feature area単位のBacklog Itemは.対応するproduct_backlogのitemよりも,サイズが小さい
- release train → Ch12
- portfolio_backlog, program backlog, チーム backlogの3 level
- 複数のチーム -単一のproduct_backlog
- Productごとに1つのBacklog → Productにかかわるすべてのチームがproduct_backlogを共有するため
- Product全体で見たときの経済的最適化可能
- 各チームがproduct_backlogのどのItemに対応できるのか知っておく必要
- → 共有Backlogに対するチームごとのviewを用意する
- チームの交換可能性に価値がある
- Productごとに1つのBacklog → Productにかかわるすべてのチームがproduct_backlogを共有するため
- 単一のチーム -複数のProduct
- チームは複数のProjectの担当は極力避ける
- 少なくとも,同時に複数のProductにかかわらないようにする
- どうしても同時に複数のProductの作業をするときは,各product_backlog_itemを1つのproduct_backlogにまとめる
- 基本原則: Productごとにproduct_backlogを1つ用意
- まとめ
- product_backlogが果たす,不確実性のもとで高速かつ柔軟に価値を提供する流れを実現するための役割
Ch07 見積もりとVelocity
- 概要
- まず作ろうとしているもののサイズを見積もる
- さらに,その作業におけるVelocityを計測
- 各sprintで完成したItemの見積もりサイズを合計することで出せる
- → 期間・costを導ける
- いつ何を見積もるのか
- Portfolio Backlogの見積もり
- 作成するすべてのProductの優先順位づけ
- ざっくりとした相対的な見積もり(Tシャツのサイズていど)
- Product Backlogの見積もり
- product_backlogのgroomingの一環として行うactivity
- 見積もりmeeting @初回のrelease planning
- 以降も新たなproduct_backlog_itemの見積もりが必要なら見積もりmeetingを招集する
- 成熟したScrum_teamならすべてのproduct_backlog_itemを同程度のサイズにまとめられる
- 見積もる理由
- すべてのproduct_backlog_itemが同時に同じサイズにはならない
- チーム memberがすべてのproduct_backlog_itemのサイズを同程度にまとめられるようになるには,それなりに時間がかかる
- 同じサイズにすることが目的となってしまい,不自然なところでstoryを分割せざるをえなくなることを避ける
- 最重要なこととして,見積もりの最大の目的は,対話の過程でいろいろな気づきが得られる
- 健全な議論を促す
- taskの見積もり
- commitmentが達成可能だと確信するため
- 大きさは理想時間単位で判断(a.c.a. 工数)
- Portfolio Backlogの見積もり
- product_backlog_itemの見積もりの考え方
- チームで見積もる
- development_teamが,全員で見積もる
- 全員の意見をまとめて,それぞれのproduct_backlog_itemのサイズを判断できるようにする
- 見積もりはcommitmentではない
- 数字を操作しないでよいようにする
- 正確性か精度か
- 無駄は×
- やり過ぎると逆効果
- 相対サイズの見積もり
- チームで見積もる
- product_backlog_itemの見積もりの単位
- story_point
- product_backlog_itemの大きさを計測する指標
- storyの複雑さ,物理的サイズなどを組み合わせて,相対サイズを測る単一の指標としたもの
- development_teamの視点からの,storyの完成に要する作業を表す
- 理想日
- 誤解を招くriskあり
- あと(理想日)日で終わるというわけではない
- 作業日数(人日)で表す
- 誤解を招くriskあり
- story_point
- planning poker
- memberの同意ベースで見積もりを行うtechnique
- 見積もりの尺度
- フィボナッチ数列や2の累乗などで,同程度のサイズのproduct_backlog_itemをひとまとめにして同じ数字をassign
- 進め方
- merit
- チーム memberをまとめて,正確な見積もりとなる
- 議論によりproduct_backlog_itemの理解が深まる/共有できる
- Velocity
- 各sprintで完成した仕事量
- 計測対象: 生産量(サイズ), not 成果(value)
- 利用目的
- Scrumでのplanning
- チームの現状を診断
- Velocityの幅の算出
- 幅を持たせる → 不確定要素があることが伝わる
- min/max sprint数が分かる
- 最大値と最小値の平均や,90%信頼区間など単純計算で求める
- Velocityの予想
- sprint planning
- commitしたproduct_backlog_itemのサイズを合計してチームの予想Velocityとする
- 2回のsprintで見積もりを行うなどで,幅を求める
- sprint planning
- Velocityへの影響
- 向上のための手段や悪影響のある手段など
- Velocityの誤用
- performanceの指標としては使えない
- 数字の操作(pointのインフレなど)や手抜きが起こる
- 見積もりの正確さの検証,チーム内での改善がどの程度進んでいるのかの判断にのみ使う
- performanceの指標としては使えない
- まとめ
- サイズの見積もり,Velocityの算出,期間の求め方
- 各levelでの見積もり
- story_point/理想日
- planning poker
- Velocityの使い方
Ch08 技術的負債
- 概要
- 負債の返済のために十分に注意が必要
- systemの設計や実装は,業務ドメインに関する知識をよく反映できるように発展させていく必要
- 無責任や不注意といった原因で発生する,ナイーブな技術的負債
- 不可避な技術的負債
- 戦略的な技術的負債
- 技術的負債の行く末
- 技術的負債を生む要因
- 納期を守るためのpressure
- Velocityの偽装
- testを減らせばVelocityが向上するという神話
- TDDのような優れた手法を使うのが〇
- 負債が生む新たな負債
- 納期を守るためのpressure
- 技術的負債の管理
- どんなproductも技術的負債からは逃れられない
- 技術者の言い分とbusiness関係者の言い分のbalanceを取る
- product_ownerが存在する理由の1つ
- 技術的負債の発生の管理
- 優れた技術的practiceの活用
- ナイーブな技術的負債を増やさない
- simple design, TDD, CI, 自動テスト, refactoringなど
- refactoringによる負債の支払い
- 厳しい完成の定義の使用
- checklistの技術的な網羅性を高める
- 技術的負債の経済的意味についての適切な認識
- 技術的負債の多方面への影響
- 真のcostより低く見積もってしまいやすい
- 技術的負債の多方面への影響
- 優れた技術的practiceの活用
- 技術的負債の可視化
- 技術的負債という例えの最大のmerit: development_teamと経営者の会話での共通認識の獲得
- business levelでの技術的負債の可視化
- 組織のBalanceSheetに短期・長期の技術的負債の行を追加
- Velocityの変化の追跡
- 金銭的costを明確にできる
- 技術者levelでの技術的負債の可視化
- 既存の障害管理systemに記録
- product_backlog_itemを作成
- 返済のcostが高い場合
- 技術的負債だけを扱うBacklogを作成
- 技術的負債の現状を把握
- どの時期に負債を返済するか積極的に決められる
- 同じ場所で作業なら,掲示板を壁に掲げるのもよい
- 技術的負債の返済
- category
- 偶発的な技術的負債
- 既知の技術的負債
- 返済対象の技術的負債
- flow
- 既知の技術的負債を返済すべきか判断
- 作業中のコードに偶発的な技術的負債を見つければ,きれいにする
- 多すぎるときもできるだけは対応する
- 返済できない分は既知の技術的負債としてまとめる
- sprintごとに既知の技術的負債のいくつかを返済対象の技術的負債とみなす.
- とくに利率の高いものを選ぶ
- すべての技術的負債を返済すべきというわけではない
- 寿命が近づいているproduct
- 使い捨てのprototype
- 短期間しか使わないproduct
- boy scout ruleに従う(負債を見つけたらその場で返済)
- 「来た時より美しく」
- 偶発的な技術的負債の返済用の時間を予めある程度見込んでおく
- 5%~33%
- 技術的負債の返済はincremental
- 一括払いは×
- 返済のためだけのsprintが必要となってしまうような状況にしない
- 既知の技術的負債をピックアップして返済対象の技術的負債とみなし,次のsprintで返済
- 金利の高い技術的負債から返済
- 多くのコードが依存していて頻繁に変更が加わるモジュールなどでの負債
- 技術的負債を返済しながら顧客に価値をもたらす作業も行う
- できるだけ,返済のためだけのsprintは作らない
- 負債の返済だけを扱うproduct_backlog_itemは置かない
- merit
- 負債の削減の作業を顧客に価値を届ける作業とうまく組み合わせて進めれられ,product_ownerが適切に設定した優先順位に合わせられる
- development_team_member全員が,技術的負債の削減に関する責務を共有.ほかの人が後で対応してくれるものではないとはっきりできる.
- 技術的負債の予防や対策の力を養える.
- 金利の高い分野を見つけやすくなる. ← 対応中のコードは何かしらの重要性がある(からこそ対応している)ため.
- 対応不要な分野の技術的負債の返済に,時間を無駄遣いせずに済む.
- 既知の技術的負債の返済をproduct_backlog_itemと関連付けて進めるのは,simpleに組み合わせられて〇
- category
- まとめ
- 技術的負債の概念
- 3つの分類
- 管理
- activity
- 発生の管理
- 可視化
- 返済
Part 2 Role
Ch09 product_owner
- 概要
- product_owner: productの統括を認められた中心的な存在.Scrum_teamを構成する3つのRoleのうちの1つ.
- 2方向を見る → business analyst & tester
- 組織内のstakeholder, client, userが持つneedsや優先順位について,代理ができるくらいに深く理解
- product managerとして振る舞い,正しくsolutionが開発されていることを保証
- development_teamに何をどの順番で構築するか伝える
- & 受け入れ条件を明確にして,条件を満たすテストを実行させる
- 抽象的なtestは書く
- 組織内のstakeholder, client, userが持つneedsや優先順位について,代理ができるくらいに深く理解
- 主な責任
- 経済性の管理
- 各levelで適切な経済的意思決定が継続的になされるようにする
- release levelの経済性
- product development中に生じる経済的に重要な情報に対応
- scope,納期,予算,品質との間で継続的にtradeoff
- sprint終了時に,次のsprintを実施するかどうか判断
- product_backlogによる柔軟性
- sprint levelの経済性
- sprintごとのROI(投資収益率)を高めるため
- sprintのcost: 期間とチーム 構成を把握
- Product Backlogの経済性
- 経済状態に応じた優先順位に責任
- planningへの参加
- product_backlogのgrooming
- 受け入れ条件の定義と検証
- product_backlog_itemの受け入れ条件を定義する責任
- 受け入れ条件: 機能要求や非機能要求が満たされているとproduct_ownerが納得できる条件
- sprint planningの前に受け入れ条件が必要 ← 準備完了の定義に含まれる
- 受け入れ条件が満たされていることを確認する最終的な責任
- 検証は,sprint実施時に行う
- sprint reviewまで待たない
- sprint reviewでのデモは完成した機能のみ
- sprint reviewまで待たない
- product_backlog_itemの受け入れ条件を定義する責任
- development_teamとの協力
- 積極的な関与,献身,毎日果たすべきRole
- stakeholderとの協力
- 内外のstakeholder
- 経済性の管理
- 特性とskill
- domain skill
- 対人skill
- stakeholderとよい関係が必要
- → 交渉,合意形成
- stakeholderとdevelopment_teamとの橋渡し
- 適切な言葉で情報を伝えるcommunication skill
- 特性
- 現状に反する場合でも声を出す
- ideaに自信がある
- domain 知識がある
- 単純かつ簡潔にわかりやすく情報を伝えられる
- 信用されている
- 周囲のmotivationを高められる
- businessの課題を伝えて,仕事の目的を思い出してもらい,情熱を持続させる
- stakeholderとよい関係が必要
- 意思決定力
- 意思決定の権限が必要
- 適切なtiming, 正当な理由
- 最終的な意思決定者
- business needsと技術的な実現性とのbalance
- 説明責任力
- business resultを届けることへの説明責任
- resourceを有効に活用する責任
- product_backlogの変更,優先順位の調整,開発の中止が可能
- stakeholder, Scrum_teamともにかかわる,フルタイムの仕事
- product_owner, Scrum_master, development_teamは共通のgoalをもつ1つのunit
- 1日の様子
- 1週目,2週目
- 3週目
- 初期のrelease planning
- 内部stakeholder, development_team_member, ときに外部stakeholderも参加する,product_backlog_item記述(story writing) workshopを含む
- release planningで使用する概要levelのproduct_backlogを作成
- 見積もりworkshop
- 1~2日
- development_team_memberが価値の高いproduct_backlog_itemの規模を見積もる
- 最初のrelease planningのfacilitation
- product_backlogの優先順位づけ
- 制約(scope, schedule, budget)のbalanceをとる
- stakeholderに加え,development_team_memberの一部~全員で,product_backlog_itemの並び順に影響を与える技術的な依存性を特定
- goal: releaseの全体像の明確化.いつ何ができるのかというbusinessの最初の質問に答える
- 1~2日
- 継続的なactivity
- 内部stakeholder, development_team_member, ときに外部stakeholderも参加する,product_backlog_item記述(story writing) workshopを含む
- 初期のrelease planning
- 4周目~ Scrum_teamが最初のsprintを開始
- 開始時に,sprint planningを統括
- 実施中は,daily_Scrumに参加
- 質問に答え,review可能になった機能をテスト
- 毎日する必要があるので,できないときは代理を立てる
- 内外のstakeholderに会って,次のsprintの優先順位が正しいことの確認
- 今後のsprintで機能を選択するときに影響を与えるuserからの重要なinputを取得
- product_backlogのgrooming
- sprintの終わりに,検査と適応の2つのactivityに参加
- sprint review, sprint_retrospective
- 誰がproduct_ownerになるべきか
- 社内開発
- 開発の恩恵を受ける部門から任命された人がなる
- 商用開発
- product managerやproduct marketerなど,clientの声を代弁する人がなる
- できるだけ多くのactivityに責任をもつ
- 1人でこなしきれないほど膨大ならproduct_owner チームを作るが,Scrum_teamのproduct_ownerの役割は常に1人
- 外部委託開発
- 委託元の社員がproduct_owner
- 固定価格契約は× → 複雑
- component開発
- 技術componentに注力 → Backlogの優先順位を決定できる技術志向の人がなる
- feature_teamの技術的要求の優先順位や統括方法がわかるproduct_ownerが必要
- 社内開発
- その他のRoleとの組み合わせ
- 余裕があれば,同じ人が複数のScrum_teamのproduct_ownerの担当も〇
- それぞれの成果に関連があるときはこの方が簡単
- product_ownerとScrum_masterの兼任は×
- 余裕があれば,同じ人が複数のScrum_teamのproduct_ownerの担当も〇
- product_owner チーム
- 1人の人間が, product_ownerとして意思決定し,stakeholderやScrum_teamとの橋渡しをする必要がある
- 1人のproduct_ownerに対してproductに問題があるだけなら,productの問題に対応するべき
- 必要なときのみproduct_owner チームを作る
- product_owner proxy
- 事業部門の人にproduct_ownerを担当してもらい,IT部門の人はproduct_ownerとチームのやり取りをするproduct_owner proxyを任せるcase
- 特定の状況でproduct_ownerの代理になれる人
- 本当のproduct_ownerではなく,代わりに戦術的な意思決定をする人
- 責任はproduct_owner
- chief product_owner
- 階層的にscale
- 各チームのproduct_ownerが上の階層に投げることなく,ほとんどの意思決定をできるようにしておく
- まとめ
- product_ownerのRole
- productの統括を認められた中心的な存在
- product_ownerの重要な責任と特性
- Scrumの各activityにおけるproduct_ownerの行動
- 誰がなるべきか
- product_ownerのRole
Ch10 Scrum_master
- 概要
- Scrum_master: Scrumのvalue, principle, practiceがみんなに正しく理解され,受け入れられるようにする
- as a coach
- 自分たちに適したperformanceの高いScrum手法で開発できるように,processのleaderとして支援する
- Scrum_master: Scrumのvalue, principle, practiceがみんなに正しく理解され,受け入れられるようにする
- 主な責任
- coach
- Scrum_teamのAgile coach
- development_teamとproduct_owner間の垣根を取り払う → product_ownerが直接開発を駆動できる
- 問題には支援する.impedimentは解決する.
- 新人product_ownerが責任を理解し,Roleを果たせるようcoaching
- product_backlogのgroomingなどのactivityを継続的に支援
- Scrum_masterとproduct_ownerの関係: sport チームのcoachとownerの関係に似ている
- Scrum_master: Scrumでbusinessの成果を最大化,期待を管理,product_ownerがチームに必要なものを提供,変化に対するproduct_ownerの不満や要求を聞き出し,チームが実行可能な改善へと翻訳
- Scrum_teamのAgile coach
- servant leader
- processの権威者
- 防御壁
- チームがvalueを届けることに集中できるようにする
- impedimentの除去
- change agent
- 意識の変化を支援
- 変化の必要性やScrumの影響・利点を理解してもらう
- 長期的な成功につなげる
- 大きな組織では複数のScrum_master
- 意識の変化を支援
- coach
- 特性とskill
- 博識
- いずれも専門のtop levelでの知識は不要だが,ある程度の知識は技術でもbusinessでも必要
- 質問力
- 内容を促す質問(probing question)
- 自分で答えを見つけられることを認識してもらう
- 辛抱強い
- 自分の方がチームの集合知よりも賢いと考えない
- 定期的に内省を促す質問をして,チームにsolutionを見つけてもらう
- 協力的
- 保護力
- 経済的に正しいbusiness judgeができるようにチームを保護
- Scrum_teamが健全なbalanceを取れるように支援
- Scrumからさまよい出るmemberを支援.チームが難しさを克服できるようにする
- 透明性
- 博識
- 1日の様子
- Scrumのactivityの開催やfacilitationに毎日時間を使う
- sprint planning
- sprintの実施
- sprint review
- sprint_retrospective
- daily_Scrum
- activityの準備・統括・Scrum_teamが価値の高い成果を達成できるように支援することも含む
- 毎日チーム memberをcoaching
- 毎日一定時間をcommunicationに使う
- sprintでは,product_ownerとともにproduct_backlogのgrooming
- change agentとしての時間
- impedimentの除去
- Scrumのactivityの開催やfacilitationに毎日時間を使う
- Roleを果たす
- 誰がなるべきか
- project managerやproduct managerからScrum_masterになることもある
- technical leadは△
- managerとの兼任も△(×)
- 必ずしもfull timeではない
- ほかのRoleとの組み合わせ
- ほかのRoleと兼任するより,複数のチームのScrum_masterになるのが〇
- 誰がなるべきか
- まとめ
- Scrum_masterのRoleなど
Ch11 development_team
- 概要
- 機能横断的
- Role別のチーム
- 技術的skillを一通り備えたチームが必要
- できるだけ機能横断的
- 主な責任
- sprintの実施
- 大半の時間を割く
- 自己組織化
- 日々の検査と適応
- daily_Scrumに全員で参加
- product_backlogのgrooming
- sprintの10%を使い,product_ownerのactivityを支援
- sprint planning
- sprintの期間に応じて,半日~1日
- 反復して実施, just_in_time
- productとprocessの検査と適応
- sprint review
- sprint_retrospective
- sprintの実施
- 特性とskill
- 自己組織化
- bottom up型の創発的な特性
- 外部からの圧力なし
- 複雑適応system
- 多くのentityが様々な方法でやりとり
- やりとりは,単純で局所的なruleで統括され,定期的なfeedback
- 大いに堅牢で斬新
- managerは自己組織化チームのための環境を設定・再構築する
- bottom up型の創発的な特性
- 機能横断的な多様性と十分な能力
- product_backlog_itemを選択し,Scrum_teamの完成の定義を満たして動作する,高品質の機能を作り出す
- 成果の手渡しの数を最小化
- 複数の視点が良い成果になる
- 問題解決のための認知toolの持ちより
- 解釈,戦略,mental model,手法やsolutionの嗜好が異なる
- → すばやいsolution,高品質の提供物,大いなるinnovation,という観点から良い成果につながり,経済的valueへと変わる
- 問題解決のための認知toolの持ちより
- チームの多様性のために,senior, juniorを組み合わせる
- みんなで協力して学習し合う健全な環境
- T型skill
- 幅広さと深さ
- 継続的に学習してskill setを増やせるような環境が必要
- skill set: domain knowledge, technical knowledge, thinking skillなど
- 学習と実験の時間がうまく作れるように,managerがチーム memberを支援
- 専門家は,複数のチームに余裕がある状態で時間を分割するか,ほかのmemberの学習支援に時間を使う
- チーム memberがcoreとなる専門分野をもち,全体として柔軟性をもたらすような,skillに重なりのあるチームを作ることがgoal
- 銃士の姿勢
- one for all, all for one
- 仕事を成し遂げる責任をチーム member全員が負う
- できないことがあるときは,skillある人に学習支援をしてもらい,チームの総合力を高める
- 1人に負担がかからないように作業を構成
- 議論に多様性
- 広帯域のcommunication
- 有益な情報をすばやく交換,overhead少なく効率的にやり取り
- 情報共有の頻度と品質を向上
- → 検査と適応の機会が増え,意思決定を迅速にできる
- 情報の経済価値は時間に依存 → 共有速度が高まれば,チームは情報の価値を最大化できる
- 方法
- face to face
- TV会議system
- 機能横断的チーム
- 形式的な提出物によるやり取りの量も回数が少なくなる
- valueのない儀式を減らす
- 小さなチーム
- channel数の増加率
- 適切な規模
- 小さなチームの理由
- 社会的手抜きが少ない
- 建設的なやり取りが頻繁に起こる
- 調整に必要な時間が少ない
- 誰も陰に埋もれない
- 小さなチームの方がmemberを満足させられる
- 有害な過度の専門化が発生しにくい
- 人数が多すぎたら,9人程度にチームを分割
- Scrum projectのscale: 複数のScrum_teamを作る
- Scrum of Scrum
- 上位levelのdaily_Scrum相当のものを開催する
- Scrum of Scrum
- 小さなチームの理由
- 集中とcommitment
- 集中: チーム memberが積極的にかかわって,チーム goalに専念
- commitment: よいときも悪いときも,チーム goalの達成に尽力
- 3つ以上のprojectの仕事を同時に行うのは経済的でない
- 調整・想起・情報の追跡に時間がかかり,valueを付加する仕事の時間が減るため
- 基本は1つがよい
- 持続可能なpaceで仕事をする
- Scrumの優れた技術的なpracticeで平準化
- 長寿
- groupではなくチーム
- チーム: 機能横断的で多様性のある協調性の高い人たちで構成され,visionを共有し,それを実現するために協力する集団
- group: 共通のlabelがついた人の集団
- 長寿は経済的に好ましい
- forming, storming(争乱期), norming(規範期), performimg
- 上手く機能したチームはbusiness資産になる
- 歴史的に重要な情報を蓄積
- Velocityや見積もりの履歴
- 歴史的に重要な情報を蓄積
- Agileの通貨はチーム
- 個人と対話に価値
- チームの信頼,一貫性,作業効率を保つ
- 人ではなくチームを移動させる方が,経済的に優れている
- チームは資産
- 同時に行うWIPの数やproductの種類を決めるときのcapacityの単位
- 自己組織化
- まとめ
- development_teamのRole
- product_backlog_itemを出荷判断可能なproduct incrementに変える責任
- sprintにおける責任
- 10個の特性
- 自己組織化,機能横断的で多様性がある,仕事を成し遂げるために十分なskillがある
- T型skill
- 銃士の姿勢
- 広帯域のcommunication
- 小さなチーム
- 集中とcommitment
- 長寿
- development_teamのRole
Ch12 Scrum_teamの構成
- チームの構成や,チーム間のかかわり方が,Scrumをうまく進めるために重要になる
- 概要
- businessのvalueを提供するために,複数のScrum_teamsの協力が不可欠
- feature_team or component_team
- feature_team: 機能横断型かつcomponent横断型のチーム
- component_team: 何か1つのcomponent, subsystem(featureの一部)のdevelopmentにのみ注力
- a.c.a. asset チーム, subsystem チーム
- 同じ専門skillを持った人たちの集まりで構成される実行部隊など
- 中央管理型の共有resource的
- 各component_teamが作ったcomponentを統合し,完全なfeatureにまとめてdelivery
- 一番良いのは,機能横断型のfeature_team
- 移行措置としては,1つのfeature_teamがfeatureをproduct_backlogから取り出し,作業完了までの責任を持ち,完成までの手配をする
- 個々のcomponent_teamも残り,各チームからfeature_teamのmemberをassignし,仲介者・収穫者の責務を持たせる
- 頻繁にやり取りするcomponentを1つのfeature_teamにまとめる
- component_teamを導入するのは,resourceを1つのチームにまとめて集中管理した方が合理的なときだけ
- 複数チーム間での調整
- Scrum of Scrum
- チームをまたがる作業を複数チーム間で調整
- question
- 前回以降で各自のチームの作業で,ほかチームに影響しそうなもの
- 次回の各自のチームの作業で,ほかチームに影響しそうなもの
- 各自のチームの今抱えている問題で,ほかチームに助けてほしいもの
- 階層化も〇
- release train
- vision, planning, 複数チーム間の依存関係などを調整するための手法の1つ
- チーム間でのsync
- product levelでの高速で柔軟な流れに注力
- featureが「駅を出発」するscheduleが決まっている
- rules
- 頻繁に定期的にplanning, solutionのrelease日またはPSI(出荷判断可能なincrement)の公開日を決めておく(固定期日,固定品質,可変scope)
- すべてのチームでiterationの長さをそろえる
- 全体での中立的かつ客観的なmilestoneを決める
- 継続的なsystem integrationを,top level(system level)だけでなくfeature level, component levelで行う
- releaseできるincrementを定期的に(一般的に60日/120日ごと)用意し,client向けのpreviewや内部review, system level のQAに使えるようにする
- system levelのhardening iterationを実行し,技術的負債の返済やrelease levelの検証・テストを行う
- 各チームを同じような構造にするために,基盤になる各種component(interface, SDK, 共通でinstallするsoftware, license, UX framework, data, Web serviceなど)をきちんとそろえる
- richな概念
- portfolio level, release levelなど,何段階かの詳細度で考えられる
- enterprise backlog modelに基づく
- portfolio_backlog, program backlog, チーム backlogの3段階のbacklog を使う
- チーム levelのrelease train
- 各チームが数個のfeature areaに分かれてまとまる
- 現実的な頻度で,feature areaをまたがったsystem全体の統合やテストを行う
- 列車が発射する直前をhardeningのための時間と決めていることもある
- release trainの流れ
- release planningのmeeting
- sprintの実行
- release train levelでの検査と適応
- Scrum of Scrum
- まとめ
- Scrum_teamを構成するさまざまな方法
- feature_team
- component_team
- feature_team, component_teamの組み合わせ
- 徐々にfeature_team主体に移行し,codeの共同所有を実現
- Scrum of Scrum
- release train
- 数多くのScrum_teamのactivityを調整
- Scrum_teamを構成するさまざまな方法
Ch13 manager
- 組織内にはScrumと関係ないRoleがあり,それらも組織に必要
- 各managerのRoleを見る
- 概要
- Scrumでもmanagerはimportant
- functional_managerの責務: teamの編成と育成, 環境を合わせてなじませる,value創造の流れを作る
- teamを編成する
- 境界を定める
- managerが,product, project(砂場)を定める
- teamの構成を決める(誰をどのprojectとするかを決める)
- teamはprojectの中で自己管理する
- 自己組織化したteamにどこまで許すか決定
- いくつのprojectを作るか決定し,各projectの境界も定める
- teamの構成を決める(誰をどのprojectとするかを決める)
- managerが,product, project(砂場)を定める
- 明確なgoalを定める
- 目的と方向性をteamにもたらす
- product_ownerがgoalをより具体化する
- teamの体制を決める
- business上のneedsや制約とのbalanceをうまくとったteamを作る
- 機能横断的なScrum teamを作る
- 各分野やcommunity(functional area)にfunctional_managerがいて,各functional areaからmemberを選んでteamを組む
- teamの構成を変更する
- memberの変更
- teamに権限を持たせる
- 自己組織化したteamでお互いをよりうまく管理することの達成のため,team_memberに責務を委譲
- 権限のlevel
- 通知,説得,相談,合意,助言,確認,委譲
- 信頼し,委譲した分は任せる
- team_member間に銃士の姿勢を育てる
- 境界を定める
- teamを育てる
- managerがteam を管理するわけではない
- teamを活気づける
- 楽しくて創造的でvalueをもたらせるような環境を作るのが責務
- → motivation, energyを引き出す
- task levelの作業のassignはやる気をそぐ
- 楽しくて創造的でvalueをもたらせるような環境を作るのが責務
- 能力を伸ばす
- memberが常に何かを学び,skill setを磨き続けられるような環境づくりをする
- 研修の受講,カンファレンスへの参加
- 個別のfeedbackの頻度を調整し,各memberの直属の上司が属するteam内での学習loopにマッチさせる
- sprint単位など
- 個人のperformanceがteamのperformanceを以下に支えるかという観点で行う
- memberが常に何かを学び,skill setを磨き続けられるような環境づくりをする
- functional areaのleadershipを発揮する
- functional area での一貫性を保ち,memberを指導する
- areaに関する標準を設定
- teamの安定を保つ
- teamの構成を可能な限り変えずに保ち続ける
- 環境に合わせてなじませる
- 価値創造の流れを作る
- manager's Role @Scrum
- 戦略的な方向性を定める
- 戦略目標を達成するための組織的resourceを,採算を考慮して揃える
- 全体的な視点で考える
- 財政の管理
- 任された財源をどのように使うか責任
- portfolio management, corporate governance
- 評価や報告の管理
- 評価や報告をScrumの価値や原則に見合ったものにする
- 作業の手待ちを評価
- 動作する検証済みの成果物で進捗を見積もる
- すばやいfeedback flow を作る
- 革新会計の要点となる評価基準
- 革新会計: 不確定要素が多い状況でproduct, serviceを作るような組織でimportant
- 行動につながる評価基準を使って,自分たちがどの程度すばやく学べるか評価
- → business valueのある結果を得るための進捗を見る重要な指標
- 3段階
- 実用最小限の製品(MVP)を作って,組織やproductの現状に基づいた行動につながる評価基準のbaselineを設定
- productのincrementalな改良をもとに行動につながる評価を向上させ,目標値に近づける
- 行動につながる評価の値から目標に向かっての進み具合が実証できればそのまま続行.実証できなければ,新たな戦略にpivotして,この手順を繰り返す
- 評価や報告をScrumの価値や原則に見合ったものにする
- manager's Role @Scrum
- project manager
- Scrum_teamにおける project managementの責務
- Scrumの3つのRoleに分散している
- 調整を任せるのではなく,さまざまな分野にまたがる依存関係を把握して各所とやり取りし,ほかチームとの調整を効率的にできるようにすること
- project managerのRoleを残す
- 大規模で複雑な開発の場合は,手配や調整にからむタスクが非常に多いので,そのままRoleを残すのも〇
- ただし,かなり大規模になったときのみ
- できる限り,チーム間でのcommunication channelを見直すべき
- Scrum of Scrumなどを使う
- できる限り,チーム間でのcommunication channelを見直すべき
- 調整の責務を第3者に渡すべきではない
- project managerはScrum_teamのassistant(servant leaderのようなもの)
- system全体の視点で考えて,各clusterや個々のチームのために動く
- チーム間の調整に何が必要か,誰もがきちんと理解している状態にする.実際の調整はチーム自身で行う
- project managerはScrum_teamのassistant(servant leaderのようなもの)
- 大規模なproduct/service developmentでほんの一部だけがScrumを採用している場合にも使える
- Scrum_teamにおける project managementの責務
- まとめ
- Scrumの組織におけるfunctional_managerのRole
- 4 category
- team編成
- team育成
- 環境を合わせてなじませる
- 価値を創造する流れを作る
- 伝統的な組織とScrumを使った組織でのfunctional_managerの責務の比較
- project managerの責務,Role
Part 3 planning
Ch14 Scrumのplanning principle
- 伝統的な開発よりplanningに時間を割く
- Scrum principleの一部をさらに掘り下げ,planningにどのように適用するのか見る.その土台となる議論を見る.
- 事前にきちんと計画を作れると思うな
- 検査と適応の経験に従う
- 最初のうちにある程度はplanningの成果物を出す
- 事前のplanningとjust_in_timeでのplanningのbalanceのため
- 事前のplanningのやり過ぎに注意
- planningの選択肢は,最終責任時点まで変更可能にする
- planを守ることよりも,planの調整や再計画を重視する
- knowledgeがない状態で作られたため,事前のplanは抜けがある
- plan list management
- WIP management
- 無駄なplanningの作成物を作らない
- 早めにrelease, 頻繁にrelease
- すばやいfeedback, ROIの向上
- 市場に出せるlevelは保つ
- 早めに学び,必要ならpivot
- pivot: 方向転換しながらもそれまでに学習したことはそのまま維持し続ける
- 製品,戦略,成長のengineに関する根本的な仮説を新たに策定し,それを検証できるコースに方向転換すること
- 学習loopを高速かつ経済的に回すことが目標であり,正しくない方向に向かっていればpivot
- pivot: 方向転換しながらもそれまでに学習したことはそのまま維持し続ける
- まとめ
- Scrumにおけるplanning principleの概要を見た
- 原則に従い,経済面で妥当なplanningを達成する
- 事前に用意する適度な量の計画と,実際の結果をふまえたより詳細なjust_in_timeの計画との間で,うまくbalanceをとる
Ch15 さまざまなlevelでのplanning
- Scrumにおけるplanningの概要を全体的な視点で見る
- 概要
- 5種類のplanning
- 範囲,作成者,対象,成果物
- 5種類のplanning
- portfolio_planning
- 複数のproductの中から,どれをどの順番でどの程度の期間で進めていくか決める作業
- 概念的にはproduct_planningより上だが,inputの1つはproduct_planningで得られる新たに考え出したproduct idea
- product_planning(envisioning)
- 作ろうとしているproductの本質を見出して,productを作るための大まかな計画を作る
- まずvisionを作り,その後,概要levelのproduct_backlogを作る
- product_loadmapを作ることも多い
- product vision
- user, clientといったstakeholderたちが得る価値を明確に説明するもの
- 概要levelのproduct_backlog
- まったく新しいproductを作る場合なら,少なくとも最低限の要件は事前に決めて,product_backlogに含める
- 少なくとも最優先のitemがどれになるかも判断する
- 想定されるuser_storyを設定
- product_loadmap(release loadmap)
- productを今後どのように構築してどのようにdeliveryするのかのincrementalな性質を示し,各releaseに含まれる重要なfeatureについても示す
- continuous deploymentがあっても有用になりうる
- 意思決定者に十分な安心感を与え,product developmentを認めてもらうため
- release_planning
- incremental deliveryに向けて,scope, 期日, 予算とのtradeoffを考慮する
- product_planningが終わったら,releaseの最初のsprintを開始する前に最初のrelease_planningを実施する
- そのreleaseでどれだけのものを作るか,それをいつreleaseするのかの間でbalanceを取ったもの
- ある程度のitemをproduct_backlogに用意して見積もる必要がある
- product_backlogに線を引く
- product_loadmapとproduct_backlogをより強く結びつける
- 期間を,そのreleaseを達成するために必要なsprint数で表す
- 直近の数回のsprint程度の推測は,作業の調整や事前に必要な支援のために有用.それより先の推測は無駄
- sprint_planning
- product_backlog_itemの中から,Scrum_teamが次のsprintでどのitemに取り組むかを決める
- 各sprintの最初に行う
- sprint backlogを作る
- product_backlog_itemの完成に必要な作業を,task levelで記述
- より踏み込んだlevelで,just_in_time planning
- daily_planning
- 最も詳細なlevelのplanning
- team_memberが集まり,高度に可視化されたstyleで,当日の大まかな予定を話し合う
- resourceのproblemに気づけるようにする場でもある
- 良い流れでsprintを進める
- まとめ
- さまざまな詳細度でのplanningが,Scrumの開発中にどのように発生するのかを見た
- Scrumの階層的なplanningの図
Ch16 portfolio_planning
- 複数のproductを作るにあたって,product_portfolioをどのように管理するか,継続的に理にかなった選択をする必要がある
- portfolioの管理processを,Agile practiceとうまく協調させる必要もある
- 概要
- portfolio_planning: portfolio_backlogのitemから,どれをどの順番にどの程度の期間で進めていくか決める作業
- item: product, そのincrement(1回分のrelease), or project
- 素早く柔軟な流れを壊さないように実施する
- timing
- product_planningのdataを使って,どのproductにどの順に投資するのかを判断し,portfolio_backlogにまとめる
- 新たなproductに対してだけでなく,既存(仕掛け中)のproductについても見直す
- 参加者
- 内部のstakeholder
- business的な大局観が必要
- portfolio_backlog itemの優先順位の決定や,仕掛け中のproductに対する判断のため
- business的な大局観が必要
- 各productのproduct_owner
- 必要なresourceを主張
- option: senior architect, technical leader
- 技術的な制約を考慮
- 内部のstakeholder
- process
- output: portfolio_backlog, active product群
- outputのために,4種類のactivityがある(→ 以下に続く)
- portfolio_planning: portfolio_backlogのitemから,どれをどの順番にどの程度の期間で進めていくか決める作業
- schedulingの戦略
- 組織内のproductに割ける限りあるresourceを,経済的に理にかなった方式で配分するために,product群を優先順位づけ
- lifecycle 収益で最適化
- 最適化が効いているかどうかをどの変動要因で判断するか決定する必要
- → lifecycle 収益を使ってあらゆる判断やtradeoffを見る
- 標準化されていて使いやすい経済的枠組み
- lifecycle 収益の総量が最大となるように,portfolio_backlog内のproductを並べ替える
- goal: portfolio_backlog itemを並べ替えて,portfolio全体のlifecycle 収益を最大化すること
- 最も影響する重要な2つの変動要因
- delay_cost
- 存続期間(作業量やproductの規模の代替)
- → いずれも各productで異なる場合は,重み付きで一番短いjobを優先する(WSJF)
- delay_costを存続期間(or 実装の作業量)で割ったもの
- delay_costを算出する
- 経済的な意思決定をするにあたって欠かせない情報
- ほとんどの変動要因が時間の影響を受けるという事実
- → 時間軸を必ず考慮してportfolio_backlog内のitemを並べ替える
- 算出方法: spread sheetを2つ作り,遅延がない場合と遅延がある場合の2種類の収益をそれぞれ計算すればよい
- model
- user的な価値
- 時間的な価値
- riskの削減/機会の確保
- 以上の3つの属性について,delay_costを1から10までの数字で設定し,合計したものがproductのdelay_costになる
- または,よくありがちな特徴でdelay_costを分類する
- 線形, 巨大なcost, すぐにでもやるべき, 固定期日, 対数, 実体がない
- 見積もるのは正確性であって精度ではない
- portfolio_backlogのitemを適切にschedulingするために,各productの作業量/costを理解する必要
- 最初の見積もりが必要になる時点では手持ちのdataはたかが知れているので,精度より正確性に注目する
- Tシャツのサイズなどでcostの範囲を関連付け
- → すばやく見積もれる,十分な正確さを得られる,portfolio levelで次の動きを決めるための情報を用意できる
- → 無駄な時間を削減,見積もりの数字を過大評価したり誤った安心感を与えることもなくなる
- inflowの戦略
- product developmentを始めるかどうかの判断において,組織の経済的filterを以下に適用するのかを見る
- 経済的filterの適用
- envisioningのoutputは,product visionに加えて,信頼の閾値を明確にするための情報となる
- → このdataをもとに,product developmentを進めるか中止するか判断
- ↑ このactivityが新しいproductへの「経済的filterの適用」
- 組織の資金需要を満たすかどうかを確かめるactivity
- 組織ごとに経済的filterを定義
- よいfilterはコストをはるかに上回る価値を生み出せる機会をすばやく承認できる
- それ以外の機会は,やむを得ない事情がある場合を除いて却下すべき
- costや価値に合意が取れず決断ができないなら,十分な資金援助が得られないことは明白
- ほとんどの組織ではよい機会がありあまっている
- 追加と取り出しのbalanceを取る
- 年に1度で達成内容の詳細をproduct levelまで全部決めてしまう必要はない
- productをportfolio_backlogに追加する頻度を高くする(月に1度か少なくとも四半期に1度)
- → 新しいproductのreviewやportfolioへの追加にかかるcostや労力を大きく軽減できる
- → portfolio_planningの全体的な安定性や予測可能性を高められる
- productを小さめにすることにも力を入れる
- 頻繁に取り出せるようにしておく
- → inflow, outflowのbalance〇
- portfolio_backlogの肥大化 → portfolio_backlogに追加するproductの流れを絞る
- 経済的filterを調整して,productを承認する条件を厳しくする
- → より価値の見込めるproductだけを承認
- 創発的なchanceへの素早い対応
- 小さめなreleaseを頻繫に行うための計画
- 明白なmeritのほかに,コンボイ効果を回避できる
- 大きなproductが共有resourceを占有することを回避
- 徐々にreleaseしていった方が経済的なmeritが大きい
- 一括releaseをできるだけ避けるようにplanning
- 明白なmeritのほかに,コンボイ効果を回避できる
- outflowの戦略
- 作業者の手待ちではなく作業の手待ちに注目
- productをportfolio_backlogからいつ取り出すのか判断するカギとなる戦略
- WIPを制限する
- 自分たちが完了させられるだけの量に達したら,それ以上のproductをportfolio_backlogから取り出さない
- 制限の程度
- Scrum_teamがいくつあるか,それぞれのteamがどのtypeのproductの作業ができるかで判断
- team全員の準備が整うのを待つ
- 作業者の手待ちではなく作業の手待ちに注目
- 仕掛け品の戦略
- 現在作業中のproductについて,開発を続行するかpivotするか停止するかを判断する助けとなる
- 定期的に判断
- 臨時にも判断する
- 限界費用を使う
- これまでにいくら使ったのかは一切無視して判断する
- 判断のflow
- 資金の追加投入に値するか → 維持
- 必要最小限の機能が実装されているか → delivery
- 別の未知はあるか → pivot
- → 打ち切り
- まとめ
- portfolio_planningに関する11の重要な戦略
- すべて互いに補強し合う
- 各categoryで1つずつのみの場合
- delay_cost
- 小さめのreleaseを頻繁に
- WIPを制限
- 限界費用
- portfolio_planningに関する11の重要な戦略
Ch17 envisioning(product_planning)
- product_backlogを最初に作るときに,productについてのvisionのための事前の作成物を用意するactivity
- 概要
- goal: ideaを詳しく説明し,できあがるであろうproductの本質を示し,実際に作るための大まかなplanを用意
- ideaをportfolio_planningに組み込めるだけの十分な自信が必要
- productの資金調達にまず不可欠なのは,visionを持つ
- client, feature, 概要levelのsolutionについて理解するために必要な程度に情報を把握する
- productにかかるであろうcostの見当をつける
- 短時間で実施する
- timing
- 何度も繰り返す
- きっかけは誰かからのidea
- 組織の戦略的filterを通し,組織の戦略方針に一致しているか調べ,さらなる調査と資金投入の価値があるか判断
- 通過したら,最初のenvisioning
- これから作ることになるproductに関する十分な理解を得て,最初の最低限のreleaseがどんなものであるべきか定義
- → 最大限の価値をすばやく提供,costを抑えられる,すぐに役立つfeedbackを得られる
- feedbackで間違いを判断したら,pivotしenvisioningをやり直して計画を見直す
- 参加者
- 最初のenvisioningに必ず参加が必要なのはproduct_ownerのみ
- 内部のstakeholderも巻き込むことが多い
- specialist(market分析,投資対効果の拡大,UX design,system architectureなど様々な分野)もかかわることが多い
- Scrum_teamが割り当てられてからは,Scrum_master, development_teamも含めてteam全体で参加すべき
- process
- input
- 最初: 戦略的filterを通過したidea
- 2回目から: pivotしたidea
- userやclientからのfeedback, 財源の状況の変化,競合の予期せぬ動き,その他重要な変更から,ideaを更新,修正する
- planningの範囲
- いつまでにenvisioningを完了させるか
- 割けるresourceの量や種類
- 信頼の閾値
- envisioningの完成の定義
- より詳細な開発に向けた出資をするべきか否かを意思決定者が決断する際に,十分な確信をもって結果を出せるようにするために必要な情報をまとめたもの
- input
- goal: ideaを詳しく説明し,できあがるであろうproductの本質を示し,実際に作るための大まかなplanを用意
- SR4Uの例
- 最初の開発に進むべきか否かの判断材料
- 最初のproduct vision, product_backlog, product_loadmap
- userについての最初の前提の検証
- userについてのそのほかの重要な前提・仮説や,最初のreleaseでテストしようとしているfeature群に関する説明
- その他の全体を確かめたり,最初のreleaseが期待通りか判断したりするために使う,計測可能な基準
- 対応すべき疑問点のlist
- 最初の開発に進むべきか否かの判断材料
- visionづくり
- 概要levelのproduct_backlogの作成
- product_loadmapの定義
- visionと概要levelのproduct_backlogが完成したら,最初のproduct_loadmapの作成に進める
- product_loadmap: 一連のreleaseでproduct visionのどの部分を達成していくか示す
- incremental deploymentについて,当初の概要を示す
- 1回のreleaseの規模を小さくして,releaseをより頻繁に行う
- 小さめのrelease 1度で済んでしまうような計画なら不要
- 頻繁なrelease: 個々のreleaseをrelease可能な最小限のfeature(MRF)単位にまとめる
- MRFの補完として,定期的なrelease(四半期ごとなど)を採用するのも〇
- product_loadmapがsimple
- わかりやすい
- 内外を含めた関係者全員へreleaseの周知がしやすい
- 組織のreleaseを事前の予測通りに活用できる
- ほかのgroupもその組織の計画に合わせて動きやすくなる
- MRFの開発がrelease間隔より短く済む場合は,さらに追加で価値の高いfeatureを作る
- release goalの明示
- releaseの目的と期待される成果を示す
- solutionのreleaseの内容を決める助けとなるあらゆる要素を考慮
- target client
- 各segmentのclientに対していつどのように対応するか示す
- 概要levelのarchitectureや技術面の課題
- 市場の重要eventなど
- target client
- 大雑把な予想に過ぎない
- 詳細な情報が分かり次第loadmapを更新する
- どの程度の将来までカバーするか
- 大規模なvisionでも,理にかなった必要な分だけのloadmapを作る
- 誰かに出資をお願いする期間はカバー必要
- その他のactivity
- 信頼の閾値を満たすために関係があると参加者が認めた作業なら,どんな作業を含めてもよい
- e.g. 市場調査,競合productとの比較分析,大雑把なbusiness modelを作成して経済的filterと照合
- envisioningを複数のsprintに分けて行うときには,担当teamがenvisioning関連の作業のbacklogを管理する
- 優先順位を決めて,短期間(1週間程度)のsprintを回す
- 知識を得るためのsprintもある
- 信頼の閾値を満たすために関係があると参加者が認めた作業なら,どんな作業を含めてもよい
- 経済的に理にかなったenvisioning
- envisioning系の作業は,project charteringやproject inception, or project initiationと呼ぶこともある
- このprocessを,総合的なstage gate型管理modelの一部として組み込んでいる組織もある
- 重厚で儀式的なprocessになってはいけない
- このprocessを,総合的なstage gate型管理modelの一部として組み込んでいる組織もある
- できるだけsimple
- productの性質や規模,risk levelに基づいて,必要最小限の事前planning,そして知識獲得のための作業を行うのみ
- 詳細な作成物はjust_in_time
- goal: 現時点での最良な判断をするのに十分なだけの情報を,資金や期間を考慮して得ること
- 指針
- 現実的な信頼の閾値を目指す
- 注目するのは短期間だけ
- 1度にやり過ぎず,主に,直近のreleaseで必須となるfeatureに注目する
- すばやく動く
- envisioningは手早く効率的に済ませる
- → できあがるのが早まり,検証できる
- → delay_costを小さくできる
- 緊迫感をもってproductについての判断を下せるようになる
- 適切なresourceを割り当てて,envisioningを適切なtimingで終えられるようにする助けとなる
- 方法
- envisioningの完了予定日をenvisioning teamに知らせておく
- envisioningは手早く効率的に済ませる
- 検証による学びに投資する
- envisioningのactivityを経済面で評価するときの基準
- 対象とするclient, feature, and solutionについての検証による学びを得るためにどの程度役立ったのか
- envisioningのactivityを経済面で評価するときの基準
- incrementalに出資する
- 出資にかんする判断は,より正確な情報がそろうのに合わせて常に行う
- envisioningに要する時間も短縮できる
- 早めに学んでpivotする(fail_first)
- 責任をもって効率的にresourceを管理し,手早く格安にenvisioningを行う
- 資金負担を大きく減らすことができる
- envisioning系の作業は,project charteringやproject inception, or project initiationと呼ぶこともある
- まとめ
Ch18 release_planning(長期計画)
- release_planningでは,scope, schedule, budgetといった制約の下で,clientにもたらす価値と全体的な品質とのbalanceを取らなければならない
- release_planningをどのようにScrum frameworkに組み込むのか,固定期日と固定scopeのそれぞれのreleaseで,release_planningをどのように行うか見る
- 概要
- featureをclientに届けるためのreleaseの周期を適切に定める必要
- continuous deployment(delivery): sprintの終わりを待たず1つのfeatureが完成するたびにrelease
- どの周期でも,ある程度の長期間にわたる概要levelの計画(release_planning)を作っておくと便利
- a.c.a. 長期的planning, milestone駆動planning
- 狙い: 重要な変動要因(期日,scope,予算など)のbalanceを取ったうえで,将来の状態を定める
- timing
- sprintごと
- envisioningやproduct_planningのあと
- product_planningの目的: productがどうあるべきか思い描く
- ←→ release_planningの目的: そのproductのgoalに向けて,次の一歩を定める
- 初回releaseの前に最初のrelease_planningを行い,release planを作成する
- 通常は1日から2日程度だが,productの規模やreleaseのrisk,参加者のproductへのなじみによって変わる
- 完全でなくてよい
- 検証による学びをもとに更新する
- sprint review中でも次のsprintの準備のときでもよい
- 参加者
- stakeholderとScrum_team全体が,互いに協力し合って行う
- businessと技術のtradeoffを考慮して,もたらす価値と品質の間で最適なbalanceが必要なため
- process
- input
- product_planningのoutput: product visionや概要levelのproduct_backlog, product_loadmap
- releaseにかかわるであろうteamのvelocity
- 繰り返すactivity
- releaseの制約であるscope, 期日, budgetを確認し,これまでの流れやわかってきたことから,制約を変更する必要があるかどうか見直す
- product_backlogのgrooming
- 概要levelのproduct_backlogをもとに,より詳細なproduct_backlog_itemを作ったり,見積もったり,優先順位をつけたりする作業
- timing
- product_planningを終えてから,最初のrelease_planningに入る前
- 最初のrelease_planningの一環として
- 各sprint中で,必要に応じて
- envisioningで定義された明確に定義されたrelease可能な最小限のfeature(MRF)を,常に見直す
- client視点で,実現最小限の製品になっていることを確かめる
- sprint mapを作ることも多い
- 各product_backlog_itemがどのsprintで作られるか示す
- 直近の範囲の可視化
- → team内の依存関係やresource配分を管理しやすくなる.複数のteamでの共同作業の調整にも役立つ
- release_planningのoutputをひとまとめにしてrelease planと呼ぶ
- どの時点までに判断できる妥当なlevelの正確さでの計画を表す
- 開発作業がどれくらい残っているか,いつごろ終わるか,どのfeatureができあがるか,costがどの程度かかわるかなどを含む
- そのreleaseで求められるMRFを明確化
- releaseのための各sprintで対象となるproduct_backlog_itemをどのように扱うのか示すことも多い
- どの時点までに判断できる妥当なlevelの正確さでの計画を表す
- input
- release制約
- release_planningの目的: 次のreleaseで最も重要なものが何なのか,そしてそれをどの程度の品質で用意するのかを定める
- scope, 期日, 予算といった制約は重要な変動要因であり,どうやって目標を達成するかに大きく影響する
- product_planningにもとづいて,いくつかの制約が生じる
- 各制約の固定/可変の組み合わせ
- すべて固定
- 現実的でない
- scopeと期日を固定
- 多くの問題があり,厳しすぎる
- 固定したものはいずれもかなり予測しづらい
- いずれかは犠牲になる
- resourceを投入すればするほど多くの作業をこなせ,作業の所要時間も短縮できるという想定だが,現実的でない
- 実現できないか,持続可能なpaceに反するか
- Scrumのiterative and incrementalなapproachを使うことで,何か問題が発生しても早めに気づけるようになる
- scopeを固定
- 実際問題として,scopeのほうが期日より重要な場合
- 全体的なscopeがあまりにも大きすぎる場合
- より小さ目なreleaseに分割して,それぞれを期日で固定した方がよい
- 期日を固定
- Scrumの原則に最もあてはまる
- timeboxingを重視するScrumにぴったり
- 優先度の高いfeatureから作るという原則に従うことで痛みを軽減できる
- 優先度の高いfeatureが本当に(合意済みの完成の定義にもとづいて)完成している場合だけ
- MRFを小さめに定義できるとき
- Scrumの原則に最もあてはまる
- 品質を可変に
- 制約が厳しすぎる場合
- 期待に添わないsolution, 技術的負債の発生
- 制約の見直し
- 現時点でわかっていることから,制約のbalanceを見直すべきかどうか見極めることが,release_planningにおいて重要
- 継続的に判断する
- すべて固定
- product_backlogのgrooming
- 概要levelのproduct_backlog_itemを,user_story 記述workshopなどで,より詳細なlevelに踏み込んだproduct_backlog_itemにする
- → teamで見積もって,どの程度costがかかるかを判断できるようになる
- → releaseのgoalと制約に基づいて,各storyの優先順位を判断
- MRFがrelease_planningの参加者間で常に合意の取れた状態である必要
- release可能な最小限のfeature(MRF,Minimum Releasable Feature)の見直し
- sprint_mapping
- 直近のsprintについて,product_backlog_itemを事前にmappingしてみるのが有用
- 複数のteamで作業を進めている環境で,協調が上手くいくようになる
- 移動する先読み範囲: 直近のsprintと少なくともその後数sprintで必要なproduct_backlog_itemを検討する
- team間の依存関係が扱いやすくなる
- 複数のteamで作業を進めている環境で,協調が上手くいくようになる
- ある程度詳細なlevelまで落とし込み,見積もりが終わって,優先順位のついたproduct_backlogが必要
- teamのvelocityを使って,product_backlog_itemをsprint単位に適切に割り当てられる
- 一般的なschedule表にsprint_mapを並べることもある
- 単一のScrum_teamのdevelopmentでは,mappingは最初のrelease_planningで行う
- そのsprintで各teamがどのfeatureを担当するかを最終的に決断するのは,最終責任時点,sprint_planningのとき
- product_backlog_itemとsprintのmappingをほとんど/全くしないこともある
- 直近のsprintについて,product_backlog_itemを事前にmappingしてみるのが有用
- 固定期日のrelease_planning
- release_planningの手順
- このreleaseでsprintを何回実行するか決める
- product_backlogを適切な詳細度までgroomingする(backlog itemを細かくしたり,sizeを見積もったり,優先順位を決めたりする)
- MRFを構成するfeatureは,与えられた時間の60~70%で完成できるようにしたい
- teamのvelocityの範囲を計測する
- 最も遅い場合のvelocityとsprintの数を掛ける.product_backlogの先頭からその結果分だけのitemを数え,そこに線を引く
- 最も早い場合のvelocityとsprintの数を掛ける.product_backlogの先頭からその結果分だけのitemを数え,そこに2番目の線を引く
- release日までにどのfeatureができあがるかという問いの答えがある程度分かる
- release_planどおりに進んでいるか判断するためには,必須featureを表す線をproduct_backlogの上に重ねてみるだけでよい
- sprintが終わるごとにrelease_planを見直す
- release_planningの手順
- 固定scopeのrelease_planning
- 実際には期日よりもscopeの方がずっと重要という場合
- 本当の意味でのMRFを厳選し,徐々に増やしていくようにする
- → 固定scopeのreleaseもいくつかの固定期日のreleaseに分割できる
- どうしても期日が最大の制約の場合の流れ
- 少なくとも今回releaseしようとしているproduct_backlog_itemを含むよう,product_backlogをgrooming(新たなitemを作ったり,sizeを見積もったり,優先順位を決めたりする)
- このreleaseでdeliveryするproduct_backlog_itemの合計サイズを決定する
- team velocityの範囲を計測する
- product_backlog_itemの合計サイズを最も早い場合のvelocityで割り,端数を切り上げる
- product_backlog_itemの合計サイズを最も遅い場合のvelocityで割り,端数を切り上げる
- costの算出
- 手順
- team_memberを決める
- sprintの長さを決める
- team 構成とsprintの長さに基づいて,sprintあたりの人件費を算出する
- 固定期日のreleaseの場合は,sprintあたりのcostとsprint数を掛ける
- 固定scopeのreleaseの場合は,sprintあたりのcostと最大sprint数,最小sprint数をそれぞれ掛ける
- story_pointあたりのcostを把握しておけば,story_pointさえ求められればcostが分かるという別の方法がとれる
- 手順
- communication
- 固定scopeのreleaseにおける進捗の把握
- 完了させない作業のscopeが分かっている
- その作業の完了に向けて,今どの程度の進捗なのかを把握することが目標
- burn_down_chart: 現在のrelease goalに達するまでの残作業の累計を,sprint単位で示したもの
- y: 残story_pointの見積もり, x: release内のsprint
- burn_up_chart: releaseにおける作業の総量をgoalとして示し,sprint単位でそのgoalに近づいていく様子を示した図
- 軸はburn_down_chartと同じ
- 好まれる理由: release内でのscopeの変更を表しやすいから
- 固定期日のreleaseにおける進捗の把握
- releaseにおけるsprintの回数が分かっている
- sprintごとの進捗を見ながら,最終的にどの程度のfeatureを完了させられそうなのか(deliveryできるscopeの範囲)を把握する
- product_backlogの並び順を反転して,burn_up_chartを作成する
- 目標とするfeatureの範囲に到達するまでの進捗の把握
- 必須featureの完成までの進捗も確認できる
- 固定scopeのreleaseにおける進捗の把握
- まとめ
- release_planningについて
- いつ,だれが,どんなことをするのか,最終的にどんなrelease_planができあがるのか
- 固定期日/scopeそれぞれの場合でのrelease_planningの方法と,進捗を把握する方法について
- release_planningについて
Part 4 sprint
Ch19 sprint_planning
- releaseは1つ以上のsprintで構成
- 各sprintでは,user/clientに価値を提供する
- sprintはsprint_planningから始まる
- Scrum_teamが集まって,sprint_goalに合意し,sprintで何をdeliveryできるかを決定
- Scrum frameworkにおけるsprint_planningの位置づけと実施方法について
- 概要
- sprintで構築する最も重要なproduct_backlog_itemを決定するために実施する
- sprint_goalについて合意する
- sprint_goalに合致しており,sprint終了までに現実的にdeliveryできるproduct_backlog_itemを決定する
- 決定したitemをきちんとdeliveryできる自信を持つために,product_backlog_itemを完成させるための計画が必要
- 選択したproduct_backlog_itemと計画を合わせたものが,sprint_backlogになる
- timing
- 継続的なjust_in_time activity
- sprint開始時に実施する
- 2週間から1か月のsprintの場合,sprint_planningに4~8時間かける
- 参加者
- Scrum_team全員
- product_owner: 最初のsprint_goalを共有して,product_backlogの優先順位を示し,product_backlog_itemに対するteamの疑問に答える
- development_team: sprintで何をdeliveryできるか決定し,現実的なcommitment
- Scrum_master: Scrum_teamのcoachとしてplanning activityを観察し,内省を促す質問をして,確実に良い成果が得られるよう支援する
- 何を作るか代わりに決められはしない
- teamのcommitmentに問題を提起して,現実的で適切なcommitmentができるようにする
- process
- input
- product_backlog
- 最も重要
- 準備完了の定義に合うように,sprint_planningよりも前にgrooming
- 準備完了の定義: 受け入れ条件が定義済みで,適切なsizeで,見積もりが終わっていて,優先順位がつけられている
- team velocity
- 現実的な判断のため(以下2つも同じ)
- 制約
- team capacity
- initial sprint_goal
- development_teamのcommitmentが現実的なものになるため
- teamは優先順位のbalanceが取れる
- product_backlog
- sprint_goalを達成するための計画を作る
- sprint_backlog
- product_backlog_itemを見積もり可能なtaskに分解
- 1つのtaskは8時間以内
- product_backlog_itemを見積もり可能なtaskに分解
- sprint_backlog
- sprint_planningが終わると,development_teamのcommitmentが最終的なsprint_goal, sprint_backlogとなって伝えられる
- input
- sprint_planningの手法
- 二部構成のsprint_planning
- what part
- development_team capacityを決定し,sprint終了時にdeliveryできるproduct_backlog_itemを予想する
- how part
- product_backlog_itemをtaskに分解して,task完了に必要な工数を見積もり,計画を立てる
- 見積もったtaskの時間とcapacityを比較して,最初のcommitmentが現実的かどうか確認
- 予想がcapacityの範囲や制約に収まれば,commitmentを最終決定して,sprint_planningを終了する
- what part
- 一部構成のsprint_planning
- itemの選択とそのための自信の獲得を交互に行う
- まず,development_teamがcapacityを決定する
- 次に, product_backlog_itemを選択して,それがsprintに収まるという自信を持つ
- これをteamのcapacityの上限に達するまで続ける
- 最後にcommitmentを最終決定して,sprint_planningを終了する
- 二部構成のsprint_planning
- capacityの決定
- sprint_planningの最初の重要なactivity
- what's capacity
- 影響を与える要因
- sprint以外のactivityに必要な時間
- sprint以外のcommitment
- 個人的な休暇
- 必要なbuffer
- 経験的に決定
- capacity
- 単位
- product_backlog_itemに使う単位(story_point or 理想日)
- team velocity
- teamのこれまでの平均velocityや前回のsprintのvelocityを参考にする
- そして今回のsprintとの違いを考慮する
- → 予測したvelocity
- team velocity
- or sprint_backlog_taskに使う単位(作業時間)
- まず,team_memberが次のsprintで何日作業できるかを表明する
- 次に,sprint以外のactivityにかかる日数を決定
- team_memberの1日の作業時間を決定
- product_backlog_itemに使う単位(story_point or 理想日)
- 単位
- 影響を与える要因
- product_backlog_itemの選択
- sprint_goalがあれば,それに合わせてproduct_backlog_itemを選択する
- なければ,product_backlogの優先順位の高いほうから順番にitemを選択する
- skillの問題などでitemにcommitできないときは,優先順位がその次に高くて実現できそうなproduct_backlog_itemを選択する
- 完成できそうにない場合は,clientにとって価値のある小さなitemに分割するか,完成できそうな別のitemに着手する
- 準備完了の定義を用意しておくことで,定義・resourceが不十分だったり,依存関係が未解決のproduct_backlog_itemを選択して,sprintで完成できなくなることを回避できる
- 「完成できそうなものだけを開始する」
- WIPの制限や未完成作業のムダの原則に基づく
- 出荷判断可能なproduct incrementがsprint終了時に必要
- WIPの制限や未完成作業のムダの原則に基づく
- 自信の獲得
- velocityを予測
- commitmentが現実的かどうか確認
- ただし,実際はtask levelにするまで分からない
- → さらに,product_backlog_itemをtaskに分解することで自信を獲得する
- taskは見積もり可能で,team capacityを考慮して決定
- 一種の設計で,itemを完成させるためのjust_in_timeの計画
- 分解した結果がsprint_backlog
- それぞれのskillを把握しておかないと,正しいcommitmentができない
- ただし,個人へのtask assignは×
- taskは見積もり可能で,team capacityを考慮して決定
- velocityを予測
- sprint_goalの洗練
- sprint_goal: sprintでのbusinessの目的と価値をまとめたもの
- commitmentの最終決定
- sprint_planningが終了するまでに,development_teamはsprintでdeliveryするbusiness valueにcommit
- sprint_goalと選択したproduct_backlog_itemは,commitmentを具現化したもの
- 予想/commitment
- ニュアンスの違いは,development_teamが何をdeliveryするかのscopeと,Scrum_teamがsprintの実施中に手に入れた新しい情報への対応に影響する程度
- まとめ
- sprint_planningをいつ実施するか,誰が参加するか
- 2つの手法
- teamがproduct_backlog_itemを選択して,それらをすべてdeliveryできるという自信を獲得
- product_backlog_itemの選択と自信の獲得を交互に行い,少しずつcommitmentに追加する
- development_team capacityを決定するための2手法
Ch20 sprintの実施
- Scrum_teamがsprint_goalを達成するための作業
- sprintの実施において,Scrum_teamが計画,管理,実行,伝達するためのprinciple, technique
- 概要
- sprintの実施は,それ自体が1つのmini projectのようなもの
- 出荷判断可能なproduct incrementをdeliveryするために必要な作業をすべて行う
- timing
- sprint_planningが終わった後に始めて,終了したらsprint review
- 8割程度はsprintの実施が占める
- 参加者
- development_teamのmemberが自己組織化して,sprint_planningで設定したgoalを達成する最善の方法を決定
- Scrum_masterは,あらゆる支援
- product_ownerは,質問に答えたり,feedback, sprint_goalを調整,product_backlog_itemが満たすべき受け入れ条件を検証
- process
- input
- sprint_planningで作ったsprint_goalとsprint_backlog
- output
- 出荷判断可能なproduct_increment
- 開発が終わったproduct_backlog_itemの集まり
- Scrum_teamの完成の定義に従う
- 開発が終わったproduct_backlog_itemの集まり
- 出荷判断可能なproduct_increment
- 実施
- testされた動作する機能に必要な計画,管理,実行,伝達
- input
- sprintの実施は,それ自体が1つのmini projectのようなもの
- sprint_planning
- 万全な計画にはしない
- taskに依存関係があるときは,事前の計画が有用なこともある
- sprintの実施の原則: 事前に完璧な作業計画を立てるのではなく,状況に応じてtask levelの計画を立てる
- teamで継続的にtask planningをすることで,sprintを取り巻く環境の変化に適応できる
- flow management
- teamの責任
- どれだけの作業を並行でやるべきか,特定のitemにいつ着手すればよいか,task levelの作業をどのように構成するか,何の作業をすべきか,誰が作業すべきか,を決める
- 並列作業とswarming(群がる)
- 単純なmulti taskでもoverheadは極めて高い
- 並列作業が少なすぎてもムダ
- swarming
- 適切なbalanceのために,team_memberのT型skillやcapacityを活用できるだけのitem数に絞って作業する
- deliveryするvalueを最大化しながらも,各itemの作業時間を短縮
- 新しいitemに着手する前にcapacityあるteam_memberが集まって,すでに誰かが着手したitemを完成させる
- taskではなくgoalに集中し,より多くより早く作業が終わる
- 同時に作業するitemの数を極力減らすべき
- 複数のitemが適切な場合もあるので,必ずしも1つではない
- 適切なbalanceのために,team_memberのT型skillやcapacityを活用できるだけのitem数に絞って作業する
- mini waterfallのようになるのは×
- 同時にすべてのproduct_backlog_itemに着手することになるため
- → riskが非常に高い
- どの作業から着手すべきか
- itemの優先順位をproduct_ownerが決定
- 技術的な依存関係やskill capacityの制約がitemの順番に影響を及ぼすため,常には上手くいかない
- → development_teamは,itemを適時適切な順番で選択する能力が必要
- itemの優先順位をproduct_ownerが決定
- どのようにtaskを計画すべきか
- 作業に論理的な順番はない
- test first development
- 適時team_memberがtaskや担当をうまく構成する
- → 作業の手待ちを最小化し,team_memberがほかのmemberに手渡す作業の量や回数を減らせる
- → すばやいfeedback, 課題の発見と解決が迅速に実施できる
- どの作業を完了させるべきか
- team_memberが決定
- 経済的に合理的な方法でinnovative solutionを作るための権限を与える必要
- product_owner, managerは,どのtaskを完了させるべきかに影響を与えるinputを持っている
- product_ownerが機能のscopeと受け入れ条件を確定する.いずれもtask levelの作業の境界線となる
- 完成の定義に必要なbusiness 要件を提供する
- product_ownerは,businessに重大な結果をもたらす技術的判断が経済的合理性をもって行われるように考慮しながら,teamと一緒に作業する
- 決定の中には,完成の定義の技術的な側面に組み込まれているものもある
- そのほかの決定は,機能に特化したもの
- development_teamはproduct_ownerと一緒にtradeoffを行い,経済的に合理的な決定をすることが望まれる
- team_memberが決定
- 誰が作業をするのか
- 作業をすばやく正しくできる人が最適
- 様々な要因が影響する
- team_memberの共同責任
- T型skillで,より効率的なteamになる
- daily_Scrum
- team activityを高速にして,solutionまでの流れを柔軟にする,毎日の検査と適応のactivity
- 24時間ごとに15分のtimebox
- 自己組織化teamが仕事をより良くするために,検査とsyncを行う適応型planning activity
- goal: sprint_goalの達成に集中している人たちが集まって,何が起きているかの全体像を共有し,どれだけの作業をしているか,どのitemに着手したか,team_memberの最適な作業配分についてみんなで理解すること
- 待ち時間の回避にもなる
- flowの管理に欠かせない存在
- taskの実行(technical_practice)
- 短期間のtimeboxというiterationで作業する → 出荷判断可能なproduct incrementを届けることを期待されている
- 技術的負債を制御して仕事をやりきるというpressureがteamにかかる
- → 適切な技術的skillを持っていなければ,長期にわたって継続的にbusiness valueを届けるために必要なagilityのlevelを達成できない可能性がある
- Scrumを使う場合は,software developmentのtechnical_practice_skillが必要になる
- TDD, refactoring, simple design, pair programming, CI, codeの共同所有, coding standard, metaphor
- called as XP
- TDD, refactoring, simple design, pair programming, CI, codeの共同所有, coding standard, metaphor
- 長期的なScrumの利点のためには,task levelの作業に,強力なtechnical_practiceが必要
- 短期間のtimeboxというiterationで作業する → 出荷判断可能なproduct incrementを届けることを期待されている
- communication
- information radiator(radiate: 放射 ← 光を放つ)
- task_board, burn_up_chart, burn_down_chartの組み合わせ
- task_board
- sprintの進捗が一目で伝わる簡単だが強力な手法
- sprint_backlogの変化の状態を伝える (→ 図20.6)
- sprint_backlog_itemが複数のtaskで表現
- すべてのtaskはTO DOから
- → 進行中
- → 完了
- カンバンではより詳細なboard
- sprint burn_down_chart
- product_backlog_itemのtaskは,いつでもsprint_backlogに追加できる
- y: 見積り残作業時間, x: sprint日数
- 進捗管理に使える
- 傾向線を書くことで,sprintのflowを管理するための知識となる重要なdataを得られる
- 常に残りの見積もり時間を使用する.実際にかかった作業時間は不要.
- sprint burn_up_chart
- sprintの進捗を可視化
- 完了した作業の量を表す
- y: story_point, or 作業時間, x: sprint日数
- ほとんどはstory_point
- ↑ business valueのための成果に注目するため
- 作業の流れが滞っていないか,teamがどのようにproduct_backlog_itemを完成させているかの感覚がつかめる
- ほとんどはstory_point
- sprintの進捗を可視化
- information radiator(radiate: 放射 ← 光を放つ)
- まとめ
- sprintの実施について
- sprintの大部分を占めるもの
- who, what, whenの計画に従うものではない
- teamのskillを活用して,完了した作業やsprintの予期せぬ環境の変化からfeedbackを受け取る
- flow management principleに従う
- 並行していくつの作業をするか,何の作業に着手するか,作業をどのように構成するか,誰が作業をするか,作業にどの程度の時間をかけるか,を決める
- daily_Scrum meetingはflow managementのimportant activity
- technical_practice's importance for high level agility
- sprintの進捗を視覚的に伝える
- task_board, sprint burn_down_chart, sprint burn_up_chart
- sprintの実施について
Ch21 sprint_review
- productに集中したもの
- 目的,参加者,事前準備,問題について
- 概要
- 作業結果(出荷判断可能なproduct_increment)の検査と適応をする.
- sprint終了間際に実施する
- product developmentのinputにかかわった人に対して,これまでに構築したものについて検査と適応をする機会を提供する
- 質問,観察,提案したり,現状を考慮して今後の方針について議論する
- 正しいproductを開発中だと組織が確信できるようになる
- → 最も重要な学習loopの1つ
- sprintの期間は短いので高速
- → 頻繁に軌道修正〇
- → 最も重要な学習loopの1つ
- 参加者
- sprint_reviewは,Scrum_teamに対して,sprintの実施に毎日参加できないような人たちからのfeedbackを得る重要な機会を提供
- sprintで作成された成果について議論する最初の機会
- すべての利害関係者が参加すべき
- Scrum_team
- 内部stakeholder
- business area owner(developing systemの発注者), exective management, resource担当managerなど
- → teamが経済的に合理性のある成果に向かっているか確認
- 内部userがいる場合は内部userからのfeedbackも有益
- そのほかの内部stakeholder
- 営業,marketingから,productが市場で成功するかどうかの有益なfeedbackを得る
- support, 法務部は,teamの進捗の把握や,timelyなinputを提供したり,自分たちの仕事をいつ開始すればよいか判断するために,参加することもある
- ほかのdevelopment_teamの代表者も〇
- 外部stakeholder
- client, user
- direct feedback
- 内部の議論で済むなら,必要はないかもしれない
- 常識,熱意,人格を考慮して招待する対象を検討
- client, user
- すべての利害関係者が参加すべき
- 事前準備
- 誰を招待するのか決める
- 適切な人たちを集める
- schedule調整
- Scrum_team以外の参加者が多いので,調整が難しい
- important stakeholderが希望するscheduleを聞いてから,そのほかのactivityをその時間の前後に設定する
- sprintの期間は固定されているので,ほぼすべてのsprint_reviewのscheduleが決まる
- 長さ
- sprintの期間,teamの規模,reviewに参加するteam数など複数の要因で決まる
- 4時間のtimeboxを超えない
- sprint 1週間につき1時間
- sprintの成果が完成したことを確認
- 利用可能になったproduct_backlog_itemを,product_ownerがjust_in_timeでreview
- demoを準備
- goal: productの検査と適応のために透明性を提供すること
- 儀式を少なく,価値を高く
- sprint 1週間につき30分から1時間をsprint_reviewの準備に使う
- 誰が何をするかを決める
- facilitator, demoを誰がするか,Scrum_teamが決定
- 誰を招待するのか決める
- approach
- input
- sprint_backlog, sprint_goal, teamが作成した出荷判断可能なproduct_increment
- output
- groomingしたproduct_backlogと更新したrelease_plan
- sprint_goalの何を達成して何を達成していないかの概要を示し,出荷判断可能なproduct_incrementのdemoをして,productの現状について議論し,今後のproductの方向性を適応させる
- 概要
- sprint_goal, sprint_goalに関連したproduct_backlog_item, sprintで実際に開発したproduct_incrementの概要をScrum_teamのmemberが伝えるところから始まる(product_ownerが担当することが多い)
- sprintの成果とsprint_goalを比較したときの結果が分かる
- 不一致なら,説明
- 責任追及,非難はしない
- 何が達成できたかをもとに,今後取るべき行動を決定する
- 不一致なら,説明
- demo
- sprint_reviewの目的はdemoではない
- 最も重要な側面は,参加者たちの徹底した対話や協力
- → 生産的な適応を可能にし,有効活用する
- demoは,対話をより具体的にするための効率の良い方法
- 準備完了の定義に,Scrum_teamがdemo方法を理解することも含まれる
- product_ownerの要求を満たしていることをdemoするテストが必要
- sprint_reviewでは,テストを使ってdemoできる
- sprint_reviewの目的はdemoではない
- 議論
- product_incrementのdemoは会話の中心になる
- productや今後の方向性に関する意見,コメント,妥当な議論が,参加者に強く求められる
- sprint_reviewは問題解決の場ではない
- Scrum memberが,client/userを喜ばせるためのfeedbackを得ることで,productのbusiness, marketingの認識を深める
- 適応
- question
- stakeholderは見たものを気に入っているか
- 変更点を見たいと思っているか
- 今でも市場やclientに受け入れられるのか
- 重要な機能が不足していないか
- 不必要なところに機能を作りこみすぎたり,costをかけすぎていないか
- → product_backlogやrelease_planの適応のinputになる
- grooming @sprint_review
- より大きなscopeのrelease_planにも影響する
- 変動要因に影響
- scopeの変更など
- 変動要因に影響
- より大きなscopeのrelease_planにも影響する
- sprint終了ごとに,適応の方法,変化の反応方法,それらを実施できる時期を見つけるための機会を提供する
- question
- input
- sprint_reviewのproblem
- signoff
- 正式な認可やsignoffのeventではない
- 事前にproduct_ownerが承認済み
- product_ownerがproduct leadershipの権限を持つため,承認は決定事項
- 要求があれば新しいproduct_backlog_itemを作成し,機能変更の予定を立てるのが〇
- なぜ要求を出した人がstakeholderから外れていたか調査し,今後は調整する
- 正式な認可やsignoffのeventではない
- 参加者の不在
- 数週間で価値のあるものを作り出せると思われていないため
- 解決方法: business valueのある出荷判断可能なproduct_incrementをsprintごとに開発する
- → 頻度の高いreviewに参加する価値を理解してもらえる
- 大規模project
- sprint_reviewの共同実施
- merit
- stakeholderはsprint_reviewに1回だけ参加すればよい
- 成果が統合できれば,個別のincrementではなく統合した結果をreviewすればよい
- すべてのteamが完成の定義に統合test要
- demerit
- 時間がかかる
- 大きな部屋が必要
- merit
- sprint_reviewの共同実施
- signoff
- まとめ
- sprint_reviewの目的であるScrum developmentのfeedbackについて
- 参加者のgoalは,現状のproductの検査と適応
- 非公式なactivityだが,最小限の事前準備をする
- Scrum_teamは,reviewの概要とsprintで達成したことを説明する
- product_incrementのdemoをする
- 質問,意見,提案が歓迎される
- grooming, release_planの更新を行う
Ch22 sprint_retrospective
- productを構築するのに使ったprocessを,Scrum_teamが調査する
- 目的,参加者の概要,事前準備,主要なactivityを見る
- 最も重要なのは,参加者が発見した改善をsprint_retrospectiveの後に継続的に行うこと
- 概要
- 考える機会を与えるもの
- teamは何が起きているのかを調査したり,自分たちの仕事のやり方を分析したり,改善の方法を見つけたり,改善の計画を立てたりする
- teamのproductの作り方に影響を与えるものであれば,なんでも自由に調査・議論できる
- e.g. process, practice, communication, 環境, 作成物, tool
- 最も重要で,最も使用されているpractice
- teamが自分たちの環境に合わせてScrumをcustomizeできる機会を提供するため
- しかし「実際の」作業ではないので過少評価されている
- Scrumが提供する継続的改善に不可欠
- sprintごとにretrospectiveを行い,insightやdataを見失うことなく活用できる
- 早期にincrementalな学習ができる
- → projectの成果に大きな影響
- theme
- sprintでうまくいったので,今後も続けたいこと
- sprintでうまくいかなかったので,今後は中止したいこと
- これからやった方がよいこと,改善した方がよいこと
- → 行動可能な変更点を決定,incrementalに改善したprocessで次のsprintに取り組む
- 考える機会を与えるもの
- 参加者
- Scrum_team全員
- team memberの多様な視点で,さまざまな観点からprocessの改善点を見つけられる
- Scrum_master: processに欠かせないRole,processの権威
- teamが合意したprocessで守られていない点を指摘
- teamにとって重要な知識やideaの源泉となる
- product_ownerとteamの信頼関係をScrum_masterがcoachingして築くまでは,product_ownerは参加できない
- 信頼と安全が確保できれば,product_ownerは欠かせない
- 高速で柔軟性の高いbusiness valueのflowの達成のため
- 要求のflowをteamに伝えるchannelやrouteなどのRoleを担う
- stakeholder, managerなどは,招待されたときだけ
- 参加は安全性の問題がないときだけ
- Scrum_team全員
- 事前準備
- sprintの期間が短かったり,実践的で簡潔なretrospectiveのformatを使っていれば,事前準備には必ずしも時間はかけなくていい
- focusを定義
- default: Scrum_teamが使用したprocessの関連する側面をすべてreviewする
- 現状のteamの重要性や改善点に合わせて,別のfocusの選択も〇
- e.g.
- TDD skill up方法にfocus
- 要求の誤解の頻発の理由にfocus
- e.g.
- 開始前にfocusが分かることの効果
- Scrum_team以外の人たちを招待するかどうか決定できる
- 適切なexerciseを選択できる
- retrospectiveを円滑にするために必要なdataの収集や準備に時間をかけられる
- 具体的に定義 → retrospectiveから計測可能な価値を抽出〇
- → 持続可能なhigh performance Scrum_teamになる
- exerciseを選択する
- 協力して考え,探索し,意思決定するためのもの
- 通常のexercise
- event timelineの作成と発掘
- insightのbrainstorming
- insightのgroupingと投票
- focusや参加者に合わせて変更可能
- 新鮮さのために新しいexerciseを試すのも〇
- 事前準備で決める必要はない, just_in_timeで決定〇
- data, 備品が必要なexerciseは,事前準備で決めておく
- 準備しながら,柔軟性は確保
- 客観的dataを収集する
- data収集に必要な作業は,retrospectiveの開始前に済ませておく
- 客観的data: 確実なdata(not 意見)
- e.g. どのようなeventがいつ起きたか,着手したが完成しなかったproduct_backlog_itemの個数,完了した作業のflowを示したsprint burn_up_chartなど
- この段階では,dataの構造化や分析は行わない
- retrospectiveを構成する
- sprint_reviewと場所や時間を合わせるかどうか,参加者やexerciseの都合などから考えておく
- 正確な時間は,team人数,年数,memberがremoteにいるかどうかなどの要因の影響を受ける
- sprintの期間に合わせて時間を決定する
- 2週間のsprintなら1.5時間をretrospectiveに割り当てる
- sprintの期間に合わせて時間を決定する
- 成功に最適な場所
- 情報にaccessしやすい仕事場
- 安全な環境のために仕事場以外
- facilitatorは,Scrum_masterがやることが多いが,誰でもよい
- 中立性のため外部から招くこともある
- approach
- input
- 合意したfocusなど,retrospectiveで使用するとteamで決めたものならどんなexerciseや道具でもいい
- 事前に収集した客観的data
- 参加者は,sprintについての主観的なdataを持参する
- 前回retrospectiveで作成したinsight backlog
- output
- 次のsprintで行う具体的な改善action
- 今回retrospectiveで作成したinsight backlog
- 仲間意識の向上
- theme
- sprintでうまくいったので,今後も続けたいこと
- sprintでうまくいかなかったので,今後は中止したいこと
- これからやった方がよいこと,改善した方がよいこと
- 有効なapproach
- retrospectiveの雰囲気づくり
- team/個人を徹底検証するのは不愉快な経験なので,心地よくなってもらうための雰囲気づくりが必要
- 報復を恐れずに自分の意見を言えるような,安全性を感じてもらう
- rule設定/取り決め
- 自分の意見や内輪の恥をさらしても安全であることを明確にする
- 間違いを探しても安全だとわかるように,個人ではなく組織のsystemやprocessにfocusしていることを明確にするruleを設定しておく
- 問題が人の問題であることもあるが,retrospectiveは問題解決の場ではない
- 責任の所在を明らかにしたり,個人の振る舞いを非難したりする場ではなく,Scrum_teamのprocessを改善する場
- 非難のない環境を作るためのruleであることを明確にしておく
- rule設定/取り決め
- 積極的な参加の前例を作る
- 参加者に口を開いてもらう
- 今の感情やenergy levelを一言で表現してもらうteamもある
- 内容より,何かを言うことで,話そうという雰囲気を作る
- 参加者に口を開いてもらう
- 参加者によるcontextの共有
- → 客観的に大局的にsprintを見ることができるようになる
- 多様性のある個人の視点をteamの視点に統一
- sprintのevent, 達成, 欠点に対する大局的な考えに基づくようにする
- × 個人的な経験
- → ただの意見の言い合いになる
- × 個人的な経験
- sprintのevent, 達成, 欠点に対する大局的な考えに基づくようにする
- contextを共有して行動可能な成果に注力
- 雰囲気づくりができたら,客観的data(commited product_backlog_item, 完成したproduct_backlog_item, 不具合数など, retrospectiveのfocusに合ったもの)を共有する
- 主観的dataも共有・議論する
- → 誤解の回避のため
- 誤解があると,他人のcommentや提案を理解できない
- event_timeline
- sprintのeventの流れをvisualに表現した作成物
- 簡単で強力
- event: buildが動かなくなった,本番環境の修正で手間取った,Salinaが休暇から戻ったなど
- timelineを壁やwhiteboardに描き,eventを表すカードをtimelineに貼り付ける
- colorfulなcardを使う
- sprintのactivityの流れが時間軸で可視化されて,contextがよくわかる
- → 見逃していたり忘れていたeventをすばやく特定できる
- 感情seismogram(seiein: 揺れる)
- event_timelineの補完
- 上下に並べる
- sprintにおける参加者の感情の起伏を視覚的に表現
- 客観的dataだけでなく,主観的dataまで含めてcontextを共有できる
- event_timelineの補完
- 改善につながるinsightの特定
- dataを深く調査,理解,解釈して,process改善のためのinsightを獲得
- system levelの(より大きな)focusが必要
- → 表面的なproblemだけでなく,根本的な原因を特定できる
- まずは共有したdataを発掘
- insightを明らかにするための質問
- 何がうまくいったか
- 何がうまくいかなかったか
- やり方を変えるところはないか
- → brainstorming
- insightを明らかにするための質問
- insight backlog
- 優先順位をつけたが,まだ実行されていないinsight
- 検討すべきものがあるか調査
- insightを意味のあるgroupに分けて,類似や重複しているカードを示す親和図法のようなexerciseを行うことが多い
- silent groupingは,時間の効率も効果も高い
- 複数のcategory area(継続,中止,挑戦)に分けることもある
- → 参加者同士の深い議論につながり,根本原因,重要なpattern,関連をよく理解できるようになる
- 次のsprintで実施する具体的な改善actionの決定
- insightからvalueを抽出するために,実際に行動可能なものにする
- 前回から行われた改善actionのreviewもする
- 何故できなかったのか把握
- 改善を続けるか,優先順位を新しいinsightと入れ替えるかなどする
- insightの選択
- 短期間で実行できる以上のinsightが手に入る
- 参加者がどれを重要と思っているか,どこを改善していきたいと思っているかで決めることが多い
- ドット投票
- 参加者がinsightに専念できるcapacityや期間によって,いくつ選択するか決定する
- 期間: 次のsprint期間
- capacity: 前回のretrospectiveで見つけたactionの計画から決定できる
- product_ownerからのinputをもとに,Scrum_teamが時間を割り当てる
- capacityが分かれば,どのinsightに取り組むべきか分かる
- actionの決定
- 優先順位のついたinsightが手に入り,それらに取り組むcapacityが判明した状態
- 具体的なactionの決定
- ほとんどのactionは,次のsprintで1~2人のScrum_team memberが担当できる程度の具体的なtaskになる
- development_team_memberのtask levelの作業が必要
- 誰がどれだけの時間をかけるかteamで決定する
- → insightが実行可能かどうか決まる
- すぐにinsightには取り組めない
- 次のsprintではinsightの調査が必要
- まず問題をよく理解するためのactionを作る
- insight backlog
- retrospectiveで発見したが,すぐには改善できない課題をまとめる
- backlogのinsightと新しいinsightを比較して,次のsprintで集中するところを決める
- 定期的にgrooming
- 選択肢しなかったinsightを破棄することもある
- 必要ならまた発見できるという考え
- retrospectiveの終了
- 学んだことをふまえて,決定したteamのactionを再確認する
- commited actionと担当者を読み上げるだけの簡単なものでいい
- 感謝を述べる絶好の機会
- retrospectiveを改善できる提案を求める
- 学んだことをふまえて,決定したteamのactionを再確認する
- retrospectiveの雰囲気づくり
- input
- その後のfollow
- 参加者が選択したactionをfollow_up
- 何度も言い続けるしかないものもあるが,それ以外はsprint_planningで指摘する
- actionに関連するtaskをsprint_backlogに追加して,新しい機能よりも優先順位を高くする
- 分離×.統合〇.
- team_memberの時間を必要としないactionは,Scrum_masterのimpediment listに加える
- ほかのteamや組織のactionは,担当者の適切なbacklogに入れておく
- 確実に実行されるように,Scrum_masterは外部の人たちをfollow
- 参加者が選択したactionをfollow_up
- sprint_retrospectiveのproblem
- retrospectiveを実施していないか,参加者が少ない
- All fluff(ふわふわしたもの, つまらないこと), no stuff(本当に役立つこと)
- 外部からretrospective facilitatorを招いて支援してもらう
- 部屋にいる象を無視している
- 劇的な影響をもたらす重大な問題に誰も触れない
- Scrum_masterがleadershipのRoleを担い,まずそのimpedimentを除去する
- facilitatorのskill不足
- 外部からretrospective facilitatorを招いて支援してもらう
- 気がめいってenergyが奪われる
- 雰囲気づくりの時間を作る
- 責任追及game
- facilitatorがすぐに悪い振る舞いを消滅させるようにする
- 不満大会
- face to faceで対話するようにする.
- 現場の改善をつぶす
- retrospectiveは改善活動そのものではない
- 意識が高すぎる
- Scrum_masterが熱意を和らげるよう支援する
- followがない
- Scrum_masterの支援が必要
- まとめ
- sprint_retrospectiveは,teamがScrumをうまく活用できたか振り返る時間,改善案を策定する時間
- Scrum_team member(必要ならそれ以外の人たちも)の共同作業
- 事前準備が終わったら,雰囲気づくりをして,dataに基づいたcontextを共有して理解を統一し,改善insightを見つけ,改善actionを決定し,retrospectiveを終了する
- 終了後は,参加者をfollowして,改善actionを実行していくことがimportant
- → 次のsprintでteamがより効果的になる
- 成功を阻害する問題に注意を払い,すばやく対処することもimportant
- sprint_retrospectiveは,teamがScrumをうまく活用できたか振り返る時間,改善案を策定する時間
Ch23 未来へ
- Scrumを実践するうえでの,普遍的な最終目的はない
- agilityに向かって,独自の道を定義する必要がある
- 最終形態などない
- Scrumの適応やScrumの移行について完成の定義はない
- 継続的改善の一形態
- より効率的かつ経済的にbusinessの目的を達成するための手段に過ぎない
- productによって状態が異なる
- Scrumの適応やScrumの移行について完成の定義はない
- 独自の道を探し出す
- 学び,検査し,適応して,進むべき道を探る
- base: 組織独自の目標・文化,変わりゆく複雑な環境
- 自分の学習loopを素早く完結させて,その学びに基づいて,検査と適応を行う
- 学び,検査し,適応して,進むべき道を探る
- best practiceを共有せよ
- practice: Scrumの核心的基礎的な側面
- approach: Scrumのpracticeの1つの実装
- Scrumを使って,進むべき道を探し出せ
- enterprise_transition_community
- managerと経営者で構成されたgroup
- 組織変革のための取り組みや,Scrum_teamを妨げている重大なimpedimentの除去がbacklogに入る
- Scrumを適用するためにScrumを用いる
- iterative and incrementalによりいっそうAgileになっていくうえで適切なapproach
- enterprise_transition_community
- 歩みを止めるな
- 経験上,teamの最初の2回程度のsprintはうまくいかない
- 次のsprintで,前のsprintより良くなっていさえすればよい
- 始めるのを遅らせてはならない
- Scrumの実施を難しくするimpedimentが必ずある
- 機能不全やムダが可視化される
- こうした課題の解決は,Scrumでは組織の中にいる人にゆだねられる
- 現状維持をしたいという強い力の考え
- 組織を変革するために確固たる信念をもって辛抱強く立ち向かう必要
- 変革に対する抵抗は自然なもの
- 協力し合うことで障害を取り去る
- → team, product development, 組織が,Scrumの実践の恩恵を十二分に理解できるようになる