memcachedのサイズ別データ数のカウント

これは何

memcachedからredisへ移行したいので、redisのベンチマークを取っていたが、その時にmemcachedに保存されているデータがどれくらいのサイズがどれくらいの量で出現するのか確認した。その話を適当にまとめておく。

chunkサイズとslabについて

参考: http://gihyo.jp/dev/feature/01/memcached/0002

memachedはメモリのフラグメンテーションを防ぐために slab allocator というのを使っているっぽい。単にmalloc と freeを使ってメモリを扱うとばらばらになってしまう。確保したメモリをいろいろなサイズの塊(chunk)にわけて同じサイズの塊をクラスとしてひとまとまりにする。stats itemの出力はどうもこのchunk別に別れている模様。

class別個数カウント

$ echo "stats items" | nc localhost 11211    | grep number
STAT items:1:number 6
STAT items:2:number 4990
STAT items:3:number 1
STAT items:4:number 39
STAT items:5:number 160
STAT items:6:number 3
STAT items:7:number 17
STAT items:8:number 19
STAT items:9:number 46
STAT items:10:number 1
STAT items:11:number 5326
STAT items:12:number 1397
STAT items:13:number 60
以下略

このコマンドは↓でもよい

$ echo "stats slabs" | nc localhost 11211  | grep used_chunk

classのサイズを確認する

上からサイズ順になっている。

$ echo "stats slabs" | nc localhost 11211  | grep chunk_size
STAT 1:chunk_size 96
STAT 2:chunk_size 120
STAT 3:chunk_size 152
STAT 4:chunk_size 192
STAT 5:chunk_size 240
STAT 6:chunk_size 304
STAT 7:chunk_size 384
STAT 8:chunk_size 480
STAT 9:chunk_size 600
STAT 10:chunk_size 752
STAT 11:chunk_size 944
STAT 12:chunk_size 1184
STAT 13:chunk_size 1480
以下略

トータル個数とか一定サイズ以上のものとか

$ echo "stats slabs" | nc localhost 11211  | grep used_chunk | awk 'BEGIN {num = 0};{ sum =+ sum + $3} END {print sum }'

大きいサイズの個数が知りたいななど。例えばclass 19以上のもの(size 5kでの区切りで見たやつ)

$  echo "stats slabs" | nc localhost 11211  | grep used_chunk | egrep "(STAT 19|STAT [2-4][0-9])" |  awk 'BEGIN {num = 0};{ sum =+ sum + $3} END {print sum }'

redisとmemcachedのベンチマークをmemtier-benchmarkで取る

これは何

redisとmemcachedベンチマークを memtier-benchmark というツールで取った。 ざっくりまとめておく。

github.com

memtier-benchmark と redis-benchmark の比較

主にmemtier-benchmarkのメリット。redis-benchmarkにできないこと。

  • redis, memcache の両方のプロトコルを喋れる(redis-benchはredisのみ)
  • SET/GETの重み付けができる(redis-benchはそれぞれ別に全力で投げる)
  • keyとvalueサイズを変えられて(redis-benchはkeyサイズを変えるのが大変。valueサイズはどちらもオプションで変えられる。)

memtier-benchmark のインストー

概ねREADMEどおりにできた。 インストールしたlibevent-2.0がmemtier-benchmark実行時に見つからなかったので雑にlinkして見えるようにした(多分良い解決策ではないが、使い捨て負荷試験環境なので良し)。

sudo ln -s /usr/local/lib/libevent-2.0.so.5 /usr/lib64/libevent-2.0.so.5

コマンド

memtier_benchmark -p 6379 -P redis --threads 4 -c 250  --test-time=10 --ratio=1:10 --data-size-list=1000:60,5000:30,100000:10  --key-prefix=memtier-memtier-memtier- --key-maximum=100000

実際にやったコマンドとは異なるが、サンプルとして書いておく。以下雑な説明

  • -p port
  • -P protocol
  • –threads クライアントのスレッド数
  • -c スレッド数あたりのコネクション数。トータル4*250=1000となる
  • –test-time テスト時間
  • –data-size-list. 今回はmemcachedからredisへの移行が目的。現行のmemcachedのデータに合うようにweightを決めた(別記事として書くかもしれない)
  • –key-prefix key長を適当に調整するために使える
  • –key-maximum キーのバリエーションになると思う

