ひだまりソケットは壊れない

ソフトウェア開発に関する話を書きます。 最近は主に Android アプリ、Windows アプリ (UWP アプリ)、Java 関係です。

まじめなことを書くつもりでやっています。 適当なことは 「一角獣は夜に啼く」 に書いています。

読んだ : エンジニアリング組織論への招待

広木大地さんの 『エンジニアリング組織論への招待』。 良い本だと話題になってましたね。 うちのチームでも皆で読んでます。

本書の 「はじめに」 には、以下のように書かれています。

本書は、「不確実性に向き合う」 というたった 1 つの原則から、エンジニアリング問題の解決方法を体系的に捉える組織論です。 わからないものを避ける本能を、どのように理解し、克服し、導くのか。 テクノロジーを力に変えたい経営者やエンジニアリーダー、そして、今、かつての私と同じように悩んでいる人へのチャレンジのきっかけとなればうれしいです。

エンジニアリングとは 「何か役に立つものを」 「実現していく」 ことであり、そこで重要なことが 「不確実性を小さくしていくこと」 である、というのが本書の根幹にある考え方です。 役に立つものを実現していく流れの中には、「役に立つものとは何か」、「どうやって実現するのか」、「いつ実現するのか」 といった不確実さが多くあります。

不確実性を大別すると未来に対する不確実性 (環境不確実性) と他人に対する不確実性 (通信不確実性) に分けられます。 それらへの対処として、認知と意思疎通、組織論やアジャイルといった考え方について説明されます。

認知の話や組織論の話、アジャイルなどについて様々な本が世の中には存在していますが、「エンジニアリング」 という観点で一冊にまとめあげられた書籍は本書しかないのではないでしょうか? エンジニアリング組織における組織論についての、ある種の教科書的な書籍であると感じました。 エンジニアリングについての組織論の入り口としてはオススメの一冊です。

個人的に気になったこと

本書を読んでいて自分が感じたことや新たに発見したことを紹介しておきます。

リアルオプション戦略 (遅延した意思決定)

早期に大きな投資をして大きなリスクを取るのではなく、最初は小さな投資を行って不確実性を小さくしてから (成功する見込みが大きくなってから) 大きな投資をする、というようなものがリアルオプション戦略。

システム思考

ロジックツリーを作るような単純な依存関係の要素分解をするのではなく、様々な要素が複雑な依存関係を持っているとみなして全体を捉えるのがシステム思考。 同僚に聞いたら 10 年前ぐらいに流行ったらしい。 下記の書籍をおすすめしてもらったので今度読んでみる。

世界はシステムで動く ―― いま起きていることの本質をつかむ考え方

世界はシステムで動く ―― いま起きていることの本質をつかむ考え方

ジョハリの窓

自己について、自己が認識しているかどうかと他者に認識されているかどうかの 2 軸で四象限に分けたもの。

  • 自己が認識しており、他者からも認識されているもの : 開放の窓
  • 自己は認識しているが、他者からは認識されていないもの : 秘密の窓
  • 自己が認識しておらず、他者からは認識されているもの : 盲点の窓
  • 自己が認識しておらず、他者からも認識されていないもの : 未知の窓

メンタリングにおいては、メンターとメンティ互いの開放の窓を大きくしていくことが、対人リスクを下げて生産的な関係を構築していくことにつながる。

能力は習慣の積分、習慣は行動の積分

能力や、それによって生み出される成果についてはコントロールできないが、行動やその積み重ねである習慣についてはコントロールしやすい。 という話。

プロダクトマネジメントとプロジェクトマネジメント

プロダクトマネジメントはマーケット不安を抱えて、目的不確実性を扱う。 プロジェクトマネジメントはスケジュール不安を抱えて、方法不確実性を扱う。 プロダクトマネジメントはプロダクトを続けること (価値を提供し続けること) が目的で、プロジェクトマネジメントはプロジェクトを完了させることが目的。

アジャイル

