行動すれば次の現実

ほどよくモダンなシステム開発を目指しています。メインテーマは生産性、Ruby、Javascriptです。

私が行っている「1週間の振り返りと計画」の具体的な方法

振り返りの時間とはなにか?

私は毎週月曜日の夜に1時間程度の振り返りの時間を設けている。 先週1週間(火~月)の学びと反省、今週1週間(火~月)の計画を行い、自分としっかり向き合うことにしている。

忙しい日々を送っていると、つい目の前のことで手一杯になり視野が狭くなってしまう。 強制的に振り返りの時間を設けることで人生を俯瞰することができ、目まぐるしく発生する日々の物事を根拠を持ってコントロールできると考えている。

振り返りはどこでやるのか?

基本的には「自宅以外」で行うと良い。 私の場合は最寄りのカフェである。 なぜ自宅以外なのかというと、集中力の向上と非日常感での思考の転換というのが一番の理由である。

それぞれメリットとデメリットをあげる。

カフェで振り返りをするメリット・デメリット

〇メリット
  • 非日常の環境に身を置ける
  • 余分なものを持ち込まないため集中できる
〇デメリット
  • カフェ代が掛かる
  • カフェに行くまでの往復時間が掛かる
  • 気楽に離席できない

自宅で振り返りをするメリット・デメリット

〇メリット
  • その気になったら、いつでも振り返りができる
  • 備品を持ち歩かなくても、家にあるものは全て使用できる
  • 費用が掛からない
〇デメリット
  • いつもと同じ環境なので非日常感がない
  • いろいろなものが目に入りため、気が散る
  • 家族のスケジュールに影響され、好きな日程が選べない

振り返りに持っていくもの

以下のアイテムを持参する

  • ペン、ノート
  • タブレット
  • スマホ

ペン、ノート

頭の整理をしながら振り返りをする。 ゼロ秒思考でメモを取ったり、図解しながら頭の整理をする際にペンとノートを使用する。

タブレット

振り返りのメインはタブレットを使用する。 後述するtodoistやmindmeisterアプリを使って振り返りを行うために使用する。

スマホ

スマホは基本的にはオフラインモードにする。 だったら持っていかなくても良いのでは?と思うのだが災害時などの緊急用として仕方なく持参するのである。

振り返りの手順

ここからが本題である。 振り返りは大きく3ステップである。

  1. 先週やったことを振り返る
  2. 今気になってることを書き出す
  3. 今週やることを計画する

1.先週やったことを振り返る

まずは先週やったことを思い出す作業である。 各媒体に振り返るための材料が残っているため、それらをもとに振り返る。

todoistを振り返る

後述するが、タスクはtodoistで管理している。 todoistの「完了したタスク」で過去1週間に自分が何を行ったのかを確認し、その時の状況などを頭の中に思い出す。

カレンダーを振り返る

先週のカレンダーに記載されているイベントを元に振り返る。

メールを振り返る

先週受信したメールをざっと振り返る。 未読メールもここでチェックして、必要なければアーカイブまたはゴミ箱行きする。

Twitterのいいねを振り返る

Twitterでは自分のツイートと、いいねしたツイートを振り返る。 自分がどんなツイートにいいねしたのかを振り返る。

Youtubeのいいねを振り返る

自分がどんな動画にいいねしたのかを振り返る。

2.今気になってることを書き出す

上記の振り返り材料を元に、今気になっていることを改めてノートに書き出す。

気になっているを書き出す

今気になっていることをノートに箇条書きで列挙する。 気になっていることをさらに深堀りたい場合は、ゼロ秒思考で深堀りをしていく。

気になっていることがある程度明確になった時点でtodoistのinboxに追加しておく。

ゼロ秒思考については下記参照

3.今週やることを計画する

いつかやるリストを確認する
  • いつかやるリストの内、今週やりたいものがあれば期限を設定してtodoistの適当なプロジェクトに移動させる。
  • いつかやるリストの内、やる必要がないと思ったものは削除する
mindmeisterで価値観マップを確認する
  • mindmeister上の価値観マップを確認する
  • 価値観マップを確認して今週やりたいことがあればtodoistの適当なプロジェクトに移動させる。
  • 価値観マップとは人生で大切にしたいモノ・コトを以下の観点ごとにまとめたマインドマップである
    • 精神生活
    • 健康
    • プライベート
    • 人間関係
    • 仕事
    • 会社
    • 資産
  • 価値観マップの作成方法はワン・シングという書籍に詳しく書いてある
    • 「11 成功の習慣」を参照
今週のカレンダーを確認する
  • 今週のカレンダーを確認してtodoistの適当なプロジェクトに移動させる。
inboxを確認する