fluent-plugin-ping-messageプラグインを最新のを入れるとtd-agentがunstable versionになるためバージョン指定する話

これは何

https://rubygems.org/gems/fluent-plugin-ping-message/ という Fluentd の plugin を最新化すると、同時に td-agent 本体も unstable な最新の 0.14 系にあがってしまい、ログ転送がたまにできなくなる事象が発生する。 その対策。

原因

この現象はpluginの依存するfluentdのバージョンが指定されているので、gemで最新のplugin version1.0.0が入るとfluentdも v0.14が必要になり、強制的にあげられてしまうため。 参考: https://github.com/tagomoris/fluent-plugin-ping-message

対策

ansibleのtasksで書くならこんな感じ。-vで指定する。

- name: add fluent-plugin-ping-message
  shell: td-agent-gem install fluent-plugin-ping-message -v {{ FLUENT_PLUGIN_PING_MESSAGE_VERSION }}

stableなtd-agentのバージョンは? どこで確認する?

http://www.fluentd.org/download

  • stable v0.12 系
  • experimental version が v0.14となっている。

td-agent本体 version確認方法

$ which td-agent
/usr/sbin/td-agent
$ td-agent --version
td-agent 0.12.29

plugin version確認方法 

td-agent-gemで入れているので listコマンドから確認

$ td-agent-gem list  | grep ping
fluent-plugin-ping-message (1.0.0, 0.2.0)

左が新しいもの、右が古いversionっぽい。gemで入れると古いバージョンも残るらしい。 $ td-agent-gem list | grep ping fluent-plugin-ping-message (0.2.0)

入れなおし方法

  • sudo yum remove td-agentを実行する
  • rm -rf /opt/td-agentを実行して残存プログラムを完全消去する。これをやらないと/opt/td-agentにプログラム本体がいるっぽく yumで再インストールしてもversionが新しいプログラムのままになる。
  • td-agent再インストー

実験

plugin入れる前

$ td-agent --version
td-agent 0.12.31

入れてみる

$ sudo  td-agent-gem install fluent-plugin-ping-message
Fetching: strptime-0.1.9.gem (100%)
Building native extensions.  This could take a while...
Successfully installed strptime-0.1.9
Fetching: serverengine-2.0.4.gem (100%)
Successfully installed serverengine-2.0.4
Fetching: msgpack-1.1.0.gem (100%)
Building native extensions.  This could take a while...
Successfully installed msgpack-1.1.0
Fetching: fluentd-0.14.13.gem (100%)
Successfully installed fluentd-0.14.13   ★ここで fluentd 0.14 を入れている。
Fetching: fluent-plugin-ping-message-1.0.0.gem (100%)
Successfully installed fluent-plugin-ping-message-1.0.0
Parsing documentation for strptime-0.1.9
Installing ri documentation for strptime-0.1.9
Parsing documentation for serverengine-2.0.4
Installing ri documentation for serverengine-2.0.4
Parsing documentation for msgpack-1.1.0
Installing ri documentation for msgpack-1.1.0
Parsing documentation for fluentd-0.14.13
Installing ri documentation for fluentd-0.14.13
Parsing documentation for fluent-plugin-ping-message-1.0.0
Installing ri documentation for fluent-plugin-ping-message-1.0.0
Done installing documentation for strptime, serverengine, msgpack, fluentd, fluent-plugin-ping-message after 8 seconds
5 gems installed
$ td-agent --version
td-agent 0.14.13

haproxy設定の自分メモ(redisのrevproとして)

これは何

redis+sentinelの前段にhaproxyを置いてクライアントがあんまりnode poolを意識しなくてもよさそうな感じにしたい。 devだけで適用して本番には適用していないので甘いところはあるかも。

方針

redis slaveにgetを負荷分散したりするほど負荷は高くない系に使えるかも。 常にmasterに接続しに行き、slaveは待機系。

