DomainModelとDTOが相互に変換可能であることを要求するTraitが作りたかったんだ

業務の中で思いついた便利 Trait を供養します。どこかで使えそうで、いやしかしどこにも使えそうにない Trait です。もしかしたらライブラリにありそうだけど標準ライブラリにはないはず。たぶん。

経緯

いま業務で関わっているアプリケーションでは DomainModel をリポジトリに渡して DB に保存しています。その DomainModel を DB には JSON 形式で保存する要件がありました。DomainModel を JSON に変換するということは、DomainModel に serde::Deserializeserde::Serialize を derive することになりますが、DomainModel は DB での保存のされ方を意識したくありません。そこで、必ず DomainModel と一対一に対応する DTO が欲しくなります。

相互に変換可能であることを要求するTrait

さっそくですが、Trait の定義は下記のようになりました。

trait BiConvertible<T>
where
    Self: Sized,
    T: BiConvertible<Self>,
{
    fn convert(self) -> T;
}

この Trait は型変数 T を受け取り、convert メソッドの戻り値になります。この T は BiConvertible を実装している必要があります。つまり、T の convert メソッドを実行すると Self に変換されます。

例えば、2つの型のうち型方の型のみに実装を行うとコンパイルエラーになります。

struct A;
struct B;

impl BiConvertible<B> for A {
    fn convert(self) -> B {
        B {}
    }
}
error[E0277]: the trait bound `B: BiConvertible<A>` is not satisfied
  --> src/main.rs:12:6
   |
