⚡️ReactiveInputMethod
🍣入力メソッドを拡張し、テキストを入力し、Enterで確定できるようにした。
コード
https://github.com/mzp/EmojiIM/tree/marked
未確定文字列の挿入
未確定文字列は、入力セッションの一部としてマークされている文字列なのでmarked textと呼ばれる。
IMKTextInput
プロトコルの setMarkedText
で設定できる。例えば"あいうえ"を未確定文字列として表示したい場合は、以下のコードになる。
let notFound = NSRange(location: NSNotFound, length: NSNotFound) client.setMarkedText("あいうえ", selectionRange: notFound, replacementRange: notFound)
クリアしたい場合は第一引数に空文字列を渡す。nilを渡すとクラッシュする。
状態遷移
未確定文字列の入力した後、入力を確定するためには、入力メソッド内で状態遷移を管理する必要がある。
未確定文字列を持たない初期状態と、未確定文字列を持つ入力中状態があり、キー入力とEnterキーの入力で遷移するので 状態遷移図は以下のようになる。
ReactiveAutomaton
状態遷移を記述するためにReactiveAutomatonを用いる。
これはReactiveCocoaで状態遷移機械を書くためのライブラリである。アイデアは以下のスライドで解説されいる。
このライブラリを使い、入力メソッドの状態遷移および遷移時のアクションを定義する。
入力
ユーザの入力はテキスト入力とEnterの押下の2種類とする。 以下のenumで定義する。テキスト入力は入力された文字列をパラメータに持つようにする。
public enum UserInput { case input(text: String) case enter }
状態
入力メソッドの状態は「通常状態」と「入力中状態」の2種類とする。以下のenumで定義する。
public enum InputMethodState { case normal case composing }
遷移
ReactiveAutomatonのDSLを用いて状態遷移を定義する。
static func isInput(_ state: UserInput) -> Bool { switch state { case .input: return true default: return false } } let mappings: [ActionMapping<InputMethodState, UserInput>] = [ /* Input <|> fromState => toState */ /* --------------------------------*/ isInput <|> .normal => .composing, isInput <|> .composing => .composing, .enter <|> .composing => .normal ]
遷移が発生した際に実行するアクションを指定できるようにDSLを拡張する。 (ReactiveAutomaton+Action.swift) そして、遷移時のアクションで確定文字列、未確定文字列を更新する。
let (text, textObserver) = Signal<String, NoError>.pipe() let markedTextProperty = MutableProperty<String>("") let mappings: [ActionMapping<InputMethodState, UserInput>] = [ /* Input <|> fromState => toState <|> action */ /* -------------------------------------------*/ isInput <|> .normal => .composing <|> { switch $0 { case .input(text: let text): markedTextProperty.swap(text) default: () } }, isInput <|> .composing => .composing <|> { switch $0 { case .input(text: let text): markedTextProperty.modify { $0.append(text) } default: () } }, .enter <|> .composing => .normal <|> { _ in textObserver.send(value: markedTextProperty.value) markedTextProperty.swap("") } ]
この状態遷移をもとに、オートマトンを作る。
let (inputSignal, observer) = Signal<UserInput, NoError>.pipe() self.automaton = Automaton(state: .normal, input: inputSignal, mapping: reduce(mappings))
入力コントローラとの接続
イベントの処理
オートマトンと入力コントローラを接続するために、オートマトンにイベントを送るメソッドを作る。 入力コントローラの仕様にあわせ、状態遷移が発生した場合は真を、そうでない場合は偽を返すようにする。
init() { ... automaton.replies.observeValues { switch $0 { case .success: self.handled = true default: () } } } func handle(_ input: UserInput) -> Bool { handled = false observer.send(value: input) return handled }
入力コントローラで、キー入力に応じて、このメソッドを呼びだす。
public override func handle(_ event: NSEvent!, client sender: Any!) -> Bool { if event.keyCode == 36 { return automaton.handle(.enter) } else if event.keyCode == 51 { return automaton.handle(.backspace) } else if let text = event.characters { return automaton.handle(.input(text: text)) } else { return false } }
未確定文字列・確定文字列の反映
オートマトンが持つ入力文字列、未確定文字列を監視し、更新があった際にクライアントアプリケーションに反映するようにする。これはReaciveSwiftのイベント監視の仕組みを用いる。
public override init!(server: IMKServer!, delegate: Any!, client inputClient: Any!) { super.init(server: server, delegate: delegate, client: inputClient) guard let client = inputClient as? IMKTextInput else { return } // 未確定文字列の反映 automaton.markedText.signal.observeValues { text in let notFound = NSRange(location: NSNotFound, length: NSNotFound) client.setMarkedText(text, selectionRange: notFound, replacementRange: notFound) } // 確定文字列の挿入 automaton.text.observeValues { let notFound = NSRange(location: NSNotFound, length: NSNotFound) client.insertText($0, replacementRange: notFound) } }
🔬Appleの非公開APIの調べ方
Touchbarに変換候補を出すの、とうとう成功した pic.twitter.com/zXNjawZct5
— mzp (@mzp) 2017年9月30日
入力メソッドからタッチバーを使う方法を調べるために使った各種ツールの使い方をメモしておく。
class-dump
実行ファイルからObjective-Cのヘッダファイルを生成する。 homebrewでインストールできた。
$ class-dump /System/Library/Frameworks/InputMethodKit.framework/InputMethodKit // // Generated by class-dump 3.5 (64 bit). // // class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2015 by Steve Nygard. // ..... @interface IMKServer : NSObject <IMKServerProxy> { IMKServerPrivate *_private; } + (id)inputDelegateClassNameFor:(id)arg1; + (id)inputControllerClassNameFor:(id)arg1; + (id)connectionNameFor:(id)arg1; + (id)_clientWrapperForXPCConn:(id)arg1; + (id)_clientWrapperForDOProxy:(id)arg1; + (id)imkServerSingleton; - (id)keyBindingManager; ...
lldb
class-dumpでは、だいたいの箇所がid型になってて詳細な使い型がわからない。 そのライブラリを使っていそうなプロセスにlldbでattachして使い方を調べた。
まずはSIPが有効だとプロセスにattachできないので、Mac OS X El CapiptanでSIPを無効化する - Qiitaなどを参考に無効化する。
目的のプロセスを調べて、attachする。 入力メソッドにattachする場合はそのマシンの入力が死ぬので、別マシンからsshするといい。
$ ps ax | grep '[E]mojiFunctionRowIM' 402 ?? Ss 0:03.51 /System/Library/Input Methods/EmojiFunctionRowIM.app/Contents/PlugIns/EmojiFunctionRowIM_Extension.appex/Contents/MacOS/EmojiFunctionRowIM_Extension $ lldb (lldb) attach 402
あとは各種コマンドを使って挙動を調べていく。 だいたい以下のコマンドを使った。
ブレイクポイントの設定
特定のセレクタにブレイクポイントを設定する。
br set --selector inputDelegateClassNameFor:
特定のクラスの全メソッドにブレイクポイントを設定する。
br set -r '\[ClassName .*\]$'
表示
# オブジェクトの表示 po $obj # 値の表示 p (BOOL)[$obj isHoge]
メソッド呼び出し時に使われるレジスタ
- self: $rdi
- _cmd $rsi
引数
変数への代入
expr id $foo = [$obj foo]
otool
それでも分からないやつは逆アセンブルした。 どのメソッド呼んでいるかなどを主に見た。
$ otool -tV /System/Library/Frameworks/InputMethodKit.framework/InputMethodKit /System/Library/Frameworks/InputMethodKit.framework/InputMethodKit: (__TEXT,__text) section -[IMKUICandidateController init]: 0000000000001d0c pushq %rbp 0000000000001d0d movq %rsp, %rbp 0000000000001d10 pushq %r15 0000000000001d12 pushq %r14 0000000000001d14 pushq %r12 0000000000001d16 pushq %rbx 0000000000001d17 subq $0x10, %rsp 0000000000001d1b leaq -0x30(%rbp), %rax 0000000000001d1f movq %rdi, (%rax) 0000000000001d22 movq 0xd6fc7(%rip), %rcx ## Objc class ref: IMKUICandidateController 0000000000001d29 movq %rcx, 0x8(%rax) 0000000000001d2d movq 0xd2dc4(%rip), %r14 ## Objc selector ref: init 0000000000001d34 movq %rax, %rdi 0000000000001d37 movq %r14, %rsi 0000000000001d3a callq 0x89b5c ## Objc message: -[[%rdi super] init] 0000000000001d3f movq %rax, %rbx 0000000000001d42 testq %rbx, %rbx 0000000000001d45 je 0x1de9 ....
🐙Githubブラウザ for iOS11
iOS11で追加されたFilesアプリから、Githubレポジトリを見るための拡張を作った。 AppStoreから入手できる。
Github->Octocat->Octopusという連想で、OctoEyeという名前にした。
⭐️使い方
OctoEyeをインストール・初期設定をすると、FilesのLocationsにGithubが追加される。
ここからレポジトリの内容をできる。
さらに、Textasticといった別アプリから開くこともできる。 ただし、保存はサポートしていない。
📦ソースコード
🎨デザイン
Sketch
最近Sketchを買ったので、事前にデザインを描いた。
今までは適当な紙に落書きするだけだったが、Sketchで描くと考慮漏れに気付けたり、単純に楽しかったりしてよかった。
アイコン
アイコンは最初いらすと屋のタコを使い、途中で自分で描いたのに差し替えた。
GithubなのでOctocatを使いたかったが、そのものを使うわけにはいかないので、一部だけ+シルエットにした。 猫耳だけを拡大したバージョンも作ったが今ひとつだったので段ボールバージョンを採用した。
ウォークスルー
初回起動時に出すウォークスルーを作った。 これは結構大変だった。
EAIntroViewの動きの確認。
アプリ起動直後にでてくるウォークスルーを作る練習してるけど、かわいい画像を挟んだら急に体験の質が向上した。 pic.twitter.com/TxG5HG28jJ
— mzp (@mzp) 2017年8月5日
iPadProで下書きをした。
雰囲気が分かったのでSketchでいろいろ描いて、デザインを検討した。
実装した。
🛠開発
Github Graphql
Githubから情報を取得するために、GraphqlAPIを利用している。 FileProviderExtensionではネットワークアクセスの回数を減らしたかったのでちょうどよかった。
ただバイナリデータを取得することができなかったので、そこだけはREST APIを使っている。
ReactiveCocoa/ReactiveSwift
レポジトリ追加画面などではReactiveSwiftを使っている。最初はRxSwiftを使おうとしたが、@banjunにReactiveSwiftのほうが語彙がSwiftっぽいと言われたので、ReactiveSwiftにした。
アイマスライブの合間にReactiveSwiftを教えてもらってたけど、プロデューサーの発音に関する話がよかった https://t.co/Xg9Hh6uJE9
— mzp (@mzp) 2017年7月31日
界隈の流行りに乗ってプロダクトに入れてみようと今週はReactiveSwiftやってたんだけど,Producer=「プロデューサー」,SignalProducer=「シグナルプロデュー↑サー↓」,小日向美穂「プロデュー↑サー↓」という知見を得た
— ばんじゅん🍓 (@banjun) 2017年5月24日
イベントをmap等で加工するのは分かるが、画面上の要素とバインドする部分がよく分かんないまま書いていた。
スクリーンショット
AppStoreに提出用のスクリーンショットはfastlaneのsnapshotとframeitで生成した。
snapshotで各デバイス用のスクリーンショットを準備し、frameitでiPhone/iPadの枠をつけ上部に文字を入れた。
Xcode9からUITestが複数アプリに対応したので、Filesアプリの撮影も自動化できた。(がMetadata Rejectになったので、このスクショは削除した)
その他
- CIはTravisCIを使った。 Xcodeの新しいベータが出て3〜4日でTravis側も更新されててすごかった。
- ベータ版のOSにはスクリーンショットを外部に出せない制約が付くので、プルリクエストに画像を貼れなかった。
🚀今後の予定
- Filesにあるタグ機能やお気に入り機能をサポートしてないのでやりたい。
- masterブランチ以外は見れないので、切り替え機能をつけたい。
- 書き込み機能もつけたいが、コミットメッセージをどうするかが難しいと思っている。
🍣入力メソッド
すべてのキー入力に対して🍣を入力する入力メソッドを作った。
🍣を入力するだけのInputMethodができた pic.twitter.com/0If6KVNTAT
— mzp (@mzp) 2017年9月16日
コード
https://github.com/mzp/EmojiIM
入力メソッドの構成
入力メソッドによるテキスト入力は、各アプリケーションと入力メソッドが通信することで実現されている。アプリケーションはキー入力を入力メソッドに送信し、入力メソッドがアプリケーションに対してテキスト入力を行なう。
この通信のために入力メソッドはIMKServerクラスを用いてサーバーを起動する。そして各アプリケーションはクライアントとなり、サーバーと通信する。この通信を入力セッション(input session)と呼ぶ。
サーバーはクライアントごとに入力コントローラーを生成する。この入力コントローラーがキー入力からテキスト入力を生成する。
プロジェクトの作成
Xcodeでプロジェクトを作成する。Cocoa Appテンプレートを選ぶ。 Product nameはなんでもいいが、Bundle identifierにinputmethodという文字列を含める必要がある。

起動時の処理
入力メソッドが起動時に各アプリケーションと通信するための IMKServer
を起動する。このとき、クライアントがサーバーに接続する際に使う名前を指定する。
import Cocoa import InputMethodKit private var server: IMKServer? @NSApplicationMain internal class AppDelegate: NSObject, NSApplicationDelegate { @IBOutlet private weak var window: NSWindow! func applicationDidFinishLaunching(_ aNotification: Notification) { server = IMKServer(name: "EmojiInputSession", bundleIdentifier: Bundle.main.bundleIdentifier) } }
入力コントローラ
入力コントローラとして使うクラスを定義する。このクラスは IMKInputController
の派生クラスにする必要がある。
このクラスの名前をInfo.plistで指定す。そのため、objc
属性を用いて、Swiftコンパイラによってマングリングされないようにする。
@objc(EmojiInputController) open class EmojiInputController: IMKInputController {
キー入力時に inputText
メソッドが呼ばれる。キー入力を処理したかどうかを真偽値で返す。
func inputText(_ string: String!, client sender: Any!) -> Bool
テキスト入力をしたいアプリケーションは client
引数として渡される。 ただし、 Any
型のままではテキスト入力を行なえないのでIMKTextInput
プロトコルにキャストする。 キャストに失敗した場合は、入力メソッドが処理しなかったことを示すために偽を返す。
guard let client = sender as? IMKTextInput else { return false }
IMKTextInput
の insertText
メソッドを用いて、入力したい文字列を挿入する。
client.insertText("🍣", replacementRange: NSRange(location: NSNotFound, length: NSNotFound))
Info.plist
Info.plistで入力セッションに使う名前や入力コントローラとして使うクラスの名前を指定する。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>CFBundleDevelopmentRegion</key> <string>$(DEVELOPMENT_LANGUAGE)</string> <key>CFBundleExecutable</key> <string>$(EXECUTABLE_NAME)</string> <key>CFBundleIconFile</key> <string></string> <key>CFBundleIdentifier</key> <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> <key>CFBundleInfoDictionaryVersion</key> <string>6.0</string> <key>CFBundleName</key> <string>$(PRODUCT_NAME)</string> <key>CFBundlePackageType</key> <string>APPL</string> <key>CFBundleShortVersionString</key> <string>1.0</string> <key>CFBundleVersion</key> <string>1</string> <key>LSBackgroundOnly</key> <string>YES</string> <key>LSHasLocalizedDisplayName</key> <false/> <key>LSMinimumSystemVersion</key> <string>$(MACOSX_DEPLOYMENT_TARGET)</string> <key>NSHumanReadableCopyright</key> <string>Copyright © 2017 mzp. All rights reserved.</string> <key>NSMainNibFile</key> <string>MainMenu</string> <key>NSPrincipalClass</key> <string>NSApplication</string> <!-- 入力セッションに使う名前 --> <key>InputMethodConnectionName</key> <string>EmojiInputSession</string> <!-- 入力コントローラのクラス名 --> <key>InputMethodServerControllerClass</key> <string>EmojiInputController</string> <!-- 入力メソッドのアイコンとして使う画像 --> <key>tsInputMethodIconFileKey</key> <string>InputMethodIcon.tiff</string> <!-- 入力ソース(入力ソースの詳細はよくわからん) --> <key>TISIntendedLanguage</key> <string>ja</string> <!-- 入力ソースに対する識別子 --> <key>TISInputSourceID</key> <string>jp.mzp.inputmethod.EmojiIM</string> <!-- 入力メソッドが対応している言語のリスト --> <key>tsInputMethodCharacterRepertoireKey</key> <array> <string>Hira</string> <string>Kana</string> <string>Latn</string> </array> </dict> </plist>
インストール
ビルドして生成された EmojiIM.app
を ~/Library/Input Methods
にコピーする。Workspace settingsからDerivedDataをworkspace relativeにしておくと楽。 こんな感じでコピーする。
cp -r DerivedData/EmojiIM/Build/Products/Debug/EmojiIM.app ~/Library/Input\\ Methods
System PreferencesのInput Sourcesから追加できる。うまく反映されない場合は、いったんログアウトするとよい。
🙋つらい時に挙げる札
こういうのを作った。元ツイートが消えているので出典が示せないが、 死にたいにゃんシールが一番近いと思う。
🛒材料
Seriaでそれっぽいのを買った。
- PPシート 不透明
- 家具用のアルミパイプ
✂️制作風景
TextEditで縦書きテキストの画像を作った。フォントはヒラギノ明朝が一番それっぽかった。
この画像を調整して紙に印刷した。
PPシートに貼って、札の形に切った。両面テープがあると思ったらなかったので、糊で貼りつけた。
アルミパイプをガムテープで貼り付けて完成。
🚨利用例
作ったはいいけど使い道がなかったのでオフィスに持っていったら、肥大化したCSSを修正するときに使われていた。 「我々のオフィスにはカンバンはあるがアンドンはなかった。それがこれにより補完される」とかいう話をしてた気もする。
つらいときにあげる札を上げながら仕事してる
— わかり亭めろたん。 (@renyamizuno_) 2017年8月2日
📝論文紹介: Software Development Waste(ソフトウェア開発におけるムダ)
ICSE 2017 勉強会のために論文を眺めてたら、リーンソフトウェア開発におけるムダについて調査した論文*1がおもしろそうだったので読んだ。
🐾背景: リーンソフトウェア開発
Womackはトヨタ生産方式を分析し、リーン思考を提案した*2。これは、「資源を消費し、価値を生まない活動」であるムダ(waste)の発見と除去を基本原則する。
リーンソフトウェア開発は、リーン思考とトヨタ生産方式をソフトウェア開発に適用した手法である。 リーンソフトウェア開発ではムダとして以下のものが挙げている。*3
トヨタ生産方式におけるムダ | リーンソフトウェア開発におけるムダ |
---|---|
在庫 | 未完成の作業 |
加工しすぎ | 再学習 |
作りすぎ | 使われないコードや余分な機能 |
物の運搬 | 作業の引き継ぎ |
手待ち | 開発の遅れ |
人の動作 | 作業の切り替え |
不良・手直し | 欠陥 |
価値 | なし |
活用されない才能 | なし |
しかし、ここでムダとされるものが妥当かを検証した研究はない。
🎯目的: ムダの発見
リーンソフトウェア開発には、どのような種類のムダが存在するかを調べる。
🔬調査方法: Pivotalにおける調査
Pivotalで行なわれたソフトウェア開発を対象とし、グラウンデッド・セオリーを構築した。
以下の3つの方法で、データを収集した。
- Pivotal Labのプロジェクトに参加し、観察した。 2年と5ヶ月間をかけて8個のプロジェクトに参加した。
- Pivotalの社員33人にインタビューを行なった。これにはソフトウェアエンジニア、インタラクションデザイナー、プロダクトマネージャが含まれる。
- レトロスペクティブ(ふりかえり)のトピックを分析した。 1年間の間に行なわれた91のミーティングを対象とした。
📊発見した9種類のムダ
調査の結果、9種類のムダを発見した。
誤った機能や製品の作成
誰も必要としない機能や製品を作ると、参加している全員の時間や労力が無駄になる。 これは、チームの士気やオーナーシップ、顧客満足度に影響する。
調査したプロジェクトにあった実例:
- ペルソナにもとづいて開発をしたが、その後の調査でペルソナがあやまっていることがわかった。
- マーケティングのために、ユーザの必要としない機能を追加した。
バックログ管理の失敗
プロダクトバックログの管理を失敗すると、プロジェクトの遅延や生産性の低下につながる。
調査したプロジェクトにあった実例:
- 一度に複数の作業を進めることを優先したため、最初のリリースでは一部の作業が未完了のままだった。その機能は無効化されてリリースされた。
- ビジネス側の要求に応じてユーザ登録プロセスを何度も変更したため遅延した。
- プロダクトマネージャーが変更をくり返したため遅延した。
作業のやり直し
作業の遣り直しがムダなのは自明である。 これは以下の原因で発生する。
- 技術負債。調査したプロジェクトの中には、リリース日を優先したため、リリース後に数週間にわたるやり直しが必要になったものがあった。
- 作業中における欠陥の発。 調査したプロジェクトの中には、いくつかの画面を作ったのちに、モバイル対応が必要であることが発覚したものがあった。
- ストーリーの拒否。実装が不十分のために、プロダクトマネージャーが受け入れを拒否することがある。
- 完了条件が不明瞭なストーリー。調査したプロジェクトの中には、開発者が実装を完了したのち、ストーリーとモックアップにインタラクションが不足していることが判明したものがあった。
不必要に複雑な解決策
機能が不必要に複雑だと、ユーザの時間を浪費する。
調査したプロジェクトにあった実例:
- 操作フローが、一部画面では左から右になっているのに、別の画面では上から下になっていた。
- 複数のインタラクションデザイナーがいるため、レイアウト、リスト、警告、ボタンなどが複数作られた。
- インタラクションデザイナーが2種類のフォームデザインを作ったために、複数のCSSが必要になった。
- 顧客側の開発者が「複雑であればあるほど、重要な仕事をしていると実感できるのでよい」という態度でいることに開発者が不満をもらした。
本質でない認知負荷
認知負荷理論(Cognitive Load Theory)は、人間の作業記憶には限りがあるため、負荷が多すぎると学習や問題解決が阻害されるとしている。本質的な認知負荷とは、タスクをこなす上で避けられないものを指す。 一方で本質的でない認知負荷とは周囲の環境などに追加されるものを指す。*4
ソフトウェア開発には認知負荷が高いもの多いため、開発者の思考力は貴重な資源である。そのため、本質でない認知負荷はムダであるとみなす。 これは以下の原因がある。
- 過度に複雑なストーリー。不必要に長く複雑で不明瞭なストーリーは開発者の作業記憶を消費してしまう。
- 非効率なツール。 機能不足だったり複雑なライブラリや貧弱な開発環境、貧弱な開発プロセスなども含む。
- 技術負債。 技術負債はコードの理解や変更を難しくする。 調査したプロジェクトの中には、テストスイートを実行すると大量の警告が出力され重要な情報が埋もれてしまうものがあった。
- マルチタスク。複数のタスクを同時にやろうとすると、作業記憶に多数の情報を保持する必要があるため、認知負荷が増加する。
精神的苦痛
精神的苦痛は貴重なリソースである開発者を消費してしまう。 精神的苦痛は生産性の低下や燃え尽き症候群や欠勤、健康問題などにつながる。
調査したプロジェクトの中には、機能とリリース日が変更不能なプロジェクトがあった。 このプロジェクトでは、チームの士気や一体感が低下したり、メンバー間の問題解決に時間がかかった。
待機/マルチタスク
優先度の高い機能に着手できない場合、開発者は待ったり、優先度の低い機能に着手してしまう。 これはプロジェクトの遅延につながる。
調査したプロジェクトにあった実例:
- 受け入れ環境が不安定だったため、プロダクトマネージャーが受け入れ作業を放置して別の作業を始めた。
- ビデオ会議設備が不足しており待ちが発生した。
- テストの実行に17分かかるため、開発者が別の作業をはじめてしまった。
知識の損失
知識の損失は、特定の知識をもったチーメメンバーがいなくなったときに発生する。 そして失なわれた知識を再獲得が必要になってしまう。
調査したプロジェクトの中には、全メンバーが入れ替わったプロジェクトがあった。 システムの理解に数ヶ月かかり、その間のベロシティはほぼ0になった。
非効率なコミュニケーション
非効率なコミュニケーションは不完全だったり誤解を産むコミュニケーションである。 チームの規模や非同期コミュニケーション、非対称なコミュニケーションが増えると、チームの生産性が低下する。
調査したプロジェクトにあった実例:
- 移動に一時間かかるオフィスにチームが分割された。 リモートコミュニケーションを活用したが、次第にレトロスペクティブで議題にのぼるようになっていった。
- ミーティングを特定の人が支配し、おとなしい人が意見を言えないようになった。
- iOSチームが自身のプロセスにチームの決定をうまく反映できなかった。レトロスペクティブを繰替えすことで改善した。
🔁既存のリーンソフトウェア開発におけるムダとの比較
リーンソフトウェア開発のムダとされているものとは、以下の違いがあった。
- 「引き継ぎのムダ」とされるものは観察できなかった。
- 新規で 「不必要に複雑な解決策」「本質でない認知負荷」「精神的苦痛」「非効率なコミュニケーション」の4つを観察した。
- それ以外は類似のものがあった。
まとめ
この論文ではソフトウェア開発におけるムダとその原因をエビデンスに基づいて示した。 そのために2年と5ヶ月をかけてデータを集め、グラウンデッド・セオリーを構築した。
その結果、既存のリーンソフトウェア開発におけるムダは支持されたが、いくつかの点で異なっていた。新規で4つ導入した一方、引き継ぎのムダは支持できなかった。
*1:Todd Sedano, Paul Ralph, and Cécile Péraire. 2017. Software development waste. In Proceedings of the 39th International Conference on Software Engineering (ICSE ‘17). IEEE Press, Piscataway, NJ, USA, 130-140. DOI: https://doi.org/10.1109/ICSE.2017.20
*2:J. P. Womack and D. T. Jones, Lean thinking: banish waste and create wealth in your corporation. Simon and Schuster, 1996.
*3:TABLE II: Comparison of Manufacturing Waste with Lean Software Development Wasteより引用
*4:http://miwalab.cog.human.nagoya-u.ac.jp/database/resume/2016-10-25.pdf を参考にした
🎤発表資料
勉強会の発表資料を作る際に気をつけていること。 最近だと📝The reason for using reason #ML_study - みずぴー日記を作った。
関係ない話は削る
発表で言いたいことがぼやけてしまうので、余分な話はしないようにしている。
自己紹介をいれると発表のテーマと違う話をすることになってしまうので、省略している。だいたいは「こんにちは、mzpです」くらいで済ませてる。
話の流れを整理するために事前に発表原稿を作っている。 何度か読み直して余分な話を削っている。 発表後は、このときの原稿をブログに貼り付けて、記事にしている。
導入は丁寧にする
「なぜこの技術が必要なのか」「なぜこの技術が登場したのか」といった動機の説明が不十分だと魅力がうまく伝わらないので、ここに時間を割く。
原稿や資料を作る際もここに時間をかけている。
「背景」→「困っていること/欠点」→「解決策」というストーリーにすることが多い。
後ろの席からも見やすいスライドにする
会場の形状によっては、前の席の人の頭でスライドの下のほうが隠れてしまうことがある。なので、文字を大きくしつつ、スライドの下のほうには空にするか補助情報だけを書くようにしている。
色を減らす
会場の明さやプロジェクターの性能によっては細かい色の区別がつけれないことがある。
なので、できるだけ使う色の種類は減らしている。地の文である黒と、強調したい部分を示す赤色系くらいにしている。
その他好み
凝ったテーマを使うことに気恥かしさを感じるので、KeynoteのWhiteテーマ(白い背景に黒い文字)を使っている。 タイトルの位置や文字のサイズなどは変更している。
明朝体が好きなのでスライドに使ってる。堅苦しい雰囲気がでてしまうが、みんなそこまで気にしないだろと思って、好みを優先させている。