設定ファイル

ansible templateの形。

global
  {# chroot内で動いているため/dev/logへアクセスできない。そのためUDP経由でrsyslogにログを渡す。-#}
  log         127.0.0.1 local2
  chroot      {{ HAPROXY_JAIL_DIR }}
  user        {{ HAPROXY_USER }}
  group       {{ HAPROXY_GROUP }}
  daemon
  {# maxconn: redisで設定しているものより大きければよい。このレイヤでは特に制限はかけない。 -#}
  maxconn     10000

{# stats: haproxyのステータスを取れるようにするための設定 -#}
listen stats 
  {# bind: ポート9000への外部からの経路はないが念のためback側からのみ疎通可能としておく。-#}
  bind :9000 interface eth0
  mode http
  stats enable
  stats uri /haproxy_stats 
  {# refresh: stats画面の自動リフレッシュを有効にする -#}
  stats refresh 10    

defaults
  mode                    tcp
  log                     global
  option                  dontlognull
  {# retries: バックエンドサーバへ接続失敗した時の再試行回数-#}
  retries                 3
  {# timeout queue: maxconnを超えたリクエストはキューに入る。そのキューに入った接続を開放するまでの時間。そんなに待たせても意味が無いのであまり大きな値じゃなくてよい。 -#}
  timeout queue           10s
  {# timeout connect: haproxyのbackendへの接続のタイムアウト。近いネットワークにいれば基本的に短い時間で接続できるはず。 -#}
  timeout connect         5s 
  {# timeout check: コネクションが確立したあとの追加のヘルスチェックのタイムアウト。これに時間がかかるということはラグの大きなサーバなので、長い時間待つ必要はない。-#}
  timeout check           5s
  {# timeout client-fin/server-fin: 正しく接続断しなかった場合に切る(こちらからFIN送信したがクライアントがFINを送ってこなかった場合)。FIN_WAIT状態が続くのを防ぐ。 -#}
  timeout client-fin      10s
  timeout server-fin      10s

{# その他入れない設定について。いれるのが推奨のようなので要検討。
* timeout client: 非活性なクライアントからの接続を切る設定。redis設定に合わせて設定しない。 
* timeout server: haproxyから非活性backendへの接続を切る時間。redis設定に合わせて設定しない。 
-#}


frontend  main
  bind *:5000
  default_backend             myredis

backend myredis
  {# balance: どれか1台だけがコネクションを受け付ける設定(first)。id設定があれば一番値が小さいもの。なければリストの1番上から優先される。負荷分散よりも1台のmasterにキャッシュがのることを優先する。 -#} 
  balance    first
  {# 通常時のヘルスチェック間隔はredisのマスター昇格のタイムスケールにあわせて秒単位でよい。haproxyのreload直後などでヘルスチェックをしていない場合には即座に行う(fastinter)。-#}
  {% for srv in HAPROXY_BACKEND_LIST -%}
  server  redis{{ loop.index }} {{ srv }} check inter 1s fall 2 rise 1 fastinter 10ms
  {% endfor %}

  option tcp-check
  tcp-check connect
  tcp-check send info\ replication\r\n
  tcp-check expect string role:master
  tcp-check send QUIT\r\n
  tcp-check expect string +OK

ちなみに rsyslog設定のansible taskはこんな感じ

# haproxyのログ出力のためのrsyslog設定
- name: enable UDP log acceptance for rsyslog for haproxy 
  lineinfile:
    dest: "/etc/rsyslog.conf"
    line: "{{ item }}"
  with_items:
    - "$ModLoad imudp"
    - "$UDPServerRun 514" 
    - "local2.* {{ HAPROXY_LOG_DIR }}/haproxy.log"

redis設定について自分メモ

これはなに

redis設定をした時の検討項目。 devで設定したのみでまだ本番で使っていないのであまいところはあるかも。

設定diff

以下は redis-3.2.8 に同梱されていた設定ファイルとplaybook template とのdiff。

diff /tmp/redis.conf roles/install_redis/templates/redis.conf.j2
1c1,3
< # Redis configuration file example.
---
> # USAGE: {{ item.USAGE }}
> # Redis configuration file.
> # This configuration is based on redis-3.2.8 default configuration file.
61c63,64
< bind 127.0.0.1
---
> {# bind: 特に認証はかけない。やるならサーバが居るネットワークセグメントごとに裏からのみ疎通できるようにするなどの設定をいれる。 -#}
> # bind
80c83,84
< protected-mode yes
---
> {# protected-mode: キャッシュサーバはインターネットに直接露出しなければ切っておいてもよいかもしれない -#}
> protected-mode no
84c88,89
< port 6379
---
> {# port: 同一サーバ内に別ポートで複数インスタンスを立ち上げることができるようにしておきたい -#}
> port {{ item.PORT }}
93c98,99
< tcp-backlog 511
---
> {# tcp-backlog: これと一緒にsomaxconnもsysctlで設定しておく必要がある。TCPの接続要求を格納するキューの最大長なので、front系サーバapacheがある程度詰まっても大丈夫な値にしておくのがよさそう -#}
> tcp-backlog 2500
104a111
> {# timeout: idle状態のコネクションはタイムアウトさせない(デフォルト設定)。アプリケーションが側できちんと切断処理がされていれば問題にならないはず-#}
127a135
> {# daemonize: 直接redis-serverコマンドにこの設定ファイルを食わせて起動した場合にはデバッグのためにdaemonize noでよい。systemctlで起動した場合にはこの設定に関わらずデーモン化される -#}
150c158,159
< pidfile /var/run/redis_6379.pid
---
> {# pidfile: 一応設定しているが、systemdではpidファイルは不要 -#}
> pidfile /var/run/redis_{{ item.PORT }}.pid
163c172,173
< logfile ""
---
> {# logfile: redisユーザに権限があるディレクトリにログを吐くようにしておく。ローテートも忘れずに。redisはログを吐くごとにファイルを開き直すのでSIGHUPなどは不要。 -#}
> logfile "{{ REDIS_LOG_DIR }}/redis.log"
177a188
> {# database: keyspaceが独立したdatabaseを何個にするか。使えるDatabaseがの数が大きくてもデフォルトは0が使われるので細かい値はなんでもよさそう。dev, productionとかで使う用途を想定しているみたい。-#}
201c212,216
<
---
> {# save: redisが死んでて復旧した時にRDBには古いデータが残っている。しかしexpireは実時間基準なのできちんと古いデータは消えておいてくれる。
> RDBを取る時に親プロセスがやるのは子プロセスをforkさせるだけなので、親インスタンスにdisk I/Oが発生して待ちになることはないとのこと。あまりにもデータが大きいもしくはCPUがいまいちな場合にはforkに時間かかるのでclientへのレスポンス遅れることもあるらしい。
> RDBの方がAOFをよりも起動が早く、データサイズも小さいのがメリット。マスタデータはredisには無ければ厳密に永続化を考えなくても良い。パフォーマンスが落ちない程度に使う方針。
> 設定はデフォルトで問題なさそうならそのまま。
> -#}
219c234,235
< stop-writes-on-bgsave-error yes
---
> {# stop-writes-on-bgsave-error: persistentが止まるよりもwriteが止まるほうが不都合。TODO 適切な監視はほしい。 -#}
> stop-writes-on-bgsave-error no
224a241
> {# rdbcompression: 多少CPUを使っていても圧縮しておいてくれた方が良い。あまりにCPU不可が高くなりそうなら変えても良い。 -#}
233a251
> {# rdbchecksum: 10%程度の性能劣化はあるがRDBファイルの破損の検知は入れたままでもよさそう。 -#}
247c265,266
< dir ./
---
> {# dir: ファイル名はデフォルトで良いが、保存先は適切なpermissionの場所に配置しなければならない。-#}
> dir {{ REDIS_DATABASE_DIR }}
265c284,288
< # slaveof <masterip> <masterport>
---
> # slaveof <masterip> <masterport>
> {# slaveof: 設定に書かないでDBと同じようにコマンドで指定するのもあり。ただしmysqlの場合にはchange master toではその設定がファイルに保存される。redisの場合には設定に書いてないと再起動時に消えてしまうと思われる。意図せず消えてしまうか、意図しないmasterを向いているかで比較的後者の方が事故が少ないと考えてファイルに記載する。原則としてシステムに組み込むときにはmasterノードがどれか確認してから組み込むのがよい。 -#}
> {% if REPL_NODE=="slave" %}
> slaveof {{ REDIS_MASTER_NAME }} {{ REDIS_MASTER_PORT }}
> {% endif %}
284a308
> {# slave-serve-stale-data: レプリケーション遅延で多少古いデータが帰ってしまったとしてもエラーを返すよりマシかもしれない(デフォルト) -#}
300a325,326
> {# slave-read-only: masterとの再同期によりslaveのみに保持されたデータは削除されるのでslaveに一時的に書き込みたいデータを書いておく用途に使える。特に使いたい理由がなければreadonlyのほうがよい(デフォルト)。
> -#}
414c440,441
< slave-priority 100
---
> {# slave-priority: 基本的には変更しないが、仮にmasterにしたくないslaveを作ることになったらansible EXTRA_VARSで0に設定できるようにしておく。-#}
> slave-priority {{ REDIS_SLAVE_PRIORITY | default("100") }}
511a539
> {# maxclients: 十分に大きいのでデフォルトのままでよい。pconnect使うならそれを意識して増やしておいたほうがよい。コネクションの数の監視も必要だろう-#}
537a566,570
> {% if ENVIRONMENT == "production" -%}
> maxmemory {{ item.MAXMEMORY }}
> {% else %}
> maxmemory 1G
> {% endif %}
592c625
<
---
> {# appendonly: redisにはマスタデータは乗らなければ永続化より性能を重視してOffにしておく。 -#}
623,624c656,657
< appendfsync everysec
< # appendfsync no
---
> # appendfsync everysec
> appendfsync no
834a868
> {# slowlog-log-slower-than: デフォルトでも秒に直すと0.01秒なので十分小さいのでそのままでよい。-#}
838a873
> {# slowlog-max-len: ここで設定した以上古いログは消えてしまう。まずはデフォルトで良い。あとで値を上げたければCONFIG SETコマンドを用いて設定できる。ログを見るためにはSLOWLOG GETコマンドを用いる -#}
905a941
> {# notify-keyspace-events: TODO 何かに使えそう。更新されたらPUBされるので他のサーバが更新を検知できる。サーバリストなど。PUBされた時にSUBしているサーバが接続していなければ受け取れないので注意が必要。pub/subを使いつつ、定期的に値を見直すなどを使えば信頼性をあるていど担保できるかもしれない。 -#}

nginxによるgzip圧縮について【技術メモ】

これは何

nginxでproxyするときにgzip圧縮してレスポンスする方法を学んだのでメモ書き。 nginx versionは 1.11.10。

動作確認

レスポンスヘッダに以下のものが含まれる

  • Vary: Accept-Encoding
  • Content-Encoding: gzip

設定内容

  • gzip_buffers 1000 64k ;   
    • 配信する最大サイズと対応させる。
    • 「マスタリングNginx」によれば、bufferが足りないと巨大なファイルは途中で切れてしまう現象が発生するとのこと。デフォルトだと(確か)128kまでしか圧縮できない。
    • 実際に使う分だけそのとき割り当てられる。
  • gzip_types
    • text/html はデフォルト。他のmime typeに対して使うなら追加する。
  • gzip_proxied any;
    • プロキシしているレスポンスについても必ず圧縮をかける。
  • gzip_vary on;  
    • Accept Encoding に gzip入れていないものについては別のキャッシュが使われる
  • gzip_comp_level;  
    • スマホなどで見ることも多い場合は基本は圧縮したほうがよい。「マスタリングNginx」によれば圧縮速度とサイズのトレードオフでバランスのとれた圧縮は4か5。圧縮にさけるリソースはそこまでないと考えれば1とか。(1でも9でも圧縮率には大差ないという情報も)。
  • gzip_disable msie6; 
    • msie4 ~ 6はAccept Encodingを送ってくるがちゃんとgzipを扱えないことがあるという情報も。msie6はIEのver 4~6に対応したエイリアスっぽい。一応入れておくほうがよさそう。

メモ

配信サーバの画像のgzip圧縮しようかと思って調べたけど、そもそもjpegは圧縮されたフォーマットだったのだった。 結局使わなかった設定。

Webエンジニアとして大事な仕事の進め方 3選(社会人4年目を前に考えたこと)【雑記】

これは何

そろそろ社会人4年目になろうという3月半ばである。 主に、これからの自分のために今まで仕事をしてきて考えたことを記録しておく。

振り返った時に、ここに書いてあることが正しいと思うのか、後から振り返った時に間違っていると思うのか。 間違っていると思ったらなら、それはおそらくその時に成長しているということだと思う。

目次

はじめに

対象読者

以下のタイプの人にはこの記事は役に立たないと思われる。

  • 常に限界突破できる
  • 常にモティーベーションが高い
  • 人生が楽しすぎて仕方がない

なんとかだましだまし仕事をしてそこそこ楽しく生きていきたい人にとっては少し役に立つかもしれない。 同じ労力をかけていても、信頼される人/されない人、仕事が進んでいるように見える人/見えない人 がいる。 楽をして楽しく仕事がしたいじゃないか。

背景

仕事辛い => なんとかなる までの道のり

丸3年仕事をしてきて、今でこそ辛さはかなり減ってきた。 辛い時期も結構多かったので、軽くだけども辛かった時期について触れておきたいと思う。

今は主にインフラエンジニアとして、小さくないサービス(月間億単位のPVがある)の主インフラ担当として日々膨大なアクセスをさばくサーバたちを管理している。 物理マシンの調達依頼から始まり、OSやネットワークの設定、ミドルウェアのインストールや設定、必要ならアプリをデプロイしての動作確認までが私の仕事。

最初はHTTPのリクエストとレスポンスの違いもわからず、ほぼズブの初心者として入社した。研究室で書いていたのはクソPerlスクリプトFortranの死ぬほど汚いコードのみ。 研修は通常は4ヶ月のところが、直属の上司が忙しく放置気味でさらに追加で5ヶ月くらいひたすら”研修資料を作る研修”を行っていた。

1年目の終わりごろに先輩からいくつかの細々としたタスクをもらってこなしていた。 楽、だけど自分でタスクコントロールしているわけじゃないし、お給料分働けていない。 しかし2年目に入る頃ぐらいに所属部署が解体された。インフラとしてまとまっていたチームメンバが各開発部に散り散りに。私も開発部へ放り込まれることに。その時点で、まともに仕事がこなせない素人である。

2年目の半ばには、やっとなんとか仕事が回せるようになった。しかしプルリクを出せばコメントが大量につき炎上状態、口頭で上司に話をしに行けば私の理解力の無さに上司がヒートアップしてしまう。辛い日々。 「いや、だって、先輩たちがドキュメントの一つも残していないのに、突然放り込まれた私の身にもなっておくれ」などと思っていた。

3年目に入ったころから信頼もされ始め、インフラ周りの仕事は作業は大量ではあるが大体どのように進めればスムーズにいくのか肌感覚でわかってきた。 お給料もやっとここら辺で上がり始める。よかった。 給料分の仕事をしている感。

3年目の終わりごろ、担当サービスのインフラのトラック係数を上げるために後輩と同期への技術共有をしなければならず、また奮闘中。 主インフラ担当が一人でタスクをこなすのはそこそこ仕事は早く進むが、私がトラックにはねられるとインフラ仕事の大幅鈍化が予想される状態。 教えることに時間を取られて辛い以外は、割と楽しく仕事をしている。

辛さをなくす方法

基本は、以下のことを考えていたように思える

  • 必要な情報を自ら取りに行く
  • キーパーソンに信頼されるためにはどうすればいいのかを考える
  • 会社やチームに(最小労力で)提供できる価値は何か考える

重要なのはこれだけで、以下に書くことは各論でしかない。 パッと思いついたことを三つだけ載せておく(3選とは。別にタイトルに意味はない。)

1.期限を確認する

インフラの仕事は2種類あると思っている

  • 期限のかっちりしたプロダクトリリースに密接に関連した仕事。新規サービスや機能拡張のためのインフラ仕事。
  • 期限が比較的緩やかな冗長化や可用性の向上などのための改善業務。サーバ構築自動化やCacheやDBのクラスタリングを組むなど。

特に前者の仕事では期日に間に合うことが重要である。 仕事のクオリティや将来のことを考えても、”とりあえずこの期限までに仕上げたい” という仕事である。 この仕事のデットラインを超えてしまうことは信頼をなくしてしまうことにつながる。 最初から無茶な仕事は投げ返すか新たに人をもらうなどの対応をすべきだが、最初に「自分一人でこの日までにできる」と言ってしまったならその日までに終わらせることが重要。

インフラの世界では Infrastracture as a code の実践が最近当たり前だが、それをやることが最優先ではない。手段である。 コード化されてなくても、再利用性が低くても、重要なリリースに間に合わせるのが最重要だった場合には、そういった手段の部分は妥協する場合がある。 10割構築自動化するのは手間がかかるので、8割自動化しておいて、2割は手作業で仕上げる。 しかし2割の部分も暗黙知にしてしまうのではなくてドキュメントに手順や懸念点を残しておくのは大切。 雑に作る予定の部分については上長にあらかじめ話を通しておき、リスク判断を自分だけの問題としない。 問題点が明らかになっていれば、期限を延ばしてその問題点を潰すべきか、無視できるならリリースするかは上長が判断できる。

2.進捗の報告はタスク内容で行う

よく 「その仕事はどこまで進んだのか?」と聞かれて「8割がた終わりました」などとざっくり答える人がいる。 大体そういう答え方をしている人の仕事は、実質5割までしか進んでいない(自分の中でも大体終わったなと思った仕事が長引くことも多い)。 エンジニアリングでは最後の詰めの部分で問題が発生することがよくある。詳細動作確認をしてみたら何かうまく動かないだとか、追加注文が入るだとか。 そういう場合に起こっている問題は解決に時間がかかるものだ。 よって、最初に見えていたタスクが8割終わっていても、2割の仕事がそこから増殖することになる。 はたから見ると、いつまでたっても仕事が終わらせられない人が出来上がる。

どこまで進んだかタスクの内容でチーム内で共有しておくと良い。 例えば

  • これとこれは終わって、後のタスクはこれ、チケットの個数でいうと8分の6終わっている。
  • だけど後のタスクで詰まるとしたらこういう可能性があるかもしれない
  • スムーズにいったら後2日だけど、バッファとして後6日欲しい

仮にざっくりパーセントで言うと、その人が何で詰まっているかもわからないし、一人で抱え込んでいるので潜在的リスクも発見されない。 他の人が見えているが自分が見えていないタスクやリスクも見えない。 ある程度細かい粒度で共有しておけば、何か困った時にアドバイスももらえるし、「それは確かに時間がかかるのもしょうがないね」と納得感も出る。

3.情報は再利用しやすいようにする

できるだけ同じことはしない。

エンジニアはコードについてはモジュールの再利用を考えてうまくやる人が多い。 私は技術調査についても同じことをした方が良いと考えている。

社内でもう詳細調査をした人がいるのに情報が公開されていない、検証過程・思考過程が見えない。そういうことはよくある。 自分が調査をしたら、その思考過程や意図を含めてドキュメントに残しておくべき。 一般的な話なら外部公開をしても良いし、社内秘が含まれるなら社内ドキュメントでも良い。

最低限殴り書きでもいい。何も残さないよりマシ。 有用な文章を残していくと、会社全体に役に立つ。 未来の自分にも役に立つ。後輩指導にも役に立つ。ちゃんと情報を出す人は信頼される。

まとめ

楽して楽しく仕事をしたい。そのためには周りに信頼されること、協力を得ること、それが大事だと思う。 どうやったら周りに信頼されるのかな? どうやったらもうちょっと少ない労力で仕事を進められるかな? というのを考えながらやっていきたい。