an odd fellow

仕事のメモ

きのこ先生

今年の新卒の研修に参加できることになり、twadaさんのエンジニア心構えの講演を聞けた。感想とメモを置いておく。

感想

twada先生の話で何度も強調されたのは「可処分時間が減るから効率的に学びなさい」ということだった。認めなければならないと思った。可処分時間が余りまくっていた学生だから勉強していたんだということを。学生の頃と比較してアニメを見る数は減っていないのに勉強する時間が減っている現状を思うと、可処分時間が減ったぶん、そのまま勉強する時間が減っているというのは自明だった。というわけで生活を見直すことにした。勉強することを生活の中心に据えなければこの先生きのこることはできないのだ。

メモ

  • 2つの本を下敷きに今日話すことのテーマを持ってきた
  • プログラマが知るべき97のこと
    • 電車の中で1本読もうみたいな感じでやれる
    • きのこ本
  • 達人プログラマ
  • ひとつだけ持って帰ってほしいのは"学び続ける姿勢"(きのこ本の18番)
    • プログラマは学び続けないと限界が来る
    • どうやって学び続けるか
  • "常にあなたの知識ポートフォリオに投資すること"(達人プログラマ
    • 技術を学ぶのではなく、技術の学び方を学ぶ
    • 自分の自由に使える時間は短くなってく
    • 効率よく学ばないとダメになってく
  • 技術者におけるメタ認知とは何か
  • Agenda

学び方を学ぶコツ

四半期毎に技術書を読む(達人プログラマーより)

  • 3ヶ月に1回は技術書を読む
  • (ちょろいな。)と思ってない?これを20年続けるってどういうことかわかる?
  • 初期のころは四半期よりもうちょっと頑張ったほうが良い
  • 脱線して人間の学びの仕組みについて
    • 感覚記憶(0.5~2sec)、短期記憶(15~30sec)、長期記憶(死ぬまで?)
    • 長期記憶は入っているけどアクセスできなくなる(忘却)
    • 脳のストレージは無限にあると考えて良いが、インデックスが貼られてないとアクセスできない
    • ピッカーを育てる方法
      • 反復練習(これは効率的だと知られている)
      • 連想記憶を育てる(他の本や、技術と紐付ける)
        • たとえば時系列に並べる
          • (余談)1999年は当たり年でその後の20年のソフトウェアを牽引する本が出た
          • (余談)アンディとデイブ(プログラミングルビーの著者、達人プログラマーの著者のうちのひとり)
          • 本は過去の本、自称しか参照できないので、この本を書いたときはこの本は知っているだろう、ということがわかる
          • DDDはrailsの登場によってソフトウェア開発が激変したことを知らない
          • railsは登場より前に出た本に書いてあることをいいとこ取りしようとした
          • railsの作者は2000年のプログラミングルビーを読んでいるだろうし、プログラミングルビーは達人プログラマーの著者の2人が書いてる、みたいな感じで紐付けていく
          • railsの著者は過去の本を良く読んでいて、本に出ていたアイディアをプログラムとして実装したもの
        • 時系列でまとめると次読む本を決めるヒントが得られる
          • 過去方向に遡るか、未来方向に遡る
        • 自分は読んだ時期とそのとき何があったっけをまとめたことがあるな

手を動かして学ぶ(きのこ本より)

  • 人ができるようになるのは強いFBループがあってこそ
    • できるようになってからやるのでなく、やるからできるようになる
    • FBサイクルを回すためにアウトプットする
    • 最初は何が面白いんだコレと思うけどやっていくうちに好きになる
    • 逆手に取って言うと、モノを始める際に感情は関係ない
  • もうひとつ有名なやつで、デールの円錐という学習モデルがある
    • ひとは2週間後にどれくらい覚えているか
    • 読んだだけのモノは10%覚えている
    • 聞いただけのものは20%覚えている
    • 見たものは30%覚えている
    • 見て聞くと50%覚えている
      • ここまでは受動的な学習
    • 発話したものは70%覚えている(議論とか説明したもの)
    • 発話して行為を伴ったものは90%覚えている
  • 同じだけの時間をかけても定着率が違う
  • なんらかの形でアクティブに勉強しよう
  • twadaさんがおすすめのやり方は写経
    • twitter見て
    • 全部やると辛いから、これはマスターすると決めたものは写経する
    • 3ヶ月に1冊は写経でやるとか
    • ほかは脳内インデックスを貼る

毎年少なくとも一つの言語を学習する(達人プログラマーより)

  • この教えが一番有名だし実践している人もいるし難易度が高い
  • どんな言語を学ぶ?仕事で使う言語が良いでしょう。
  • 最初の数年は仕事で使う言語を深く学ぶ
  • その先は仕事で使わない言語も学ぶ
  • そのときのコツはパラダイムを段々変えること
  • 動的な言語をやったら静的な型付をやるとか
  • ちょっとずつ軸を変える。色んなものを変えすぎると自分の仕事にFBが弱くなってしまう。
  • 違う言語の良いパラダイムを学ぶことで仕事で使う言語に輸入してくるためにこれをやる。
  • 最初の数年の戦略とその後の戦略はちょっと違う
  • 他の戦略としては、周りを見る。今後来そうな言語とか。
  • 四半期に一回TECHNOLOGY RADARをthoughtworksから出るからそれを見てみる
  • 専門分野において↑を見て手をつけるのは遅い。答え合わせみたいな感じで使う。専門外は参考にする。
  • あとは英語やろうね。英語の一次情報にスムーズにアクセスしましょう。喋れるまでいかなくてもスラスラ読めるように。

身の回りをプログラミング対象にする

  • 上の疑問の答え。最初は仕事の言語でやるから良いけど、その後新しい言語を学ぶときにやることは身の回りのこと。
  • twadaさんは出版社とのやり取りをプログラミングしている

アウトプットを行う

  • 「人に教える」はよい
  • アウトプット自体がインプットになる
  • 量は質に転化する
  • たくさんやろう
  • アウトプットどこでやるか。twitterとかqiitaとか書籍、github
  • ブログを書くことについて
  • 「新規性のあること書けないです」
  • 情報発信、blog、発表、公開などは数学の証明ではなく、料理のようなもの
  • 二番煎じでも三番煎じでもいいよ
  • "いつ書かれたか"も大事。バージョンを記す。日付を記す。
  • 意見でもよい。使ってみてこう思ったみたいなやつ。
  • ブログって日本だけ?海外のブログ文化みたいなの無いんですかね?<?
  • 雑誌とか
  • ブログか講演から声がかかるよ

質問

  • Q.今は本で学べばいいんだっけ?
  • A.2000年代は本がトレンドを引っ張ってた。今は企業が引っ張ってる。
    • Facebook, Google, Thoughtworks, Microsoft
    • 最近は講演が一次情報になる。彼らは本を書かない。本書いているうちに技術が古くなる。
    • しかし、ソフトウェアの作り方は変わってない。テスト駆動開発とかリファクタリングとか。日々のプログラミングを支えるテクニックは書籍が支えていた時代をおさえておくと良い。

現役プログラマでいるために

毎日コードを書く

  • twadaさん、今は40歳。プロとしてコードを書いてお金をもらい始めたのは20歳くらい。
  • John Resig、jQuery作者。週末にコードを書くの頑張ろうとして失敗した。
  • 平日と同じ馬力で書けない。土日全部空いてるわけではない。Write Code Every Day.
  • 毎日コードを書くこと。コードを書いてからブログ、ドキュメント書くこと。
  • 意味のあるコードを書くこと。可能ならばリファクタリングもカウントしない。
  • 日が変わる前に終わらせること。
  • 書いたコードをすべてgithubにpushしOSSにすること。
  • 必要最小限のコードへの集中
  • 30分とか1時間しか空いてない日もある。短いコードで意味のあるコードを書かないといけない。
  • プログラミングの習慣化。
  • 不安との戦い。進んでいるという実感は、実際の進捗と同じくらい大事。
  • 週末の過ごし方。他の日もコードを書いているので、土日に躍起にならなくなった。
  • バックグラウンドにコーディング状態ができた。
  • コンテクストスイッチがなくなった。
  • ワークライフバランスがよくなった。毎日続けるにはバランスが大事。
  • まわりからの理解。毎日コードを書くを公言した。
  • どれだけコードを書いたか。
  • 続けるコツ
    • 小さなイライラを無視しない(ちょっとした改良を続ける)
    • 有名になると自然に仕事が増える
    • 自分のためのツールをコツコツ作る
      • 勤太郎CLIとか
    • 住む場所を工夫する
      • 意図的にオフライン時間を作る。
      • やや遠くなっても良いので始発駅に住む。座れるから。
      • or 会社の近くに住む。

年下から学ぶ

  • 一生プログラマーでいれるかどうかは、言い換えれば年下から学べるか否か。
  • イノベーションは年下から起こる。
  • 世の中が変わったことに気づけなくなる
  • ベテランはベンチマークとアンラーニングが必要
    • 積極的に外部に出て自分のスキルを相対化する
    • 使う道具を定期的に変える
    • 若者から学ぶ
    • 若者と同じ土俵で競う
    • ペアプロ

過去から未来を学ぶ

  • 技術は振り子のように見えるが螺旋
  • デブサミ講演参照(これは僕も前聞いた)
  • 行ったり来たりしているから、過去のブームを知っていれば、今のブームは差分を見れば良い

人の作る渦を見る

  • githubの登場はインパクトでかかった
  • 組織の時代から個人の時代へ

大事なことに集中する

  • エッセンシャル思考
  • 自分の中で大事なこととそうでないこと

oracleで今日の日付を取得したり、関数をちょっと試したりする

背景

最近oracleを叩いている。今日の日付を取得する関数が知りたかったので調べた。

結論

select to_char(sysdate, 'yyyy/mm/dd') from dual

で今日の日付が取得できる。to_charの第二引数にフォーマット文字列を渡す。

from dual のdualはダミーテーブルらしい。oracleの全てのユーザーからアクセスできるダミーテーブル。ちょっと関数を試すときなんかは from dual でおk。

locustのNUM_CLIENTSとHATCH_RATEについて

背景

GAEで作ったAPIサーバーを本番反映するに当たり必要なリクエスト数さばけるのかを確認する必要があった。先輩社員がlocustを使っていたので自分もlocustを使ったが、NUM_CLIENTSとHATCH_RATEの挙動が分からな過ぎた。NUM_CLIENTSとHATCH_RATEの関係が分からなかったし、locustを動かしているとAll locusts hatchedというログを残して計測した情報が一旦リセットされる理由もわからなかった。

結論

NUM_CLIENTSは同時接続数でサーバーでさばきたい接続数でOK。

問題はHATCH_RATEでこいつはクライアントを毎秒いくつ作るかを指定する。そしてNUM_CLIENTSで指定した数までクライアントを作ったら統計情報がリセットされる。

つまり最初クライアントは0で、HATCH_RATEで指定した数だけクライアントが作られていくことで、接続数が徐々に増えていく状況をシミュレート出来る。これによりオートスケールの設定が正しく動いているかが確認できる。また、本当に計測したいのは"オートスケールした後のピークリクエスト時に正しくリクエストを捌けるか"なので統計情報がリセットされる。

docker multi stage buildを試す

背景

reviewdogのDockerfileについて先輩社員にレビューもらうと、dockerのmulti stage build使うと幸せになれるよと教えてもらった。

レビューをもらったDockerfileは以下。pythonのdockerイメージにgoをインストールしてからgo getでreviewdogを入れている。これをまずgoのイメージでreviewdogをgo getしてきて、pythonのイメージにコピーしてくればいいんじゃないの?という意図で言ったのだと思う。

FROM python:3.6

USER root
ENV HOME /root

# install Golang
ARG GO_VERSION=1.8.3
RUN mkdir /tmp/go && \
    cd /tmp/go && \
    curl -O -sS -L https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz && \
    tar zxf go${GO_VERSION}.linux-amd64.tar.gz && \
    mkdir /usr/local/go && \
    mv go/* /usr/local/go && \
    rm -rf /tmp/go
ENV GOPATH=${HOME}/go
ENV PATH=${PATH}:/usr/local/go/bin:${GOPATH}/bin

# install reviewdog
RUN go get github.com/haya14busa/reviewdog/cmd/reviewdog
ENV GITHUB_API="https://ghe.misosiru.io/api/v3/"
ARG GITHUB_API_TOKEN=""
ENV REVIEWDOG_GITHUB_API_TOKEN=${GITHUB_API_TOKEN}

# リポジトリをコンテナにマウント
ARG WORK_DIR
WORKDIR ${WORK_DIR}
ENV APP_ROOT ${WORK_DIR}
COPY . ${APP_ROOT}

# install flake8
RUN pip install flake8 flake8-import-order flake8-double-quotes flake8-print pep8_naming

結論

やってみた。こんな感じになった。goはバイナリに出来るので、ランタイムが不要になるのでこういうことが出来るのか。素晴らしい。golangのインストールとかGOPATHの設定がなくなっていい感じになりました。

FROM golang:latest as reviewdog
# install reviewdog
RUN go get github.com/haya14busa/reviewdog/cmd/reviewdog

# ------------------------------------------------------

FROM python:3.6

USER root
ENV HOME /root

# reviewdogの設定
COPY --from=reviewdog /go/bin/reviewdog /usr/local/bin/reviewdog
ENV GITHUB_API="https://ghe.misosiru.io/api/v3/"
ARG GITHUB_API_TOKEN=""
ENV REVIEWDOG_GITHUB_API_TOKEN=${GITHUB_API_TOKEN}

# install flake8
RUN pip install flake8 flake8-import-order flake8-double-quotes flake8-print pep8_naming

# リポジトリをコンテナにマウント
ARG WORK_DIR
WORKDIR ${WORK_DIR}
ENV APP_ROOT ${WORK_DIR}
COPY . ${APP_ROOT}

# 自前のflake8 pluginのインストール
RUN pip install flake8_ml_batch/

IntelliJでproject painとかをショートカットでリサイズする

背景

IntelliJと書いたがPyCharmもRubyMineでも同様。

Command+1でプロジェクトペインが開けるが、windowをガチャガチャリサイズしたあとだとプロジェクトペインのサイズが以上にデカかったり小さかったりする。マウスでサイズ調整してたが、キーバインド割り当てられてないのか?と思って調べた。

f:id:roronya:20180115134012p:plain

結論

Resize (tool) windows with keyboard | IntelliJ IDEA Blog

リサイズしたいペインにフォーカスを当てて、Shift+Command+カーソルキーでリサイズできた。

docker runでローカルのgoファイルを実行する

背景

RealWorldHTTPを読み始めた。goでサンプルのHTTPサーバを立ててcurlで色々オプションを付けてリクエストを試しながら挙動を確認していくようだった。

それで、サンプルのHTTPサーバーを書いたんだけど、dockerで実行したくて以下のようにした。

$ docker run golang go run server.go

が、当然docker runに渡すコマンドはコンテナ内で実行されるので、server.goはコンテナ内には無いのでこうなる。

stat server.go: no such file or directory

結論

まずdockerコンテナのワーキングディレクトリを調べる。

$ docker run golang pwd
/go

/goらしい

そしたらローカルのカレントディレクトリを/goにボリュームしながらrunする。

$ docker run -v $PWD:/go golang go run server.go

docker run -vに渡すパスは絶対パスでないとならないので.(ドット)で渡すとdockerに怒られるため$PWDとするしかない。

あとはdocker-composeのvolumesを使う方法もあるけど、そのためだけにdocker-compose.ymlを書くのも馬鹿らしいので上でやることにした。

drone.ioでdocker run -vしてハマった

背景

drone.ioでreviewdogを使いたくてこんな感じで書いた。

Dockerfile.reviewdog

FROM python:3.6

USER root
ENV HOME /root
ARG WORK_DIR
WORKDIR ${WORK_DIR}
ENV APP_ROOT ${WORK_DIR}

# install Golang
ARG GO_VERSION=1.8.3
RUN mkdir /tmp/go && \
    cd /tmp/go && \
    curl -O -sS -L https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz && \
    tar zxf go${GO_VERSION}.linux-amd64.tar.gz && \
    mkdir /usr/local/go && \
    mv go/* /usr/local/go && \
    rm -rf /tmp/go
ENV GOPATH=$HOME/go
ENV PATH=$PATH:/usr/local/go/bin:$GOPATH/bin

# install reviewdog
RUN go get github.com/haya14busa/reviewdog/cmd/reviewdog
ENV GITHUB_API="https://ghe.misosiru.io/api/v3/"
ARG GITHUB_API_TOKEN=""
# build時にGITHUB_API_TOKENを渡す
ENV REVIEWDOG_GITHUB_API_TOKEN=$GITHUB_API_TOKEN

# install flake8
RUN pip install flake8 flake8-import-order flake8-double-quotes flake8-print pep8_naming

.drone.yml

pipeline:
  reviewdog:
    image: drone-build:latest
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      - WORK_DIR=/usr/src/app
    commands:
      - export GITHUB_API_TOKEN="GITHUB_API_TOKEN"
      - docker build --build-arg WORK_DIR=$WORK_DIR --build-arg GITHUB_API_TOKEN=$GITHUB_API_TOKEN -t reviewdog:latest -f Dockerfile.reviewdog .
      - docker run -v $PWD:$WORK_DIR reviewdog:latest /bin/bash -c "flake8 . | reviewdog -ci=droneio -f=pep8"
    when:
      event: pull_request

だが、reviewdogがreviewをしてくれない。(flake8が文句を言うコードはリポジトリに含まれている)

docker run -v $PWD:$WORK_DIR reviewdog:latest /bin/bash -c "flake8 ."

としてもflake8も何も出力してくれなかった。

仮設

docker run -v $PWD:$WORK_DIR reviewdog:latest ls

としたが、droneのログに出力されなかったので、docker run -vが何らかの理由で効いておらずマウントされていないのでないかと考えた。

結論

https://stackoverflow.com/questions/31381322/docker-in-docker-cannot-mount-volume

A Docker container in a Docker container uses the parent HOST's Docker daemon and hence, any volumes that are mounted in the "docker-in-docker" case is still referenced from the HOST, and not from the Container.

Dockerコンテナの中で動かすDockerデーモンはHOSTのものらしい。なので、docker run -v で渡すファイルパスはHOSTのものになってしまっているとのこと。

なので、droneではdocker in dockerは使わないで予めDockerfile.reviewdogの中でCOPYをしてから、prepareでdocker buildするようにしました。

Dockerfile.reviewdog

FROM python:3.6

USER root
ENV HOME /root
ARG WORK_DIR
WORKDIR ${WORK_DIR}
ENV APP_ROOT ${WORK_DIR}

# install Golang
ARG GO_VERSION=1.8.3
RUN mkdir /tmp/go && \
    cd /tmp/go && \
    curl -O -sS -L https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz && \
    tar zxf go${GO_VERSION}.linux-amd64.tar.gz && \
    mkdir /usr/local/go && \
    mv go/* /usr/local/go && \
    rm -rf /tmp/go
ENV GOPATH=$HOME/go
ENV PATH=$PATH:/usr/local/go/bin:$GOPATH/bin

# install reviewdog
RUN go get github.com/haya14busa/reviewdog/cmd/reviewdog
ENV GITHUB_API="https://ghe.misosiru.io/api/v3/"
ARG GITHUB_API_TOKEN=""
# build時にGITHUB_API_TOKENを渡す
ENV REVIEWDOG_GITHUB_API_TOKEN=$GITHUB_API_TOKEN

# install flake8
RUN pip install flake8 flake8-import-order flake8-double-quotes flake8-print pep8_naming

COPY . $APP_ROOT  # ここを追加

.drone.yml

pipeline:
  prepare:  #prepareでdocker buildをしてしまう
    image: drone-build:lates
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      - WORK_DIR=/usr/src/app
    commands:
      - export GITHUB_API_TOKEN="GITHUB_API_TOKEN"
      - docker build --build-arg WORK_DIR=$WORK_DIR --build-arg GITHUB_API_TOKEN=$GITHUB_API_TOKEN -t reviewdog:latest -f Dockerfile.reviewdog .

  reviewdog:
    image: reviewdog:latest
    environment:
      - flake8 . | reviewdog -ci=droneio -f=pep8
    when:
      event: pull_request