エンジニアのコミュニケーション能力

自分はおしゃべりの力や世間一般でいうコミュニケーション能力や雑談力は低いのだけど、プロダクト開発を早く進めて行くというコミュニケーションに関してはそこそこできると思っています。 そこで、自分が気をつけていることをちょっとまとめて見ようと思いました。

今回はチーム外とのエンジニアとのコミュニケーションに対して気をつけていることを考えてみました。

ほぼ全ての技術的な仕事は問題を解決するために、チームの外の人とコミュニケーションを取る必要があると思っています。 例えば、自分はプログラムが動く環境のOS全てスクラッチでプログラミングしていると行ったって、プログラミング言語を使っている時点でそのコンパイラーやインタープリターに依存しているわけだし、コンパイラーを作っている人だって、CPUやMemoryを作っている人とコミュニケーションすることはあるだろうし、CPU作っている人もその素材を作っている人とコミュニケーションすることはある気がします。

チーム内とは進捗や問題をほぼ毎日共有したりする単位。例えばスクラムで行ったら同じスプリントのタスクをやるようなイメージで、チーム外とはそのチームにいない人ということです。
 私はアプリやWEBサービスを作る仕事をしていますが、社内、社外を含めlibraryの作者、サーバーなのどのインフラを提供してくれるチーム、連携するWEB APIの開発者など様々な人と一緒に仕事をしています。 マイクロサービスなどが流行ったこともあって、社内でもそういうコミュニケーションが必要な場面は多いのじゃないかなと思います。

心構え

  • コミュニケーションの取り方次第で驚くほど柔軟対応してくれる
  • 相手側はついついブラックボックスと考えがちだけど、内部構造や相手の事情を推測したりすることやまた同じエンジニアであることを意識する
  • 自分と相手との関係は理解しておくといい
    • もちろん全てのやり取りする相手を尊重するべきだけれど、やり取りすることで相手の時間を奪うことになるので相手がどれぐらい時間と労力を使ってくれそうかを考えてコミュニケーションするといいと思う 例えば、OSSの使用者と作者、有料サービスサポート付きのユーザーと提供側では使ってくれる時間と労力が違う

チーム外と連携を取る場面

  • バグがありそうな時
    • 正確な再現手順を共有する
      • WEB APIであれば再現するcurlコマンド
      • 再現環境(OSやlibraryのversion)
      • アプリやUI操作を含むものは動画
    • エラーメッセージなどidやtimestampのあるログを共有する
      • libraryじゃなくてWEB APIやインフラなどであれば該当時間に障害があったかもしれない
    • 公開されているバグ管理表があれば似たようなケースがないか検索する
    • OSSや社内でもソースコードが公開されていれば原因を探してみる。可能であればPRを作る
  • 使い方がわからない時
    • 公式のドキュメントやFAQを読んだり、社外のものであればWEBで検索したり、社内のもの出れば社内のwiki的なこのを検索したりして解決できないか調べる
  • 共同で開発する時
    • 出来るだけ早く連携する
    • 自分たちが必要と思う部分だけではなく背景も伝える
    • リリース日に依存関係がある場合十分にバッファーを持ったスケジュールにする

コミュニケーション全般で気をつけること

  • 曖昧さをできるだけ排除する
    • やり取りの中で出てくるAPIなどは公式ドキュメントのリンクとともに伝える
  • やり取りはログが残り形にする
  • 使っている言葉が違うことがあるので相手の使っている言葉に合わせる

こちらに書いていることは、チーム外とのコミュニケーションに限らないことも多いと思いますが、チーム内であればもうちょっと雑でもいい気がしているので、個人的にチーム外の人とコミュニケーションするときに特に気をつけていることをあげてみました。

Start learning Rust

I don’t know the reason, but I want to do something different from what I’m usually doing. And I want to have a familiar programming language which does not have garbage collection (GC). There are two candidates for me, swift and Rust.

I think swift is more popular and will be getting more popular. But I started to learn rust because its memory management mechanism is interesting for me. The official web site says that the following.

  • guaranteed memory safety
  • threads without data races

Even though we use a language which has GC such as Java, we need to care about memory management especially in operation phase. I think it’s more difficult to write an application in Rust than other modern programming languages which have GC. But once it’s done, it’s easier to operate the application wrrtten in Rust.

I think I would feel that I can use a sharper knife when I get used to Rust.

Cast a List type object without iteration

There are three choices as follows:

        List<String> list = Arrays.asList("1","2");

        List<Object> objectList = (List) list;

        List<Object> objectList2 = (List<Object>)(List<?>) list;

        List<Object> objectList3 = new ArrayList<Object>(list);

But the choice 1 and 2 seem to be dangerous because the exception will be thrown when the item of the list is fetched if the list is not a List<Object>.

Job Scheduler

Since scheduled tasks of my project has some dependencies, I’m thinking whether I should use a job scheduler. There are lots of job schedulers and lots of comparison of job schedulers.

My requirements are 1. Manages DAGs which define job dependency 2. Has scheduler 3. Web UI with authentication 4. Has low maintenance cost

I’m trying airflow now.

HttpClient in Java

Both Apache HttpClient and OkHttpClient are thread-safe.

Apache HttpClient

HttpClient - HttpClient Performance Optimization Guide

Chapter 2. Connection management

OkHttpClient

Concurrency · square/okhttp Wiki · GitHub

OkHttp3 does not use a global singleton thread pool. Each OkHttpClient object can have its own private connection pool.

okhttp/CHANGELOG.md at master · square/okhttp · GitHub

Markov Chain Monte Carlo

One of the goal of the current my study about statistics is understanding Markov Chain Monte Carlo(MCMC).

I would like to write about one of the popular algorithms of MCMC, Metropolis–Hastings algorithm. It’s a kind of rejection sampling algorithm.