todoistのinboxに追加されたタスクを確認する。

  • 2分以内でできるなら今すぐやる。
  • 今週やりたいなら期限を設定してtodoistの適当なプロジェクト(※)に移動させる。
  • 誰かがボールを持っていてpendingしているなら「連絡待ち」のプロジェクトに移動させる。
  • やる必要がないならタスクを削除する。
  • いつかやりたいならtodoistの「いつかやる」のプロジェクトに移動させる。

※ 適当なプロジェクトとは以下のいずれかである

  • 精神生活
  • 健康
  • プライベート
  • 人間関係
  • 仕事
  • 会社
  • 資産

まとめ

以上が振り返りの全容である。

1.先週やったことを振り返る 2.今気になってることを書き出す 3.今週やることを計画する

の3ステップを1週間おきに回していくという内容である。

この振り返りのフレームワークは、主に以下の書籍や手法を元に独自で編み出したものである。 参考としてリンクをまとめておくので読んでおくとフレームワークの本質が把握できるかと思う。

参考書籍
  • はじめてのGTD ストレスフリーの整理術
  • 新版 ザ・マインドマップ(R)
  • ゼロ秒思考 頭がよくなる世界一シンプルなトレーニング
  • ワン・シング 一点集中がもたらす驚きの効果
使用ツール
  • evernote
  • todoist
  • mindmeister
  • ノート
  • ペン

Javascriptによる画像リサイズ処理の実装例(blueimp-load-imageなど)

画像アップロード処理をする際、昨今ではフロントサイドでもリサイズするのが主流である。