アジャイルというのは開発方式を表しているのではなく、状態を表す。 アジャイルな状態まさに自己組織化された状態で、下記のような状態を指す。

  • 情報の非対称性が小さい
  • 認知の歪みが少ない
  • チームより小さい限定合理性が働かない
  • 対人リスクを取れていて心理的安全性が高い
  • 課題・不安に向き合い不確実性の削減が効率よくできている
  • チーム全体のゴール認識レベルが高い

アジャイルに関してはこれまでもいろいろ読んできたけど、本書では歴史なども踏まえて説明してくれていて、特に思想的な部分がわかりやすくて良かった。

暗黙知形式知についての SECI モデルや、リフレーミングに関わるダブル・ループ学習の話なども (これまでも見たことがあったけど) 紹介されていた。

プリンシパル・エージェント理論やエージェンシースラック

経済における人間関係を、依頼者 (プリンシパル) と代理人 (エージェント) の契約の束としてとらえる考え方をプリンシパル・エージェント理論というらしい。

そして、代理人が依頼者に対して嘘をついた方が利益を得られる状況の場合に、その利益分をエージェンシースラックというらしい。 (例えば、代理人が実際よりも多くコストを算出して依頼者に請求する場合とか。) エージェンシースラックを解消するために依頼者が払う監視やインセンティブなどのコストを、コントロールコストという。 また、代理人側がエージェンシースラックを解消するために払うコストをシグナリングコストという。

こういうの、いたるところで発生するはずだから意識していきたい。

多点見積もり

工数見積もりにおいて、平均的な場合の工数だけでなく、最悪の場合の工数 (もうちょっとちゃんとした言葉で説明されてる) なども見積もって見積もりに幅を持たせる手法が紹介されてた。

確かに見積もりには幅があるはずだけど、普段チームで会話するときには見積もりの幅を意識してないなーと思った。

権限移譲

権限移譲のレベルについて、デリゲーションポーカーで測るという方法や、その後に権限の可視化をしっかりしておくことが大事。 (チームが自発的、自律的に動きやすいように。)

技術的負債

マーティン・ファウラーの 「技術的負債の四象限」 やフィリップ・クルーシュテンの四象限、クラウス・シュミットによる数学的定義が紹介されてた。

本書では 「技術的負債の問題とは、経営者とエンジニアの間に存在する 「認識の差」 の問題」 って書かれてて、確かにそういう部分が問題になることも多いと思うのだけど、結構エンジニア側のチーム内でも認識の差があることも多いよなぁ、ということを思ったりした。 (個人的には、チーム内で認識を統一できていれば経営者側への説明もしやすいのではないか、って気がしている。)

取引コストと技術組織

プロダクト開発を内部のリソースで行うべきか、外部のリソースで行うべきか、ということを考えるために取引コスト理論が紹介されていた。

経済取引には取引コストが発生し、それは次の 3 つに大別できる。

  • 探索のコスト : 取引相手を見つけるために支払うコスト
  • 交渉のコスト : 取引相手と交渉を行うために発生するコスト
  • 監督のコスト : 取引相手が契約した取引を履行するように監督と矯正を行うコスト

外注して取引を続けた場合、システムについてその外注先しか把握できない状態になり、ホールドアップ問題 (取引コストが上がる) が発生したりもする、とのこと。

あと、社内でも IT 部門を P/L 独立させた場合などは、部門間で取引コストがかかることになって、外注するのと変わらない状況が発生しうる、みたいな話も。 耳の痛い話だ。

関連書籍の紹介

上でも言ったように、本書では様々な内容を取り扱っているので、特定の内容について掘り下げたい場合は別の書籍を読むのも良いと思います。 また、本書の内容とはちょっと違っているが、関連する内容の書籍もいろいろあります。 自分が読んだことのある書籍をいくつか紹介します。

認知についてや、なぜ行動を起こせないのか、リフレーミングという点については、『なぜ人と組織は変われないのか』 が興味深かったです。

なぜ人と組織は変われないのか ― ハーバード流 自己変革の理論と実践