Let  P(x) be a probability distribution of a target distribution( P) that you would like to draw samples from.

Let  g(x) be a probability distribution of a proposal distribution( G that you already know how to draw samples from.

  1. Draw a sample  x_{n+1} from  P (The previous sample is  x_n)

  2. Accept the sample with a probability  \min\left(1,\frac{P(x_{n+1})}{P(x_n)}\frac{g(x_n | x_{n+1})}{g(x_{n+1} | x_n)}\right). Otherwise, reject it.

    It means that you always accept the sample when  \frac{P(x_{n+1})}{P(x_n)}\frac{g(x_n | x_{n+1})}{g(x_{n+1} | x_n)} \geq 1. And you accept the sample with the probability  \frac{P(x_{n+1})}{P(x_n)}\frac{g(x_n | x_{n+1})}{g(x_{n+1} | x_n)} when it’s not.

  3. Repeat 1 and 2 util the sample’s probability distribution is close enough to the target.

Let’s think about why this algorithm works.

Reversible Markov chain that I explained in the previous post plays an important role of this algorithm.

Let’s assume that samples from the target probability distribution are generated based on a reversible Markov chain which has a stationary distribution  \pi(x) = P(x).

Since the Markov chain is reversible, each transition( x_n -> x_{n+1}) of the Markov chain fulfils the following condition:

 P(x_n | x_{n+1})P(x_{n+1}) = P(x_{n+1} | x_n)P(x_n)

When  \frac{P(x_{n+1})}{P(x_n)}\frac{g(x_n | x_{n+1})}{g(x_{n+1} | x_n)} \geq 1 is true, we always accept a sample.

 \frac{P(x_{n+1})}{P(x_n)}\frac{g(x_n | x_{n+1})}{g(x_{n+1} | x_n)} \geq 1 \Leftrightarrow P(x_{n+1})g(x_n | x_{n+1}) \geq P(x_n)g(x_{n+1} | x_n)

If  P(x) = g(x), then  P(x_{n+1})g(x_n | x_{n+1}) =P(x_n)g(x_{n+1} | x_n), but it’s not. It means the proposal distribution  g(x_{n+1} | x_n) causes the chain to move from  x_n to  x_{n+1} too rarely and from  x_{n+1} to  x_n too often. Even though this condition, we get  x_{n+1} from G. Then we accept the sample  x_{n+1}.

On the other hand, when  \frac{P(x_{n+1})}{P(x_n)}\frac{g(x_n | x_{n+1})}{g(x_{n+1} | x_n)} < 1, we accept the sample with the probability  \frac{P(x_{n+1})}{P(x_n)}\frac{g(x_n | x_{n+1})}{g(x_{n+1} | x_n)}.

 \frac{P(x_{n+1})}{P(x_n)}\frac{g(x_n | x_{n+1})}{g(x_{n+1} | x_n)} \Leftrightarrow P(x_{n+1})g(x_n | x_{n+1}) <  P(x_n)g(x_{n+1} | x_n)

It means the proposal distribution  g(x_{n+1} | x_n) causes the chain to move from  x_n to  x_{n+1} too often and from  x_{n+1} to  x_n too rarely.

We need to adjust the transition by using acceptance distribution  A(x_{n+1} | x_n) such that:

 P(x_{n+1})g(x_n | x_{n+1}) =A(x_{n+1} | x_n)  P(x_n)g(x_{n+1} | x_n)

 A(x_{n+1} | x_n) = \frac{P(x_{n+1})}{P(x_n)}\frac{g(x_n | x_{n+1})}{g(x_{n+1} | x_n)}

This is my intuitive understanding of Metropolis–Hastings algorithm.

More formal way of explanation from wikipedia is as follows:

 P(x_n | x_{n+1})P(x_{n+1}) = P(x_{n+1} | x_n)P(x_n) can be re-written as

 \frac{P(x_{n+1} | x_n)}{P(x_n | x_{n+1})} = \frac{P(x_{n+1})}{P(x_n)}

We draw a sample from the proposal distribution G and accept it with a certain probability  A(x_{n+1} | x_n) to simulate the target distribution P.

 P(x_{n+1} | x_n) = A(x_{n+1} | x_n) g(x_{n+1} | x_n)

Let’s insert this to the previous equation.

 \frac{A(x_{n+1} | x_n) g(x_{n+1} | x_n)}{A(x_ | x_{n+1}) g(x_n | x_{n+1})} = \frac{P(x_{n+1})}{P(x_n)}

  \Leftrightarrow  \frac{A(x_{n+1} | x_n) }{A(x_ | x_{n+1}) } = \frac{P(x_{n+1})g(x_n | x_{n+1})}{P(x_n)g(x_{n+1} | x_n)}

Then we need to choose a acceptance distribution  A(x_{n+1} | x_n) which fulfils this condition. The choice is used in the Metropolis–Hastings algorithm is as follows:

 A(x_{n+1} | x_n) =  \min\left(1,\frac{P(x_{n+1})}{P(x_n)}\frac{g(x_n | x_{n+1})}{g(x_{n+1} | x_n)}\right)

This fulfils the previous condition because

When   \frac{P(x_{n+1})}{P(x_n)}\frac{g(x_n | x_{n+1})}{g(x_{n+1} | x_n)} \geq 1, then  A(x_{n+1} | x_n) = 1.

When   \frac{P(x_{n+1})}{P(x_n)}\frac{g(x_n | x_{n+1})}{g(x_{n+1} | x_n)} < 1  \Leftrightarrow \frac{P(x_n)}{P(x_{n+1})}\frac{g(x_{n+1} | x_n)}{g(x_n | x_{n+1})} > 1, then  A(x_ | x_{n+1}) = 1.