12 | impl BiConvertible<B> for A {
   |      ^^^^^^^^^^^^^^^^ the trait `BiConvertible<A>` is not implemented for `B`
   |
   = help: the trait `BiConvertible<B>` is implemented for `A`
note: required by a bound in `BiConvertible`
  --> src/main.rs:4:8
   |
1  | trait BiConvertible<T>
   |       ------------- required by a bound in this trait
...
4  |     T: BiConvertible<Self>,
   |        ^^^^^^^^^^^^^^^^^^^ required by this bound in `BiConvertible`

ちなみに自身に変換する場合はエラーが発生しません。2つの異なる型が相互に変換可能であることをコンパイル時に検知したいというコンセプトからは違反しますが考えないことにします。(となると BiConvertible の命名が微妙なわけだけど結局採用はしなかったので、命名についても同様に考えないこととする)

impl BiConvertible<A> for A {
    fn convert(self) -> A {
        self
    }
}

DomainModelとDTOが相互に変換可能であることを要求するTrait

さて、本題の DomainModel と DTO の相互変換についてです。下記の Trait はコンパイルが通ります。

trait DomainModel<T>
where
    Self: Sized + BiConvertible<T>,
    T: Dto<Self>,
{
    fn to_dto(self) -> T {
        self.convert()
    }
}
trait Dto<T>
where
    Self: Sized + BiConvertible<T>,
    T: DomainModel<Self>,
{
    fn to_domain(self) -> T {
        self.convert()
    }
}

impl DomainModel<B> for A {}
impl Dto<A> for B {}

しかしこの実装では DomainModel Trait を実装する時に DTO の具体的な名前が出てきてしまいます。DTO が変換先の DomainModel の型を知っていることは問題ないですが、DomainModel はそれ自身が誰に変換されてどう使われるのかを関知する必要はなく、むしろこの形だと変換先/利用先の知識が Domain 層を侵食しています。

型変数にするから型が現れてしまうわけなので、制約を書く場所を変えてみました。

trait DomainModel: Sized {
    type T: Dto + BiConvertible<Self>
    where
        Self: BiConvertible<Self::T>;

    fn to_dto(self) -> Self::T
    where
        Self: BiConvertible<Self::T>,
    {
        self.convert()
    }
}

trait Dto: Sized {
    type T: DomainModel + BiConvertible<Self>
    where
        Self: BiConvertible<Self::T>;

    fn to_domain(self) -> Self::T
    where
        Self: BiConvertible<Self::T>,
    {
        self.convert()
    }
}

変換先を Associated Type にしました。Self が BiConvertible であるという制約は Associated Type に行っています。メソッドにも制約が加えられているのは Self への BiConvertible が where で足されているため、メソッドを呼び出す Self が BiConvertible を満たしていることがわからないためです。('not satisfy'というエラーログで出たので制約が満たせていないことはわかりましたが、where 周りの細かい理解が足りていないことがわかった)

しかし、これらの Trait を実装すると、コンパイルエラーになってしまいます。

impl DomainModel for A {
    type T = B;
}
impl Dto for B {
    type T = A;
}
error[E0275]: overflow evaluating the requirement `<A as DomainModel>::T == B`
  --> src/main.rs:50:14
   |
50 |     type T = B;
   |              ^

制約が再帰的になっているぞ、とのことです。

E0275 - Error codes index

むすび

他にも制約をつける場所を変えることはできますが、その場合は convert メソッドが呼び出せなくなってしまったり、BiConvertible が実装不要になってしまうなどの問題があり解決には至りませんでした。集合論や型パズルに自信ニキなら上手く作れそうな気もしますが時間切れ。実務に採用することはありませんでした。

こういう妙な型は納期とのトレードオフで破壊されがちなので、コメント含むドキュメンテーションを通じてドメイン知識を共有できる体制づくりをした方が長期で万人に効くと思う。とはいえ、本当に厳しい納期の前にはドキュメンテーションや体制づくりも脆弱。やはり型なのか。そうなのか。

Viteで作ったWebアプリをClaspでGASにデプロイするまで

Web アプリケーションを無料でホストする必要が生まれた。非プログラマが運用できる且つ Google Drive に保存された画像を参照することが要件であるためGASを使うことにした。ここにはメモも兼ねてその手順を残しておく。

Setup

volta install node
volta install yarn@3.5.0
volta install vite
yarn create vite
➤ YN0000: ┌ Resolution step
➤ YN0000: └ Completed
➤ YN0000: ┌ Fetch step
➤ YN0000: └ Completed
➤ YN0000: ┌ Link step
➤ YN0000: │ ESM support for PnP uses the experimental loader API and is therefore experimental
➤ YN0000: └ Completed
➤ YN0000: Done with warnings in 0s 113ms

✔ Project name: … gas
✔ Select a framework: › React
✔ Select a variant: › TypeScript + SWC

Scaffolding project in /Users/***/work/tmp/gas...

Done. Now run:

  cd gas
  yarn
  yarn dev

vite を使ってプロジェクトを作成する。VSCode で開いてみると下記のようにエラーが多く表示される。

PnP

これは yarn PnP という機能によるものである。node_modules 配下にライブラリを展開せず仮想的にロードするというものらしい。

yarnpkg.com

www.wantedly.com

qiita.com

VSCode はデフォルトで node_modules を参照してしまうのでこのようなエラーが発生する。公式ドキュメントを読むと sdk を入れる方法が記載されていた。

yarnpkg.com

yarn add -D @yarnpkg/sdks
yarn sdks vscode

実際にsdk入れてみるとエラーは解消された。

Clasp

GAS にデプロイする上では下記の記事が非常に参考になった。現在でも記載されたとおりに進めれば何も問題なくデプロイすることができた。

engineer.retty.me

記事中に記載のコマンドを自分の環境では一部変更した。

  • npm i vite-plugin-singlefile -> yarn add -D vite-plugin-singlefile
  • npm i -g @google/clasp -> yarn add -D @google/clasp
    • これにより以降 claspyarn clasp と読み換えて実行する

Google Drive の画像を参照する

Google Drive に保存されている画像を開き、「新しいウィンドウで開く」とすると https://drive.google.com/file/d/${id}/view というURLで画像が開かれる。このURLを https://drive.google.com/uc?export=view&id=${id} と変更して画像を読み込むように実装を変更する。

簡単な社内ツールならこれですぐ作れちゃいそう。

Tailscaleで自宅の外からVSCodeに繋ぐ

先月にPCを自作したけれど、もちろんラップトップではなくデスクトップなので持ち運びができない。せっかくお金をかけて作ったPCなので、自宅の外だろうがハイスペPCを使いたい。

prelude.hatenablog.jp

prelude.hatenablog.jp

Tailscale

自宅PCに繋げるために話題のTailscaleを選んだ。VPNはいまいち詳しくないので同僚から評判という理由だけで選んだ。

tailscale.com

Tailscaleとは、

Tailscale is a VPN service that makes the devices and applications you own accessible anywhere in the world, securely and effortlessly. It enables encrypted point-to-point connections using the open source WireGuard protocol, which means only devices on your private network can communicate with each other.

というものである。詳細は下記。

What is Tailscale? · Tailscale

個人利用であれば無料で使える点も良い。

インストール

Tailscaleを使うためには接続先と接続元のそれぞれにtailscaleをインストールする必要がある。

Install Tailscale · Tailscale

yay -S tailscale # Manjaro
brew install tailscale # Mac

セットアップ

tailscaleを起動するとブラウザ上でセットアップツアーが行われるのでそれに従えば良い。tailscaleを起動してpingの確認まで行うので完遂すれば接続できている。 セットアップ時にはIPアドレスを使ってpingをするが、もちろんHost名でも接続ができる。tailscale statusを実行するとターミナル上からも確認できる。tailscale ping $host_nameとすればpingもできるしコマンド変えればsshもできる。

VSCodeの設定

まずcode-serverの設定を行う。設定方法は下記に記載されている。iPadって書いてあるけど別に関係ないのでそのまま設定すればよし。

Coding on iPad using VSCode, Caddy, and code-server · Tailscale

tailscaleのインストールはされているのでcode-serverのインストールから行う。code-serverもyayで取得できるのでyay -S code-serverでOK。 設定ファイルが~/.config/code-server/config.yamlに置かれるので、そこをリンク先の通りに変更してあげる。

接続元の端末のブラウザから$サーバーIP:$config.yamlで設定したport番号に接続するとVSCodeが起動する。

Setup Manjaro Linux 続き - GPU購入

初めてPCを自作した。ゲームや画像編集などをするわけではないのでGPUは買わずにIntel CPUの内蔵GPUで十分だろう。そう思っていた時期は下記のとおり。

prelude.hatenablog.jp

GPUの購入

実際に使ってみるとマルチモニタの片方で動画を見ながらもう片方でブラウザを見るだけでもカクついてしまった。さすがにストレスなのでGPUを購入した。

現物。

挿すとこう。

GPUの起動

事前準備としてArchWikiにあるNVIDIAのページを読んだ。正直読んでもさっぱりわからなかったが、わからないなりに雰囲気は掴むことができた。

NVIDIA - ArchWiki

Manjaroではmhwdというコマンドで自動でGPUを検出して対応するdriverをインストールすることができる。コマンドは公式のwikiに記載されている通りである。

Configure Graphics Cards - Manjaro

実は内蔵GPUがあると上記のコマンドを実行するだけでは上手くいかないようだ。実際に上記のコマンドを実行して再起動したらディスプレイマネジャーが起動しなくなったのかわからないが全くエラーが表示されて操作できなくなってしまった。画面の写真は焦って撮り忘れた。 調べていくと前述の通り内蔵GPUが悪さしていることがわかり、対処するにはibtをoffにする必要があるとのことだった。

Asus Prime Z690 + i9 12900K not boot - #2 by brahma - Graphics & Display - Manjaro Linux Forum

このibtとはindirect branch trackingの略である。jump命令の悪用防止のための機構だが、offにしてもひとまず大きな問題はないらしい。(ほんとか?)

Indirect Branch Tracking - 004 - ID:655258 | Core™ Processors

GPUとモニターを繋いだら無事GPUを使って画面が表示された。

Tool

nvtopというVideoCard用のtopコマンドがあるのでこれをインストールする。

GitHub - Syllo/nvtop: GPUs process monitoring for AMD, Intel and NVIDIA

こんな感じでGPUが使われていることがわかる。

ちなみにintelの内蔵GPUが使用されているかどうかはintel_gpu_topというツールでわかる。

Setup Manjaro Linux

Mac OSでの開発がしんどくなってきたのでLinux PCを自作した。初めての自作で学びが多かったので見返せるように残しておく。試行錯誤を整理するのが面倒だったのでメモ程度に。

PCの組み立て

CPU

自作のモチベーションはPCスペックの向上なのでIntel Core i9-13900Kを購入した。

インテル® Core™ i9-13900K プロセッサー

マザーボード

YouTubeで13900Kの組み立て動画を見ながら探した結果、Z790チップセット対応のROG STRIX Z790-F GAMING WIFIを購入した。

ROG STRIX Z790-F GAMING WIFI | ROG STRIX Z790-F GAMING WIFI | Gaming マザーボード|ROG - Republic of Gamers|ROG 日本

メモリ

Z790チップセットはDDR5に対応している。最終的には下記のメモリを購入した。DIMMを4つ差せるので、32GB×4で合計128GBとなった

インテル® Z790 チップセットの概要 F5-6000J3238G32GX2-RS5K F5-6000J3238G32GA2-RS5K - G.SKILL International Enterprise Co., Ltd.

SSD

Z790チップセットはPICe 4.0のレーンがあり、上記のマザボにはそのM.2スロットがある。動画や画像などを大量にローカルに保存する予定はないので下記の1TBのものを購入した。

Crucial P3 Plus Gen4 NVMe™ SSD | Crucial JP

CPUクーラー

ゲームをしないのでそこまで長時間熱くならないだろうという読みで空冷にした。取り付けに不安があったのでYouTubeの組み立て動画で見たものと同じものを購入した。

AK620 - DeepCool

電源

ブランドは割とどこでも良さそうだったので、聞いたことのある玄人志向にした。電源容量は上記のパーツから計算しつつ、今後のパーツ交換を見越して少し大きめのものを購入した。

80PLUS GOLD取得 ATX電源 650W(プラグインタイプ) | 玄人志向

ケース

今回購入したCPUは13900Kで内蔵GPUがある。大きさはミドルタワーサイズにした。もし動作に不満がありグラボも欲しいと思った場合にケースに入らなかったら悲しいからだ。

Define R5 — Fractal Design

組み立て

購入したマザボが初心者向けということもあり非常に組み立ては楽だった。とはいえ、YouTubeにある組み立て動画事前にいくつか見てイメージを掴んでから作業に取り掛かった方が良いと思った。クーラーの向きを間違えたり、マザボのケースへの取り付け周りがまごついてしまった。

費用

購入当時の金額は下記。合計で¥313,100だった。

パーツ 金額 Link
CPU ¥88,345 https://www.dospara.co.jp/SBR1329/IC482985.html
Mother Board ¥69,677 https://www.amazon.co.jp/dp/B0BJZ9VP4V
Memory ¥109,600 https://www.dospara.co.jp/SBR1534/IC482500.html
Storage ¥10,947 https://www.amazon.co.jp/dp/B0BCNQJV9C
Cooler ¥8,061 https://www.amazon.co.jp/dp/B09HRK311X
Power ¥10,490 https://www.amazon.co.jp/dp/B0778D2FFK
Case ¥15,980 https://www.amazon.co.jp/dp/B00Q2Z11QE

OSの起動

周りにArch系OSのユーザーが数名いるためManjaroを選択した。リッチなUIが良かったのでデスクトップ環境はGNOMEにした。

Manjaro

OSをインストールするためにLive USBを作成する。下記はMacでの手順となる。 まずはdiskutilコマンドで対象のUSBドライブを確認。

$ diskutil list

作成するUSBのパスが特定できたらunmountする。

$ diskutil unmountDisk /dev/${対象のdisk}

最後にManjaroのisoファイルをコピーする。

$ dd if=path/to/manjaro-version-x86_64.iso of=/dev/r${対象のdisk} bs=1m

詳細はここ。

USB flash installation medium - ArchWiki

Live USBを挿したまま起動。BIOSが起動するので挿したUSBを選択してLinuxを起動させる。

設定

起動するとManjaro Helloというセットアップアプリケーションが起動する。ここで言語やタイムゾーンの設定がGUIで行える。しかし、日本語設定でfcitx4系がインストールされてしまうのでチェックを入れないようにする。(fcitx5のインストールは後述)

パッケージの更新元ミラーサーバーを日本にする。

$ sudo pacman-mirrors -c Japan

yayを入れてパッケージの更新を行う。

$ sudo pacman -Sy yay
$ yay

Arch Linux JP Project - base-devel (x86_64)

必須パッケージをインストールする。

$ yay -S base-devel

Display

MacではType-Cへの変換アダプタを使って2つのモニターとHDMIとそれぞれ接続していた。自作PCではHDMIポートとDisplayPortが1つずつあるので、アダプタ使わず直接繋いでマルチモニタになった。 情弱なのでDisplayPortの存在を初めてしったので、気づくまではUSBに変換して接続を試みていた。しかしUSB変換で必要なDisplayLinkはバグがあるようで全く動作しなかった。

DisplayLink - ArchWiki

モニタの管理はxrandrをラップしているarandrを使うと良いとあった。しかしこちらも動作せず、、、。

ARandR: Another XRandR GUI

色々見てたらSettingアプリから変更できた。ありがとうManjaro。

Audio

オーディオ設定は苦戦した。調べているとpulseaudioを使う記事が多かったが、最近はpipewireを使うほうが良いらしい。

PulseAudio, PulseAudio - ArchWiki

PipeWire, PipeWire - ArchWiki

Manjaroの場合はmanjaro-pipewireというパッケージがあるのでそれを入れてしまえば楽とのこと。詳しくはわからん。もうほんとわからん。

$ yay -S manjaro-pipewire

音声の確認や設定はpavucontrolを使う。

$ yay -S pavucontorl

PulseAudio Volume Control 5.0

出力確認

HDMIに接続したモニタからは音声が出力された。しかし、ヘッドホンジャックにケーブルを挿してみたが音が聞こえなかった。ヘッドホンは認識されており、pavucontrolでは音声が出力されていたので皆目検討つかず。 試しにヘッドホンジャックではなくline-outに繋いでみたらヘッドホンから音声が聞こえた。ヘッドホンジャックとline-outの違いがわかっていないので調べてみたが、やっぱりわからなかった。Audioにいまいち興味が出ない。

Line Out vs Headphone Out: What's the Difference? (Solved!)

有線でダメなら無線で、とbluetoothを試してみた。しかし接続がかなり不安定ですぐ切れてしまう。同僚に聞いてみると、creativeのトランスミッターを差してPC本体と直接接続していないとのことだった。 すぐ購入して試してみたがヘッドホンから音声を聞くことができた。マイクから入力できなかったので引き続き調べた方が良さそう。もしダメなら別途マイクを購入して、入出力のデバイスを変えることも検討が必要そう。

KyeBoard

IME

現在fcitx4系はメンテナンスモードに入っている。

GitHub - fcitx/fcitx: A Flexible Input Method Framework

これを知らずにHello Manjaro経由でインストールしてしまい、後になってfcitx5をインストールした。fcitx4系を削除後、fcitx5は下記のコマンドでインストールする。

$ yay -S fcitx5 fcitx5-configtool fcitx5-mozc manjaro-asian-input-support-fcitx5

もしかしたらfcitx5-mozcが入っていればmanjaro-asian-input-support-fcitx5は不要かもしれない。

IMEの切り替えはCtrl+Spaceだが、fcitx5の設定からIMEのActivate Input Method / Deactivate Input Methodのショートカットを設定しておくとtoggleにならないので良い。

Keymap

Macの時はCtrl+hjklでカーソル移動、マウスのミドルボタンでMission Controlとしていた。これと同様のことをしたい。結論から書くとカーソル移動はどうしてもできなかった。 まず試したことはGUIのremapperだ。ArchWikiを見て良さそうだったInput Remapperを選んだ。

Input remap utilities - ArchWiki

このツールによってミドルボタンをSuper_Lに割り当てることでMission Controlと同様のことが実現できた。しかし、Ctrl_L+hjklを上下左右にそれぞれ割り当てたが、Ctrlが入力されたままになってしまい上下移動はできず、左右移動はワード移動になってしまった。

続いて、xmodmapコマンドによってremapを試みた。

xmodmap - ArchWiki

まずはkeycodeとkeysymの確認。手を加える場合は出力結果を.Xmodmap.defaultなどとして保存し、いつでも元に戻せるようにしておくと良い。

$ xmodmap -pke

.Xmodmapというファイルを作成し、下記のように記述し読み込ませる。

keycode  37 = Mode_switch
keycode  43 = h H Left
keycode  44 = j J Down
keycode  45 = k K Up
keycode  46 = l L Right
$ xmodmap .Xmodmap

ちなみにManjaroだと.xinitrcに下記のように記述されているため、xmodmapコマンドによって毎回読み込ませなくても良い。

usermodmap=$HOME/.Xmodmap
~~~
if [ -f "$usermodmap" ]; then
    xmodmap "$usermodmap"
fi

設定内容を読み込ませたらキーを叩いて意図通りのイベントが発生するか確認する。そのためには下記のツールをインストールする。

$ yay -S xorg-xev

xevコマンドを実行すると入力を受けつけ、イベント内容を標準出力に書き出してくれる。実際にControl_Lを叩いてみるとControl_LではなくMode_switchが表示された。たしかに変更できている。しかし、前述の通りCtrl+hjklはControl_Lが入力されてしまった。

続いて、xkbコマンドによる変更を試みた。下記のように記述した~/.xkb/symbols/customというファイルを作成しsetxkbmapコマンドで設定を読み込ませた。しかしこれもダメだった。

X keyboard extension - ArchWiki

xkb_symbols "custom" {
  key <LCTL> { [ Mode_switch ] };
  key <AC06> { [ h, H, Left ] };
  key <AC07> { [ j, J, Down ] };
  key <AC08> { [ k, K, Up ] };
  key <AC09> { [ l, L, Right ] };
} 
$ setxkbmap -I ~/.xkb/symbols/custom

最後にxkeysnailコマンドを試した。READMEの通り設定したが上記と同様に上手く行かなかった。

GitHub - mooz/xkeysnail: Yet another keyboard remapping tool for X environment

半ば諦めモードだがもう少し慣れて仕組みを勉強していつか再度トライしたい。

パッケージ

インストールしたのは下記。

$ yay -S \
discord discord-screenaudio slack-desktop google-chrome \
1password notion-app google-cloud-sdk duf dust jq yq \
postgresql-libs docker kubectl exa bat-cat-git ripgrep fd gping \
glances delta buf rustup go curlie ghq neovim sccache volta-bin

素数判定器を作った

みなさんは今日が素数かどうか気になったことはありますよね?私もあります。そこで簡単にツールを作りました。

github.com

Algorithm

使用したアルゴリズムAKS素数判定法ミラー–ラビン素数判定法です。

Performance

手元の環境では下記のようになりました。やはり確率的アルゴリズムであるミラー–ラビン素数判定法の方が高速ですね。(ご存知の通り2021年12月21日は素数でしたね)

$ time ./target/release/pn -a aks 20211221
true
./target/release/pn -a aks 20211221  5.23s user 0.06s system 95% cpu 5.512 total

$ time ./target/release/pn -a miller-rabin 20211221
true
./target/release/pn -a miller-rabin 20211221  0.00s user 0.00s system 69% cpu 0.004 total

参考

AKS素数判定法の実装は下記の記事を参考にRustで書き直したものです。

素数判定いろいろ - AKS素数判定法 - Qiita

組織のナレッジマネジメントは難しい

透明性で何を実現したいのか中途半端なまま透明性を謳う組織が多いと思う。また、経営層の決定事項を詳らかにすることを透明性としている場面を見かける。しかし、それは透明性ではなくて経営層が大きな声で発しているだけだと思う。
とはいえ自分の中の「なんか違う」の違和感と「どうあるべきなんだろう」を整理しておこうと思う。来年くらいに読み返したらまた違った意見になるかも。
考えるキッカケとなった記事はこちら。

note.com

透明性って経営層だけのものだっけ

いきなり私見だが、組織の透明性の意義は情報の非対称をなくし組織のどの階層でも同じ意思決定ができるようにすることだと考えている。
この情報の非対称を防ぐためには、片方しか知り得ない情報をなくせば良い。その意味では、上から下への透明性だけではなく、下から上、横同士の情報の透明性もまた必要になると思う。また、透明性は各所での意思決定の再現性を担保するためであるから、結論だけではなく意思決定プロセスが共有されるべきだと思う。
この思考はLayerXとSlackの考え方に全面的に賛成だった。

tech.layerx.co.jp

slack.com

情報の流れにはどの種類があるか

情報の非対称について組織のどこが非対称になるのかを考える。この時、非対称が起こる方向は下記の3通りであると考えた。それぞれに意味や情報の流し方の工夫が違うと思う。

「上から下」の透明性

この透明性の目的は、全社戦略に即した意思決定をどこでもできるようにするため。つまり全体と一部で同じ意思決定を再現させるため。
開発組織では(少なくとも観測範囲では)アジャイル開発が基本となり、意思決定の数が細かく・多い。透明性を上げ意思決定の判断材料を提供することで、戦略と戦術の意思決定に統一感が生まれる。目標達成に向けてベクトルが一致する。実行の効果は高まり無駄な資源も生まれづらいため費用対効果が高まる。
事業活動以外にも、例えば社内文化は文化の意図も含めて透明にすることで定着という効果を高める。中途半端に行えば伝達コストが増えるし意図した効果は出ない。

「下から上」の透明性

この透明性の目的は、戦術の実行結果を戦略に素早く反映するため。実績は仮説の検証結果であり、仮説をより深くより強くする。次の実行の効果が高まるため、費用対効果が高まる。
事業活動以外にも、例えば透明性を感じていないというフィードバックを上に上げることがあると思う。

「横同士」の透明性

この透明性の目的は、知識の有効活用のため。透明性のない組織では課題を認識・分析・解決する材料が行き届いておらず、各所で課題の再認識・再分析・再解決が発生する。知識を有効活用し、同じ轍を踏まないようにすれば、労力をなくして解決できる。
また、横同士の透明性は社内のどこに何を聞けば良いかわかるため人的資源の有効活用にも繋がる。視点が増えるため課題の認識・分析・解決の効率が上がるため費用対効果が高まる。

transactive-memory.md · GitHub

アクセシビリティ

透明性だけあっても情報にアクセスできなければ活用できない。情報の交通整理が必要。アクセシビリティは下記の3通りに整理してみた。

  • アクセス不可
    • ファイルに権限がある場合など
    • 特定の人だけを限定的に集めたMTG
  • アクセス困難
    • 情報が展開されているが、情報共有ツール/社内Wikiから探し当てることが難しい
    • チャットツールで完結している
  • 理解困難
    • 議事録に残されている内容が名詞ばかりでただのメモ
    • 文意が一意に定まらない
    • 前提となる情報や結論が欠損している

何を情報とするか

透明性とアクセシビリティは自律的に意思決定を行う組織になるための手段。それを考えると透明性であるかどうかの基準は良質で高速な意思決定材料を現場に与えることができているのかということになる。やはり、必要なのは意思決定の過程であり、その意思決定の前提や背景、意思決定に使われた基準、他の選択肢こそ共有されるべきだと思う。意思決定の過程の理解を深めることで他の場所でもその意思決定の再現(もしくは正しい反論)ができる。

情報を流すために

透明性を上げるためには情報を公開することに加えて議論の場を設けることが重要だと考える。特に同じチーム内の振り返りを公開したりチーム関係なくシャッフルして組織や事業について議論をすると、他チームで行っていることや重要視していること、価値観がわかる。視点を混ぜて議論することで暗黙知形式知に引き出す。
また、他チームメンバーの顔と名前が一致する。

corp.kaonavi.jp

アクセシビリティを上げるためには、情報の置き場所を定めることと論理展開のフォーマットを整備することだと考える。 公開権限で置き場所を定めるだけで今よりかなり情報にアクセスしやすくなり、また知らないところで行われていた議論も表に出てくる。議論のフォーマットは前提情報、実行できる選択肢、意思決定の基準、意思決定の内容、意思決定の理由、について主語と目的語がわかりやすく記載されていれば良いと思う。

同期的である必要は一切ない。必要以上にMTGに呼ぶ必要はなく、必要な情報が整理され所定の場所に置かれさえすれば良い。そうすれば非同期に情報を渡せる。非同期の情報共有が整備できておらずMTGに出席しないと情報を得られないからこそ、MTGに呼びたがる。

構造的な問題

情報が限定的だと発生する構造的な問題として情報のブローカーがある。

ブローカーとストラクチュアル・ホール

限定的な情報にアクセスできる人は情報の仲介役であるブローカーになる。組織内の情報はブローカーを通して伝わる。ブローカーは、兼務していたり、チームの境界で働いている人がなりやすい。
ストラクチュアル・ホールとは、情報が伝達するまでの距離に相当する(グラフで言うとノード間の距離)。ブローカーを介して情報のコミュニケーションをするほどストラクチュアル・ホールは大きくなる。
このストラクチュアル・ホールを埋めるためにブローカーには負荷がかかる。社歴が長く事情通な人はブローカーになりやすく、ブローカーとしての働きを見て社内では優秀と評価されることが多い。そしてブローカーはブローカーとしての仕事を続けてしまう。しかし、むしろドキュメンテーションやコミュニケーションの工夫で伝達コストを減らしストラクチュアル・ホールを減らすアクションを評価した方が建設的。

en.wikipedia.org

構造的属人化

ブローカーは構造的属人化であり、ストラクチュアル・ホールは仕組みで減らしていくべきだ。なぜなら、これは透明性やアクセシビリティと対立するからだ。
組織の階層構造と情報の伝達構造は必ずしも一致させなくて良い。透明性とアクセシビリティを高めると構造的な属人化は減り、技能的属人化を防ぐことに集中できる。
個人メンションやDMが多い組織では、個人メンションやDMを多く受ける人こそがブローカーだと言える。

下からのフィードバック

情報を透明にしアクセシビリティを高め、フィードバックはブローカーが全て別集団に流してくれるとする。そして日々の振り返りでは心理的安全性が担保され意見(フィードバック)が多く出ているとする。しかし、そのような意見を上が受け取らなかった場合、徐々に意見はなくなる。これは意見効力感の欠乏からくる。
意見効力感を高めるためには意見を出して良かったと思える実感と意見が採用・不採用された納得感が必要になる。そのため、上が下からのフィードバックを受け取ったことをフィードバックする必要がある。また、下からのフィードバックをどのような基準で意思決定したのか過程を公開する必要がある。それは効果(組織を変えること)を実感し費用(意見を出すこと)を払うことに繋がる。
ブローカーがフィードバックを仲介すると、ブローカーが情報を止めた途端にその集団の意見効力感が減少する。つまり、ブローカーはフィードバックループを握っている。そのため、ブローカーに依存しないフィードバックループを設計した方が良い。ブローカーに依存すると、ブローカーによって情報伝達や学習効率は律速する。

note.com


ばーっと書きだしたため根拠に薄い部分が多くいまいち論理的ではないけれど、体験ベースで書き下したので個人的には納得感がある。会社で起きたことを事例としているので、あまり詳細に書けない部分があるが今後読み直した時に学びがあると嬉しい。ただ、今後は組織論の本とか読んでもう少し学術的な見地も加えて改めて考えを整理してみたい。

ナレッジマネジメントの設計とか大きい組織でしてみたいね。。。