詳しくはこちらの記事を参照( 画像アップロード時はクライアントサイドでも圧縮処理をすべき理由 - SaaS開発者のあれこれ

Javascriptcanvas APIを使って実装するのが一般的なのだが、canvasで画像圧縮処理を一から実装とそれなりのコーディング量が必要である。

画像圧縮に関するJavascriptライブラリは山程あるので、一から実装するのは車輪の再開発感が否めない。

そのため、今回は既存のJavascriptライブラリを使用した実装例を記そうと思う。

blueimp-load-imageを使った実装例

blueimp-load-imageというnpmライブラリを使用して画像圧縮処理を実装する。

github.com

blueimp-load-imageは画像を加工したり、回転情報などを考慮してプレビュー画像を作成したりが簡単に実装できるライブラリである。

以下実装例

import * as loadImage from 'blueimp-load-image';

const resizeImageWorker = (imageFile, options = {
  maxHeight: 828,
  maxWidth: 1104,
  canvas: true,
  orientation: true,
}) => {
  return new Promise((resolve) => {
    loadImage(imageFile, (canvas) => {
      const type = 'image/jpeg';
      const dataUri = canvas.toDataURL(type);
      const bin = atob(dataUri.split(',')[1]);
      const buffer = new Uint8Array(bin.length);
      for (let i = 0; i < bin.length; i++) {
        buffer[i] = bin.charCodeAt(i);
      }
      resolve(new Blob([buffer.buffer], { type: type }))
    }, options);
  });
}

function createProductImages(payload) {
  const { productRecipeId, productImage } = payload;
  return resizeImageWorker(productImage).then((blob) => {
    const params = new FormData();
    params.append('product_image', blob);
    return axios.post(`/api/product_recipes/${productRecipeId}/product_images`,
      params, {
        headers: {
          'content-type': 'multipart/form-data',
        },
      });
  })
}

function* postProductImages(action) {
  const response = yield call(createProductImages, action.payload);
  yield put(actions.putProductRecipesSuccess(response.data.data));
}

肝となるのはresizeImageWorkerの部分である。

postProductImagesとcreateProductImagesはRedux sagaの処理なので読み飛ばして問題ない。

blueimp-load-imageのloadImageという関数を使用してリサイズ処理を行っている。 loadImageは非同期処理なのでPromise化して、resolveでBlobを返却するという作りにしている。

resizeImageWorkerで返却されたBlobを画像アップロードAPIに送信することで処理が完了。

たった10行ほどのコードでリサイズ処理が実装できてしまうのである。

他に候補に上がったライブラリ

画像リサイズ処理を実装するにあたり他にいくつかライブラリを試してみた。

要件に合致すれば以下のライブラリを使用するのもありかと思う。

imtool

github.com

IE11はサポート対象外であるためNGとした。

実際に導入したところかなり使いやすかったので、IEを捨ててもよいのであればおすすめできる。

jimp

github.com

commonJS方式のみサポートなのでNGとした。

commonJSをサポートしている環境であればかなり使いやすそうである。

pica

github.com

commonJS方式のみサポートなのでNGとした。

commonJSをサポートしている環境であればかなり使いやすそうである。

画像アップロード時はクライアントサイドでも圧縮処理をすべき理由

画像アップロードする際に、最近ではクライアントサイドであらかじめサイズを圧縮しておくのが主流である。

スマホカメラの高性能化に伴い、写真がどんどん高画質になっているためである。 10MB近いサイズのこともあったりする。

画像アップロードのフローからみる改善点

スマホ ①=> サーバーサイド ②=> ファイルストレージ

上記の①のアップロード部分はクライアント側のネットワーク速度に依存するため、通信が不安定な状態だといつまでたってもアップロード処理が終わらない。 (10MBの画像を上り1Mbsの速度でアップロードすると10秒以上かかる)

②はサーバーサイドなので通信による影響はある程度制御できる。 安定した状態であれば10MB程度のアップロードは1秒未満で完了するためそれほど問題にはならない。

画像サイズを1/10の1MBまで圧縮できれば、①の処理は1秒程度で完了する。

それゆえにクライアントサイドで画像を圧縮した上でアップロードすることがとても重要なのである。

(保険としてサーバーサイドでも圧縮処理を掛けておくべきであるのは言うまでもない)

Reactのライフサイクル一覧まとめ

マウントフェーズ

componentWillMount

  • 基本的には非推奨
    • まずはconstructorかcomponentDidMountで制御できないか検討すること
    • v16からUNSAFE_componentWillMountという名前に変更
  • renderよりも前に呼ばれる
  • constructorよりも後に呼ばれる
  • setStateしてもrenderはされない

componentDidMount

  • コンポーネントがマウントされた直後に呼ばれる
  • DOMにアクセスできるため、イベントリスナー登録などはこのフェーズで行う
  • setStateは原則禁止
    • render → componentDidMount → setState → render
    • 無駄に2回描画されるので特別な理由がない限りは禁止

componentWillUnmount

  • コンポーネントがアンマウントされる直前に呼ばれる
  • setIntervalをクリアしたり、イベントリスナーを解除したりするフェーズ

アップデートフェーズ

componentWillReceiveProps(nextProps)

  • 基本的には非推奨
    • v16からUNSAFE_componentWillReceivePropsという名前に変更
  • setState可
  • propsの変更直前に呼ばれる
  • stateの変更では呼ばれない

shouldComponentUpdate(nextProps, nextState)

  • renderさせるかどうかを制御する
    • PureComponentを使用するほうが一般的
  • true/falseが返却値
    • trueの場合はrenderさせる
    • falseの場合はrenderさせない
  • 主にパフォーマンスチューニングのために使用する

componentWillUpdate(nextProps, nextState)

  • 基本的には非推奨
    • v16からUNSAFE_componentWillUpdateという名前に変更
  • renderされる直前の最後のフェーズ
  • setStateは禁止
    • componentWillUpdateが再度実行されて無限ループの可能性があるため
  • shouldComponentUpdateでfalseを返した場合は呼ばれない

componentDidUpdate(prevProps, prevState, snapshot)

  • 仮引数は前のprops、state
  • renderの直後に呼ばれる
  • setState可
  • shouldComponentUpdateでfalseを返した場合は呼ばれない

Herokuのworker dynoが突然遅くなったときに確認すべきこと

Herokuのworker dynoのパフォーマンスが数日前から突然悪くなった。

今まで30秒程度掛かっていたバックグラウンド処理が10分程度掛かるようになった。

直近で心当たりのあるリリースはなかったため、調査することにした。

丸一日調査して解決はしたのだが、思っていたよりも根が深く、自身の学びにもなったのでまとめておく。

前提情報

  • Rails5.2
  • Jobフレームワーク: Sidekiq

  • Heroku

    • web dyno * 2
    • worker dyno * 2 (内1つはcron用)
  • DB

    • posgresql
  • 監視ツール

    • Papertrail
    • NewRelic

Papertrailでworker dynoのログを確認する

まずはログを確認する。Pepartrailを入れているので、問題が発生したJobIDをもとにログを特定する。 (各処理の実行時間などから問題の原因をある程度推測できるケースが多い)

ActiveRecordが発行しているログからDBへのアクセス時間が極端に遅くなっていることがわかった。 通常時はテーブルのUpdateに1ms程度掛かっていた処理が、100ms程度掛かっていることがわかった。

「DBアクセス周りになにか原因があるのではないか?」と推測する。

NewRelicでパフォーマンスを確認する

念の為NewRelic APMも確認する。 CPUやMemoryの状況に異常は見られなかった。 Transaction traceを確認するとDB Queryのパフォーマンスに異常はなく、アプリケーションコードの実行速度が極端に遅いことがわかった。

ActiveRecordの処理時間は生のDB Query以外の処理時間も含まれている。

「DBが原因ではない」と推測する。

DBが原因ではないことの確認

念の為web dynoからのアクセス時間を図ってみたところ、異常は見られなかった。

  • webからのアクセスは遅くない
  • workerからのアクセスのみ遅い

上記よりDBが原因ではないことがわかった。

「workerで動くアプリケーションに原因があるのではないか?」と推測する。

デバッグログを仕込む

workerで実行しているJobアプリケーションに対してデバックログを仕込んだ。

デバッグを仕込む際は、

  1. 大きめなメソッド単位でログを仕込んで、どこでメソッド時間がかかっているか特定する
  2. 時間が掛かっているメソッドが特定できたら、ドリルダウンで情報が把握できるようにあらに詳細なログを仕込む

というステップを踏む。

1の段階で全体的に遅いということがわかった。

「特定のコードが遅いというわけではなくプロセス自体が遅いのではないか?」と推測する。

Herokuのmetricsを確認

原因がわからず小一時間息詰まっていたが、初心に戻ってHerokuのMetricsタブを確認することにした。

するとDyno Loadが常に1以上稼働していることがわかった。

dyno load
dyno load

試しにworker dynoをもう一つ起動してjobを実行してみると もう一つのworkerに割り振られたJobは通常通りのパフォーマンスで処理を終えたことが確認できた。

「デーモンプロセスが動いていて、dynoを専有しているのではないか?」と推測する。

Sidekiqの管理画面を確認

Sidekiqの管理画面を確認すると実行中のジョブプロセスが常に1あることが判明。 worker dynoで再起動してみる。するとSidekiqの性質上、自動でリランが走った。 リランされたジョブがなぜか終了されることなく、デーモン化していることがわかった。

「アプリケーションコードで無限ループしているのではないか?」と推測する。

コードを確認

対象クラス内で無限ループが発生しそうなコードがないか調査したところ、whileを使用している処理が一箇所見つかる。 ある特定の条件の場合、whileを抜けられずに無限ループしてしまうことが判明。

原因は、

「アプリケーション内の無限ループによりworker dynoを専有していたこと」

【超簡単】HerokuのpostgresDBを別環境にコピーする方法

本番環境のDBをステージング環境などにまるっとコピーする方法をまとめる。

前提条件

  • S3バケットを持っていること
  • b002というバックアップデータをprod-appからstg-appにデータをコピーするストーリーになっています

1. コピー対象のバックアップを取る

下記コマンドでDBのバックアップデータを作成する

heroku pg:backups:capture -a prod-app

2.バックアップデータをローカルにダウンロードする

heroku pg:backups:download b002 -a prod-app

3.S3バケットにバックアップデータをアップロードする

  • 署名付きURLを発行する or データをそのまま公開してしまう

4.コピー先のDBを初期化する

heroku pg:reset DATABASE -a stg-app

5.コピーを実行する

heroku pg:backups:restore 'アップロードしたS3のURL' DATABASE_URL -a stg-app

6.S3からdumpを削除する

  • 本番データの場合は削除しておきましょう

以上。

パッケージシステムのカスタマイズ対応をどう管理するか

弊社ではクラウド型のWebアプリケーションパッケージシステムを開発している。 基本的にはパッケージ機能のみを顧客に使用してもらいたいのだが、当然のようにカスタマイズ要望が生まれる。

カスタマイズ要望は、往々にして現場よりの要件で顧客と密結合である。 そのため、パッケージとして素直に取り入れることのできないケースが多々ある。

そのような場合にどのようなアプローチがあるのか以下に記す。

顧客ごとにブランチを分ける

パッケージとしての本ブランチ(master)と顧客ごとにブランチを作成する方法。

メリット:他顧客に影響なくカスタマイズしほうだい。
デメリット: ブランチの管理が大変。masterへのマージ、各ブランチへのマージやらでどんどん脆い作りになっていく傾向。

マイクロサービス化する

カスタマイズ機能をマイクロサービス化する。パッケージとはコードベースが別になり、サーバーなども別に用意する。

メリット:パッケージのコードベースが肥大化せずにすむ。影響範囲が絞られるのでメンテナンス性が高い。
デメリット:マイクロサービスとして切り出す上で、インフラまで影響が出る。セッション管理をどうするかなど、アーキテクチャをしっかり構築しておく必要あり。

フラグで頑張る

機能を使用するかどうかのフラグで処理を分岐させる。

メリット:一番手っ取り早い、コードベースが小規模段階ではスピード開発可能。
デメリット:コードベースが肥大化し、分岐処理によって複雑化する。がんじがらめになるのでデグレしやすい。

我々の結論

「フラグで頑張る」

自PJの場合、PMF前のプロダクトなので、スピード重視したいため。
メンテナンス性やデグレ懸念については単体テストである程度担保できると判断。
理想は「マイクロサービス化する」だが、環境を整える上でのリソース確保が課題であるため見送り。