なぜ人と組織は変われないのか ― ハーバード流 自己変革の理論と実践

組織学習 (リフレーミングについても) については 『チームが機能するとはどういうことか』 も良かったです。

チームが機能するとはどういうことか――「学習力」と「実行力」を高める実践アプローチ

チームが機能するとはどういうことか――「学習力」と「実行力」を高める実践アプローチ

自己組織化については 『エラスティックリーダーシップ』、自律やメンタリングについては 『人を伸ばす力』 も良かったです。

エラスティックリーダーシップ ―自己組織化チームの育て方

エラスティックリーダーシップ ―自己組織化チームの育て方

人を伸ばす力―内発と自律のすすめ

人を伸ばす力―内発と自律のすすめ

社内でも部署間で取引コストが発生してしまうというような話については、『サイロ・エフェクト』 が参考事例として面白い書籍です。

サイロ・エフェクト 高度専門化社会の罠

サイロ・エフェクト 高度専門化社会の罠

Android アプリの Drawable リソースのエイリアスの作成について

当たり前の内容だけど日本語ドキュメントが間違っててバグを埋め込んでしまった (リリースはしてない) ので共有。

Drawable リソースのエイリアス作成

res/values/drawables.xml ファイルみたいなファイルを作って、そこに <drawable name="alias_name">@drawable/target_drawable</drawable> みたいなタグを書けばエイリアスを作れる。

To create an alias to an existing drawable, use the element. For example:

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <drawable name="icon">@drawable/icon_ca</drawable>
</resources>

If you save this file as drawables.xml (in an alternative resource directory, such as res/values-en-rCA/), it is compiled into a resource that you can reference as R.drawable.icon, but is actually an alias for the R.drawable.icon_ca resource (which is saved in res/drawable/).

App resources overview  |  Android Developers

qualifiers との兼ね合い

以下のように、qualifiers 付きディレクトリにある実体のある Drawable リソースとエイリアスとで、ちゃんと切り替わることを確認。

  • res/drawable-v23/foo.xml というリソースファイル
  • res/values/drawables.xml<drawable name="foo">@drawable/target_drawable</drawable>

日本語ドキュメントが間違ってる件

日本語ドキュメントだと、

既存のドローアブルのエイリアスを作成するには、<bitmap> 要素を使用します。次に例を示します。

って書かれてる。 が、実際にこれをやって (Bitmap Drawable 以外の?) Drawable リソースのエイリアスを作成しようとすると実行時にエラーが発生する。 (API level 16、23、25 のエミュレータで確認。)

ドキュメントの誤りについては https://issuetracker.google.com/issues/78862550 に報告した。

`bitmap` 要素を使ったときのエラー例
<?xml version="1.0" encoding="utf-8"?>
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
    android:src="@drawable/simple_drawable" />

上のような XML ファイルを用意して、Drawable として読み込もうとすると以下のようなエラー。

    java.lang.RuntimeException: Unable to start activity ComponentInfo{info.vividcode.sample.simple/info.vividcode.sample.simple.MainActivity}: android.view.InflateException: Binary XML file line #31: Binary XML file line #31: Error inflating class ImageView
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2416)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2476)
        (中略)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
     Caused by: android.view.InflateException: Binary XML file line #31: Binary XML file line #31: Error inflating class ImageView
        at android.view.LayoutInflater.inflate(LayoutInflater.java:539)
        at android.view.LayoutInflater.inflate(LayoutInflater.java:423)
        (中略)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) 
     Caused by: android.view.InflateException: Binary XML file line #31: Error inflating class ImageView
        at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:782)
        at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:704)
        (中略)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) 
     Caused by: android.content.res.Resources$NotFoundException: File res/drawable/simple_drawable_3.xml from drawable resource ID #0x7f02005f
        at android.content.res.Resources.loadDrawableForCookie(Resources.java:2640)
        at android.content.res.Resources.loadDrawable(Resources.java:2540)
        at android.content.res.TypedArray.getDrawable(TypedArray.java:870)
        at android.widget.ImageView.<init>(ImageView.java:152)
        (中略)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) 
     Caused by: org.xmlpull.v1.XmlPullParserException: Binary XML file line #2: <bitmap> requires a valid 'src' attribute
        (以下略)

Android における View にまつわる状態とライフサイクルについての考慮のメモ

Activity が再生成される際の、View の onSaveInstanceState メソッドと onRestoreInstanceState メソッドについて個人用にまとめておく。

Activity の再生成

  • Activity が再生成 (re-creation) される際には、以前の状態が Activity#onCreateActivity#onRestoreInstanceState に渡される。
  • Activity#onRestoreInstanceState が呼ばれるのは、Activity#onStart が呼ばれた後。
    • 『This method is called after onStart() when the activity is being re-initialized from a previously saved state, given here in savedInstanceState.』 (Activity#onRestoreInstanceState より)
  • Activity#onRestoreInstanceState が呼ばれるのは、Activity 再生成後の最初の Activity#onStart が呼ばれた後の 1 回だけ。 (Android 8.0 の端末で実際の挙動を確認。)

View 周り

  • View にも View#onRestoreInstanceState メソッドView#onSaveInstanceState メソッドがあるが、これらはどこから呼ばれるのか?
  • Activity#onRestoreInstanceState メソッドや Activity#onSaveInstanceState メソッドのデフォルト実装による。
    • 『The default implementation of this method performs a restore of any view state that had previously been frozen by onSaveInstanceState(Bundle).』
  • Window あたりがルートビューの View#saveHierarchyStateView#restoreHierarchyState などを呼んで、そっから View#dispatchSaveInstanceStateView#dispatchRestoreInstanceState やらが呼ばれて、最後に View#onSaveInstanceStateView#onRestoreInsntaceState やらにたどり着く。
    • android:saveEnabled の考慮は View#dispatchSaveInstanceState で行われる。
    • 子 View の View#dispatchSaveInstanceState を呼ぶのは ViewGroup#dispatchSaveInstanceState が行っている。
    • ここら辺は実際のコードをみて確認した。 API Level 27。

View にまつわる状態とライフサイクルについての考慮 (?)

  • View Model やら Presenter みたいな、いわゆる Presentation Model 的なものを作っていると、Activity の再生成時の View の状態 (View#onRestoreInstanceState で復元されるやつ) と、Presentation Model 側の状態の同期をどうとるかで悩むことになりがち。
  • 道はいくつもある
    • カスタム View を作って、その内部に状態を持たせる。 状態の保存はカスタム View の View#onSaveInstanceState に任せる。
    • View の状態管理は完全にアプリケーション側の責務ということにして、Android フレームワークの力には頼らない。 View には android:saveEnabled="false" を設定する。
    • Activity 再生成時の状態復元は View だけでやって、Presentation Model 側では View 側の状態復元をフックにして状態を変える。
      • 具体的に言うと、例えば EditText の入力内容に対応して Presentation Model 側で状態を保持する設計を考える。 Activity 再生成時の初期状態では EditText も Presentation Model も入力内容がない場合の状態になってて、onRestoreInstanceStateEditText の状態が復元される際に TextWatcher でイベントを捕捉して Presentation Model 側の状態も変える、みたいな。
  • Android フレームワークの挙動をちゃんと理解しておけばなんとでもなるのだけど、ちゃんとりかいしておかないと変なところでハマる。
  • 個人的には Activity と View を疎に保ちたいのだけど、Android フレームワーク的には結構べったりくっついてるので辛い。

読んだ : さわって学ぶクラウドインフラ Amazon Web Services 基礎からのネットワーク & サーバー構築

Amazon Web Services 基礎からのネットワーク&サーバー構築 改訂版

Amazon Web Services 基礎からのネットワーク&サーバー構築 改訂版

インフラ周りちゃんと学ぶぞー! と言いつつ時間ばかり経ってしまっているので、いい加減ちゃんとやろうと思って本書を読んだ。

易しめの内容でわかりやすく書かれているので、「インフラ周りをちゃんと学びたいと思いつつも苦手意識を持っている」 という人 *1 や、「SSH コマンドなどもろくに使ったことがないけどインフラ周りを学んでみたい」 という人におすすめの書籍だった。

感想

  • AWSWordPress を動かす」 ために必要な内容を順番に説明していくという、わかりやすい内容だった。
  • AWS だけでなくネットワーク周り (TCP / IP とか) についても初心者向けの内容になっており、インフラ周りについてほぼ何も知らない状態でもとっつきやすい。
  • AWS についても リージョンアベイラビリティゾーン (AZ) といったことから説明がある。
  • そして、Amazon Virtual Private Cloud (VPC)Amazon Elastic Compute Cloud (EC2) といった、AWS 上で web サービスを運用する場合によく使われるであろうサービスについての知識が得られる。
  • わかっている人にとっては簡単すぎる内容だと思うが、初心者にとってはこれぐらい簡単な方がとっつきやすくてありがたい。
    • 実際に本書の内容に従って手を動かしていくと、だいぶ理解が進むと思う。
    • 自分は仕事でも AWS を使っているし、同僚の勉強会で Terraform を触りながら AWS の概念を学ぶ、ということもやってきたが、改めて本書の内容に従って学びなおすことでだいぶ定着したと思う。 (やはり一から自分で手を動かすというのが大きい。)
  • お金のことを考えるとなかなか個人で AWS を触ろうという気にならなかったりするのだけど、本書の内容は AWS のアカウント作成後に付与される無料枠の中でできることばかりなので、気軽に試していけるのも良い。

本書の内容

9 章構成になっている。

  • 1 章 : ネットワークの基礎的なところから AWS の基礎的な部分まで解説される。 また、本書で構成するシステムの全体像も描かれる。
    • 一般的な web アプリケーションに必要なアプリケーションサーバーと DB サーバーが、それぞれパブリックサブネットとプライベートサブネットに配置されているようなネットワーク。
  • 2 章 : ネットワークを作る。
    • VPC を用意し、パブリックサブネットを作る。
  • 3 章 : サーバー構築。 EC2 インスタンス起動。
  • 4 章 : Web サーバーソフトをインストール。 Apache
  • 5 章 : HTTP の動きの確認。
  • 6 章 : プライベートサブネットを作る。
  • 7 章 : NAT を構築。
  • 8 章 : DB を用いたブログシステムの作成。
  • 9 章 : TCP/IP による通信の説明。

上記のような感じで、手順としても SSH コマンドなどについても丁寧に説明しているので、本当に初心者にもわかりやすいと思う。

さらに学ぶために

上で何度か言ったように、本書は初心者にもわかりやすいような内容になっている。 逆に、自動化などの応用的な話はあまりないので、さらに学びたい人は本書の内容を Terraform で書きながら読み進める、というようなことをしてもいいかもしれない。

私は本書を読み終わった後に Terraform の Getting Started を読んでいく、という感じで学んでいった。

*1:私です

Java (JVM 言語) におけるコードカバレッジの計測方法

Java と Kotlin で書かれたアプリケーションのコードテストのカバレッジを取りたいなーと思って、コードカバレッジ計測ツールについて調べてみてる。

JVM 言語におけるコードカバレッジ計測の方法

JaCoCo のドキュメントにわかりやすくまとまっている。

Coverage information has to be collected at runtime. For this purpose JaCoCo creates instrumented versions of the original class definitions. The instrumentation process happens on-the-fly during class loading using so called Java agents.

There are several different approaches to collect coverage information. For each approach different implementation techniques are known. The following diagram gives an overview with the techniques used by JaCoCo highlighted:

f:id:nobuoka:20180228230512p:plain

JaCoCo - Implementation Design

カバレッジ情報は実行時に収集されるものなので、情報収集のための仕組みが必要。

大きく分けると Runtime Profiling と Instrumentation の 2 つの仕組みがある。 前者は JVM の仕組み (JVMTI や JVMPI) を使うもの。 後者は実行対象のプログラムの方に収集用の仕組みを搭載する (これを instrumentation というらしい?) もの。

Runtime Profiling

JVM TI は Java Virtual Machine Tool Interface の略。 Java SE 5 で導入された。

JVM TI は JVM 上で動くアプリケーションの状態を検査したり、実行を制御したりするためのプログラミングインターフェイスらしい。

JVMPI は JVM TI よりも古くからある同じようなインターフェイスらしい。 Java SE 5 で (JVM TI ができたことで) 非推奨になり、Java SE 6 で廃止された模様。

これらを使ったコードカバレッジは、アプリケーション側に何も手を入れなくて良いことが利点だと思われる。 が、詳細はわからない。

Instrumentation

上でも説明したように、計測対象のアプリケーションコードに情報収集のための仕組みを搭載する方法が、この Instrumentation である。 Runtime Profiling と異なり、Android アプリの実行環境のような非 JVM 環境でも使用できるという利点がありそう *1

上の図を見るとわかるように、様々な方法がある。

  • ソースコードを変更するもの。
  • バイトコードを変更するもの。
    • オフライン (offline) で変更するもの。 (= JVM に読み込まれる前の状態、例えばクラスファイルそのものを変更する。)
      • Replace と Inject って書かれてるけどそれぞれの意味はわからない。
    • オンザフライ (on-the-fly) で変更するもの。 (= JVM 読み込まれる際などにオンメモリで変更する。)
      • クラスローダで変更する方法と、Java Agent を用いる方法がある。

JaCoCo は、Java Agent を用いてオンザフライでバイトコードを変更する方式である。 *2

Java Agent について

Java Agent については java.lang.instrument パッケージの Javadoc に書かれている。

-javaagent:[=] というコマンドラインオプションで指定して使用できるものである。 次のページも参考になる。

エージェントの背景にある基本概念は、「JVM がクラスをロードする場合、エージェントはそのクラスのバイトコードを修正できる」 という考え方です。

それぞれの特徴

現時点で自分がわかっている範囲で特徴を書いておく。

  • ソースコードを変更する方式は、言語によって使用できるかどうかが変わる。 例えば Java 言語に対応しているツールでも Kotlin には対応していなかったりする。
    • 一方でバイトコードを変更する方式は、JVM 言語であればどれにでも対応できるはず。
  • オフラインで変更する方式は、ビルド時にクラスファイルが書き換えられてしまうので、それをそのまま本番アーティファクトのビルド時に使用できない。 (使用したらダメというわけではないが、パフォーマンスが落ちるなどの問題が起こる。)

様々なコードカバレッジツール

JVM 言語用の様々なコードカバレッジツールについて、Clover のブログで比較紹介してくれている。 (最終更新が 2017 年春なので情報はちょっと古いかもしれない。)

有名どころとしては JaCoCoOpenClover (Atlassian Clover がオープンソース化されたもの)、JCov といったところだと思う。

Instrumentation 方式に着目すると、JaCoCo と JCov はオフラインおよびオンザフライのバイトコード instrumentation に対応しており、Clover はソースファイル instrumentation に対応している。 なので JaCoCo や JCov は (JVM 言語なら何でも対応できるので) Kotlin にも対応するが、Clover は (対応言語に入っていない) Kotlin には対応しない。

Clover と JaCoCo を軽く使ってみたところ、設定の簡単さはどちらも同じ。 出力される HTML を見ると JaCoCo は単純な内容で、Clover の方はプロジェクトリスクの高いものを表示したり、視覚的だったりと、結果表示については高機能さを感じた。

今回は Kotlin でのカバレッジも取りたいので、(対応していない) Clover は選外で、JaCoCo か JCov のどっちかを使うことになりそう。

*1:憶測です

*2:オプションでオフラインでの instrumentation も可能。