emahiro/b.log

Drastically Repeat Yourself !!!!

Intellij で goappコマンドを動かす

※ 作業ログです。自分の環境で動作させたので再現性があるかは不明です。

事前準備

使用しているshell(bash, zsh, fish..etc)でgoappコマンドにpathが通ってる状態であること。

Intellij上の設定

$ which goapp
# ここでpathが通っていれば問題ない。

pathが通っていない時...
Intellij上でterminalを開き echo $PATH する。

この時何も設定していないと /usr/sbin /sbin /usr/local/sbin のみpathが通っている....はず。 なので自分の使ってるshellの設定ファイルで 上記の3つにもpathを通す。

Intellijを再起動する。

$ which goapp
/HOME_PATH/google-cloud-sdk/platform/google_appengine/goapp

通っているっぽかった。

おしまい...(これであってるのか....)

TypeScript の Optional Property

TypeScriptにはObjectのもつpropertyに Optional Property という機能があります。

propertyに ? をつけることでそのpropertyがない場合もあるよということを表現することができます。

公式のドキュメントには以下のように記載されています。

  • Not all properties of an interface may be required. Some exist under certain conditions or may not be there at all.

ref. https://www.typescriptlang.org/docs/handbook/interfaces.html

あってもなくてもどちらの場合も許容する、propertyを宣言するときに使います。

それだけだと「じゃあそんなpropertyを明示的に宣言する必要があるのか?」とか「正規でpropertyを定義しておけばいいじゃん」となりますが(僕もそう思いました)、例えば以下のような必須でない補足情報を追記したり(case1)、propertyが入ってるケースにおいて型を制限したり(case2)するときに使います。

case1: 何か付加情報を追記したいが、それはoptionalな値であること

interface Person {
  name: string // 必須
  age: number // 必須
  extra?: object // optional
}

case2: propertyが入ってるケースに置いて型を制限する

interface UserAgent {
  os?: 'mac' | 'win'
}

このケースでは os propertyは 存在しないかもしれないが、存在する場合は mac or win という文字列のみ許容する という表現になります。

どちらのケースにおいても別に ? をつけなくてもいいと思いますし、つけない方がinterfaceの構造を厳密に定義することができます。

しかし、propertyがないcaseにinterfaceの構造レベルでコンパイルエラーが検出されますし、それだけのためにいらないproperty込でインスタンス化するようなことにもなると思います。

// case1
const person: Person = {name: 'bob', age: 20} //OK

// case1': extra propertyをrequiredにする
const person: Person = {name: 'bob', age: 20} //Error
const person: Person = {name: 'bob', age: 20, extra: {}} //OK

Optional Propertyを使わないケースでのPersonオブジェクトのインスタンス化は冗長です。

また、可読性の観点からも 存在しないかもしれない値だとinterfaceを見ればわかります。

Optional Propertyは Typescriptの表現力の高さを感じさせるものでした。

『ファクトフルネス』を読んだ

『ファクトフルネス』を読んでとても面白い書籍だったので、学んだことの整理もかねて備忘録として書録を記載します。

ファクトフルネスに世界を捉えること

本書の中に書かれたことを自分なりに整理すると「事実に基づいて世界を見ることを阻害する人間の持つ10の本能」を コントロールすること だと思います。
コントロールとしたのは、ファクトフルネスを阻害する本能は、文字通り本能なので、全くなくすことはできません。しかし、何か本能を刺激する事象に触れた時に、出てこようとする本能をコントロールすることはできます。
本能が前に出てきたなと感じたら、一呼吸置いて、俯瞰して目の前の事象に対して客観的に事実に基づいて見ることを意識すること、それが無意識でできるようになることなのかなと本書を読んで感じました。

以下は「事実に基づいて世界を見ることを阻害する人間の持つ10の本能」とそれにどう対処するかについて、本書に記載されてる内容をまとめました。

分断本能

世界は分断されているという思い込み。

ex.

  • 先進国/後進国
  • 西欧諸国/それ以外

ファクトフルネスに考える

大半の人間がどこにいるのかを探す。

  • 話の中の「分断」を表す言葉 に気づくこと。
    • 多くの場合は、分断はなく、誰もいないと思われていた中間層に大半の人がいる。
    • 大半の人がどこにいるのか? を探すこと。
  • 「平均の比較」 に注意する。
  • 「極端な数字の比較」 に注意する。
  • 上からの景色 であることに注意する。
    • 上から観ると同じ「低い」でも下から見ると一段階違うと全く違う。

ネガティブ本能

「世界はどんどん悪くなっている」という思い込み。

ファクトフルネスに考える

悪いニュースの方が広まりやすいと覚えておく。

  • ネガティブなニュースに気づく。
    • ネガティブなニュースは耳に入りやすい。
  • 「悪い」と「良くなっている」は両立する。
  • 良い出来事はニュースになりにくい。
  • ゆっくりとした進捗はニュースになりにくい。
  • 悪いニュースが増えても、悪い出来事が増えたとは限らない。
  • 美化された過去に気をつける。

直線本能

「ひたすら〇〇し続ける」という思い込み。

ファクトフルネスに考える

直線もいつかは曲がることを覚えておく。

  • 「グラフはまっすぐになるだろう(線形に進む)」という思い込みに気づくこと
    • 線形に進み続ける方が珍しい
  • 直線のグラフばかりを当てはめないようにする。
    • S字、滑り台、コブ、倍増、いろんなグラフの形がある。

恐怖本能

危険でないことを「恐ろしいこと」と考えてしまう。

ファクトフルネスに考える

リスクを計算する。

  • 恐ろしい物事には自然と目がいってしまうことに気づくこと。
    • 恐怖と危険は異なる。
  • 世界は恐ろしいと思う前に、現実を見ること。
    • メディアや自分自身の関心フィルターのせいで世界は実際より恐ろしく見えてしまう。
  • リスク = 危険度×頻度質×量
    • 恐ろしさはリスクとは違う
  • 行動する前に落ち着くこと。

過大視本能

目の前の数字が一番重要だという思い込み。

ファクトフルネスに考える

出てきた数字を比較する。

  • ただ1つの数字が、とても重要であるかのように勘違いしてしまうことに気づくこと。
    • 比較、割り算など他の指標を駆使して別の意味を見いだせる。
  • 比較すること。
  • 80:20ルールを使うこと。
  • 割り算をすること。
    • 量それ自体よりも大抵は「割合」の方が役に立つ。

パターン化本能

1つの例が全てに当てはまるだろうという思い込み。

ファクトフルネスに考える

分類を疑う。

  • 1つの集団のパターンを根拠に物事が説明されていたら、それ自体に気づくこと。
    • パターン化は間違いを産みやすい。
    • パターン化本能をコントロールするには、分類を疑うこと
  • 同じ集団の中にある違いを探すこと。
  • 違う集団の中の共通項を見つけること。
    • ex.) 女性一人当たりの子供の持つ人数は地域が違えど関係する指標は所得である。
  • 違う集団の間の違いも探そう。
    • ex.) 意識のない兵士(成人)と眠っている赤ちゃんへの対応。
  • 過半数」に気をつけること。
  • 強烈なイメージに注意すること。
  • 自分以外アホだと決めつけないこと。

宿命本能

全てはあらかじめ決まっているという思い込み。

ファクトフルネスに考える

ゆっくりした変化でも、変化していることを心に留めておく。

  • 色々な物事(ヒト、国、宗教、文化)が変わらなく見えるのは、日々の変化がゆっくり少しずつ起きているからだと気づくこと。
  • 小さな進捗を追いかけること。
  • 知識をアップデートすること
  • おじいさんやおばあさんにも話を聞くこと。
  • 文化が変わった例を集めること。

純化本能

世界は1つの切り口で理解できる、という思い込み。

ファクトフルネスに考える

1つの知識が全てに応用することはできない。

  • 1つの視点だけでは世界は理解できないことを知ること。
    • なんでもトンカチで叩くのではなく、様々な道具の入った工具箱を準備した方が良い。
  • 自分の考え方を検証すること。
  • 知ったかぶりはやめること。
  • やたらめったらトンカチを振り回すことをやること。
  • 数字は大切だが、数字だけに頼らないこと。
  • 単純なものの見方と、単純な答えには注意すること。
    • シンプルであることと、簡単であることは違う。

犯人探し本能

誰かを責めればはものごとは解決する、という思い込み。

ファクトフルネスに考える

誰かを責めたところで、問題は解決しない。

  • 誰かが見せしめとばかりに責められていたら、それに気づくこと。
    • 誰かを責めると他の原因に目がいかなくなり、将来起こるであろう同じ間違いを防げなくなる。
  • 犯人ではなく、原因を探すこと。
  • ヒーローでなく、社会を機能させている仕組みに目を向けること。

焦り本能

今すぐ手を打たないと大変なことになる、という思い込み。

ファクトフルネスに考える

小さな一歩を重ねる。

  • 「今すぐ決めないいけない」と感じたら、自分の焦りに気づくこと。
    • そもそも、今(その時)決めないといけないことは滅多にない。
  • 落ち着くこと
    • 深呼吸、深呼吸。
  • データにこだわること。
  • 占い師に気をつけること。
  • 過激な対策に注意すること。

まとめ

紹介されていた本能は自分も働いてることが今まで多々ありました。特に、

  • 極端な2つの集合に分けるのではなく、レベル1~4の段階でそれぞれ集合があること。そして、中間があることを念頭に置くこと。
  • ある1時点の数字や現実の結果だけに目を向けず、過去からの進捗をみること。
  • 今時点で「悪い」ことと現在進行形で「良くなっている」は両立すること。

この3つはこの本を読んで「確かに!」と気付かされたことでした。どうしても、広告やテレビを始めたとしてマスメディアの持ち出すドラマティックなメッセージやストーリー性に引きづられてしまって、思考停止して事実を正確に捉えられないことがあります。
正直これ自体はこのエントリの冒頭でも記載した通り、本能なのでなくすことはできませんが、本能の活動を意識的にコントロールするための材料を手に入れることができたと思います。

身の回りに溢れている、極端な分断的表現、ドラマティックなストーリーや単一の数字には注意深くなるきっかけを与えてくれるいい書籍でした。

Intellijでlspを使う

lsp(Language Server Protocol) とは?

The Language Server protocol is used between a tool (the client) and a language smartness provider (the server) to integrate features like auto complete, go to definition, find all references and alike into the tool

cf. Langserver.org

自動補完、定義ジャンプ、参照の検索などの機能を各ツール(editor or IDE and so on...)に統合するために、ツール(クライアント)と言語機能プロバイダ(サーバー)間でおしゃべりできるようにする共通仕様です。
共通仕様になってるから今までは自動補完始め開発支援系のライブラリが色んな言語、IDE独自で開発しなくて良くなるのだと思います。

cf. Big Sky :: gocode やめます(そして Language Server へ)

Intellijにlspを導入してみた

公式でプラグインが出てるので入れてみました。
ほとんどGitHubに記載されてる内容ですが、Goで実際に試してみます。

github.com

手順

Preference > Pluginでlsp関連のプラグインを検索してインストール、再起動をします。

f:id:ema_hiro:20190111025121p:plain

以下の項目があればOKです。

f:id:ema_hiro:20190111025141p:plain

ドキュメンテーション

これ思いの外便利です。まず定義にhoverするとhoverした定義のドキュメントが表示されます。ショートカットはデフォルトでは ^ + J です。 さらにhoverした状態で '^ + J' を押すと右側に詳細なドキュメントが表示されます。

ドキュメントの表示設定は Preference > Editor > Generalの下記の場所にチェックマークを入れます。

f:id:ema_hiro:20190111025806p:plain

実際に表示してみた結果が以下(net/httpパッケージで試してます。)

f:id:ema_hiro:20190111024809p:plain

f:id:ema_hiro:20190111024826p:plain

f:id:ema_hiro:20190111024907p:plain

コードジャンプ(定義ジャンプ)

別ウィンドウが開くようになりました。今まではコードの定義に対して Ctrl or Command + Click/ Ctrl or Command + N をすると定義の下部にずらっと一覧が出ましたが、別ウィンドウになったので検索性が向上したように思います。
ただ、その他は特にLSPを入れてIntellijがLanguage Server Clientになったからといって特段変わったことがあるようには感じませんでした。ショートカットも同じですし。

コード検査

Analyze -> Inspect CodeでLSPを使ってるかどうか確認できます。 検査すると検査結果一覧が下部に出ます。この辺もLSP使う前とあんまり挙動自体は変わっていないと思います。

f:id:ema_hiro:20190111025854p:plain

まとめ

試しに使ってみましたが、あんまり導入前と変更点はなさそうでした。まだlsp自体も発展途上っぽいので今後に期待。

intellijのgoのプラグインではそもそも今現在LSPをサポートしていないので、この辺はクライアント(IDE)自体がLSP対応するプラグインを用意していても、言語側のプラグインが対応するのを待つことになると思います。

plugins.jetbrains.com

しかし、現時点でまだまだ機能が不十分とはいえ、ドキュメントをそのまま参照できるようになる機能だけでも良さそうでした。
IntelligのLSPの設定にはLanguage Serverの参照先追加設定とかもありましたが、この辺まだよく使い方わかってないので追々わかったら追記していこうと思います。

NuxtでTodoアプリを作成する

はじめに

気になっていたNuxtを触ってみました。
軽く触れて、ドキュメントを読んだだけでしたが今回は簡単なtodoアプリの作成をしてみます。

Nuxtは日本語のドキュメントがとても充実していて インストール - Nuxt.js を見ればほぼ誰でもNuxtを簡単に始めることができます(すごい)

SetUp

install

npx create-nuxt-app myNuxtTodos
# 略
Use a custom server framework (Use arrow keys) # Nuxt試すだけが目的なのでサーバーサイドFWは使わずにいきます。
❯ none
  express
  ...
Use a custom UI framework (Use arrow keys) # UI FWも特に使わずにいきます。
❯ none
  bootstrap
  vuetify
  ...
Choose rendering mode (Use arrow keys) # 今回はUniversalではなくSPAにします。
  Universal
❯ Single Page App
# その他は以下のような設定で行いました。
Use axios module yes
Use eslint yes
Use prettier yes
# あとは勝手にNuxtのプロジェクトが作成されます

起動方法

インストールシーケンスに書いてる通りです。devしか触るときは使ってません。

To get started:

  cd myNuxtTodos
  npm run dev

To build & start for production:

  cd myNuxtTodos
  npm run build
  npm start

ちょっとハマった

eslintをONにした状態でデフォルトでNuxtのアプリケーションを起動すると

/path/to/myNuxtTodos/pages/index.vue
  36:1  error  Delete `⏎`  prettier/prettier

✖ 1 problem (1 error, 0 warnings)
  1 error and 0 warnings potentially fixable with the `--fix` option.

というエラーにもしかしたら当たるかもしれませんが、これは書いてる通りに pages/index.vue ファイルで style 属性に改行が入ってることが問題なので該当行の改行を削除すれば問題なく起動します。

Vuexのモードを指定する

NuxtでVuexを使う場合、クラシックモードモジュールモード があります。

クラシックモードとは?

Vuex インスタンスを返すメソッドをエクスポートする store/index.js ファイルを作成します。ファイルとしては stores/index.js のみがあってこの中で Vuex をインスタンス化し、actions, mutations, getters が全て一箇所にまとまってる状態のファイルがあることを指します。

モジュールモードとは?

store ディレクトリ内にモジュールと対応するファイルを持つようにjsファイルを分割することです。 jsのファイル的には stores の中に分割するStateごとのjsファイルを作成します。

TODOアプリを作ってみる。

ドキュメントに沿って簡単なTodoアプリを作ってみます。ちなみに内容はVuexの章にあるTodoのほぼコピペです。Vuexはモジュールモードを使いました。

コードはこちらに上げてます。
il/myNuxtTodos at master · emahiro/il · GitHub

まとめ

ほぼドキュメントのコピペでしたが、Vue/Vuexの基本的なところがわかってる方にがサクッとvueのアプリを作ることができるなと感じました。 NuxtってVueでSSRを簡単にできるライブラリ?くらいの理解でしたがVueのアプリケーションを簡単に作成できるFWという説明がなんとなく理解できました。
今回は素のjsで書いたのですが、いつも業務で扱ってる技術スタックは Typescriptなので Nuxt/Typescriptを使う方 (公式ではすでに typescript-template/template at master · nuxt-community/typescript-template · GitHub があるけれど)を試してみたいと思います。

VuexにおけるGettersのメソッドスタイルアクセス

gettersでstateから任意の値を取り出すときにVuexではGettersのプロパティの返り値にfunctionを指定することで、Gettersのプロパティに関数の引数という形で直接値を渡す(アクセスできる)手法があります。これを メソッドスタイルアクセス と言います。

メソッドアクセススタイルについてはVuexの公式のGettersのドキュメントでも記載があります。

ref. ゲッター #メソッドスタイルアクセス | Vuex

サンプルコードは以下のようになります。

Gettersでのプロパティの定義

getters: {
  hogeById: (state) => (id) => {
    // 外部から直に私た変数をidに入れて使うことができる。
  }
}

呼び出す側

store.getters.hogeById(2) 
// or
this.hogeById(2) 
// getters.hogeById 内で使われる id には 2が入る。

typescriptで書き直すと...

定義元

const hogeGetters: GetterTree<HogeState, RootState> = {
    hogeById(state){
        return (id: number) => {
        // id: numberを使った何かしらの処理
    }
}

呼び出す側

this.hogeById(123) 
// hogeById内でのstateを何らかの条件を元にGetするPropertyで id=123 として直に値を扱うことができる。

VueのGettersそれ自体はオブジェクトを作成しています。

const hogeGetters: GetterTree<HogeState, RootState> = {
    hogeById(state) {
        return (id: number) => {
            // idを使った何らかの処理
        }
    }
}

typescriptの例で見るとわかりやすいですが、hogeGettersは {} なのでそれ自体はオブジェクトであり、そのオブジェクトが hogeById という関数(hogeGettersオブジェクトからみたらプロパティ)を持っています。
hogeByIdをはじめとしてgettersの関数には引数としてstate(他にもgettersやrootStateなどVuexの作法に則ったstateやgetters)を渡していますが、呼び出し元では通常stateを明示的に指定しません。これはVuexのGettersではstateを渡さずともどのstateに対して呼ばれたものなのか呼び出したコンテキストから自動的に解釈してくれているからだと思います。

例) HogeというStateをもつ画面においてにthis or store.getters でHoge StateのGettersが呼ばれた場合には必ずHogeをゴニョった結果(state)が得られます。

あるStateのもつGetterの関数が呼ばれた時に、その関数の返り値が関数(function)であると、その関数の引数を呼び出し側から指定した特定の値にstateの引数を通り越して、直でアクセスできるように解釈してくれるみたいです。
この辺、何でそうなるのか時間のある時にもう少し調べてみようと思います。(僕のフロントエンド力で調べられるかわかりませんが...)

このメソッドスタイルアクセス、非常に便利だし、ちょっと見慣れない形でありますが、慣れるととても綺麗にGettersで特定の値を扱えるなと思いました。
ただ、参考文献はVuexの公式ドキュメントくらいしかなく、ググってもあまりヒットしないのでメジャーではないのかもしれません。。。ただ知ってるといいなと思う記法だったの備忘録として記載しました。

Updated at 20190109

上記でメソッドスタイルアクセスがどうして動作するのかの仮説をややこしく記載しましたが、この記法はJSのクロージャーだと捉えると腹落ちしました。

developer.mozilla.org

getters: {
  hogeById: (state) => (id) => {
    // 外部から直に私た変数をidに入れて使うことができる。
  }
}

というgettersのメソッドに対してメソッドスタイルアクセス記法を使ったとすると、hogeByIdメソッドを実行するときは必ず、stateが暗黙的に渡されて、さらにその関数内で作成されたローカル変数 state を保持する scope オブジェクトが作成されます。それは関数の引数として渡された変数とともに初期化されます。
このgettersメソッドにおいては hogeById メソッドが呼ばれる段階で state変数とid変数が初期化されてメソッド内で使えるようになります。

refった JavaScript「再」入門のクロージャーの章にて例に出されている以下のサンプル

function makeAdder(a) {
  return function(b) {
    return a + b;
  };
}

var x = makeAdder(5);
x(6) //11が出力される

これをgettersのhogeByIdと合わせてみると以下のような構造と捉えることができます。 ※ 擬似コードです。

var hogeById = getters(state)  
hogeById(1) // id: 1 で操作されたstateが返ってくる。

厳密な理解としてはまだ正しくないのかもしれませんが、クロージャーの構造としてみるとメソッドスタイルアクセスで指定した引数をstateを通り越してgettersのメソッドに伝播させることができるのか少しだけ理解に近づいた気がしました。

fishで &&、||、! がようやく使えるようになった

やっとか!という思いが強くてエントリを書いてしまいました。

表題の通りですが fishが2018年末に3.0.0にメジャーバージョンアップされていました。

github.com

その中で特に嬉しかったのがこれ

fish now supports && (like and), || (like or), and ! (like not), for better migration from POSIX-compliant shells (#4620).

fishユーザー今まで &&|| が使えずに、そういう手順を見かける度にand|or に書き直すが、しぶしぶzshを使うことで耐え忍んできたけど、ようやくその無駄なコストから少し解放されそうです。

fishがようやくまともなshellに少し近づいてくれました。感謝🙏🙏🙏

2019年 ~抱負~

エンジニアとして

あれこれ目標立てても達成できなくて振り返りで凹むので最低限これは続けたいことをピックアップしました。

現在使ってる技術のベースの知識を深化

今の自分のベースとなる技術スタックが

なので今年一年は新しいことを覚えるのを一旦ストップしてこの領域の知見を深めることを頑張りたいと思っています。

書を読む

迷ったんですけどこれを入れました。
インプットしたことがそのまま業務に生きるわけではないですが、これを目標に入れないとやらなくなりそうなので最低月1冊は技術書を読破したいと思っています。

読書録は emahiroの本棚 (emahiro) - ブクログ にて公開してます。

技術書だけでなく、インターネットの技術仕様やホワイトペーパーも読んでいきたいと思っています。

アウトプットを継続する

アウトプットがあってこそのインプットなので、このブログは引き続き書き続けたいと思っています。

その他

エンジニアとして以外の部分で。

料理のレパートリーを増やす

本当に頑張りたい。むしろ一番頑張りたいのはこれかもしれない...。

物を捨てる

自宅にある使わなくなったものをガンガン捨てて、身の回りを身軽にしていきたいです。

いいものに投資する

捨てる一方で、QOLを上げてくれるものへは惜しまず投資していきます。とりあえず初売りでスニーカーに投資してきました。

やらないこと

あえてこの項目を用意してみました。個人的には以下のことはやらないことを目指したいと思います。

0 -> 1 の開発

これ決めておかないとどうしても心が揺れてしまうタイプなので、新規とか0->1領域の世界は昨年に続いて一旦封印し、愚直に作ったもの、及び既に作られたシステムを安全に運用する力をつけていきたいと思っています。

新しい言語の習得(※ 仕事以外)

業務で必要になるケースを除いて新しい言語を覚えることに時間を使うより、今持っている技術スタックへの理解を深化させることに時間をかけたいと思っています。

2018年振り返り

今年の振り返りです。

新年に立てた目標はこちら。

ema-hiro.hatenablog.com

エンジニアとしてコミットしたと言えるプロダクトをつくる

評価: 🙆‍♂️

普段で業務で開発しているプロダクトは AndApp | スマホアプリがPCで遊べる | アンドアップ と言いますが、今年の後半はこの リニューアルプロジェクトでUI周りをゴリゴリ触る機会があり、かなりコミットできたと言えるんじゃないかなと思います。

引き続きGoをゴリゴリ書いてましたが、個人的には古き良きjQueryで止まっていた自分のフロントエンド力を今時のスキルセット(SPA + Typescript) にアップデートできたいい機会になりました。

月2冊以上の技術書を読む

評価: 🤔

最初の半年はできていましたが、後半失速しました。
書籍である程度インプットしてもあんまり実務に活きないなーって思い始めたのがきっかけなのと、買った本でも興味のある章だけ読んでサクッと次に行くとかしてたので、実際読んでいたけど、読了した!と思えるものは後半あまりなかったのもあります。

このエントリを目にしてから少しずつ読み方が変わりました。

qiita.com

とはいえ、この読み方は嫌いじゃないので今後も続けていこうかなと思いますし、読了数を追わないようにしたいなと思いました。

そんな中でも『エンジニアリング組織論への招待 ~不確実性に向き合う思考と組織のリファクタリング』は別格に面白かったです。

使ってる技術であれば『Goならわかるシステムプログラミング』はかなり仕事に活きました。

githubへの1commit/weekをしていくこと

評価: 🙅‍♂️

全くできませんでした。個人の勉強記録はもう少し定期的にあげてもよかったような気もしてますが、やはり作りたいものがないとモチベーションが継続しません。。。
草ばっか生えてても中身なければ意味がないし、何か作りたいモチベーションが今の所ないのでここは来年はあまり意識しないようにしようと思います。

新しいサービスは積極的に使っていく

評価: 🙆‍♂️

もう癖ですね。今年も色々使ってみてそのほとんどはその場で消しました(笑)
そして使い続けてるサービスも特に残りませんでした。これほど無収穫な年も珍しいです。毎年何かしら「これはいい!」って思ってずっと使い続けてるサービスがあったので。

新しい言語を一つ仕事レベルで習得すること

評価: 🙆‍♂️

Typescriptを仕事レベルで習得することに挑戦し、実際プロダクション投入できたことはよかったです。
来年も引き続き新技術や新しい言語へのチャレンジはしていきたいことですが、浅く広くても仕方ないので当分は

  • サーバーサイド: Go/GCP(GAE)
  • フロントエンド: Vue/Typescript

をベースにしていきたいかなと思っています。
どれもIDEの恩恵を受けることができてとてもDXが高く書き心地がいいと感じています(※ 個人の主観です)

そのほか

  • 自炊のレパートリーを増やすこと
    • できませんでした...
  • ブログを引き続き継続して書くこと
    • できました。ただ、社内で捕捉され、見られてることが多いらしいので迂闊なことは書けなくなって辛いです。
  • 資産運用や税制といったファイナンシャルの教養を身につけること
    • うーん...道半ばです....。

2018まとめ

新しいサービスとか0->1が好きな人間でしたけど、そこをまずは封印してベタな ものづくり力をつけること にフォーカスすることで、気づきの多かった一年でした。
小さなサービスしか経験したことのなかった自分にとって これまでのエンジニアキャリアの中で一番障害を出しましたし、その度に色々助けてもらいながら学びがありました。
(できることなら来年は障害起こすようなことを減らしたい....)

今年の反省を踏まえつつ、来年はどんなことを頑張ろうか年末年始に考えようと思います。

それではみなさん良いお年を〜

社内isuconに参加して惨敗した話

先日(12/10)社内でisucon8の問題を解くイベントが開催されたので参加してきました。

詳しい内容は

を参照してください。
今回は僕の振り返りをまとめてみます。

結果

惨敗でした。予選通過スコアが3万弱くらいだったので遠く及ばないスコアしか出すことができず、文字通り「なんの成果もあげられませんでした」状態で無事終了しました。

敗因

こんな感じかなと(若干言い訳じみた内容も含みます)

isucon慣れしてない

しょっぱなから言い訳です(笑

こういった競技プログラミング的なことを経験したことがなかったので、まずレギュレーションを確認したり、何から取り掛かるべきかってところであたふたしてしまいました。ダッシュボードの使い方とか最初よく分かってませんでしたし初動のここで時間がかかってしまいました。

また、チームとしての作業分担をあらかじめ決めておけませんでした。慣れてるチームはある程度役割分担を先に決めてしまってサクサク作業を進めていた印象でした。

Linux忘れてる問題 and インフラの知識不足

Linuxの使い方とリソース監視のところが丸っと忘れててなんの力にもなれませんでした。
普段の業務はほぼPaaSの運用作業なのでVMSSHするなんてめちゃくちゃ久しぶりでしたし、sysctl系のLinuxコマンドもほぼ忘れてたり、知らないもの多くてこの辺知ってる知らないで確実に差が出てしまったところだなと思いました。
特にサーバーのリソース監視のところさっぱり忘れてて、サーバーのリソース監視ってどうするんだっけ?とか初歩の初歩でどつまづいてました。 dstat コマンドとかそういえばありましたね。

計測方法の未整備

憶測するな、計測せよ という当たり前のことができてませんでした。
まずボトルネックになってるEPがどこでそこを高速化するって言う当たり前のことなんですけど、参加したチームでこの辺の計測の手法とかツールを先決め事としてやっておけなかったのがあとあとになって響きました。
パラレルで作業するに当たって誰がどこをするのか、なんでそれをするのか?をちゃんと根拠立てて進めるために、計測する仕組みを先に整えておけばよかったと思います。
高得点を出したチームはここが出来ていたように思います。
この辺もプロファイルするコマンド知っていたり、計測するプラクティスを知ってる知らないで差が出たなと思いました。

実装力不足

GetEvents地獄をさっさと取り除ければもっと色々出来たのかなと感じました。
ボトルネックを見つけた後、素早く修正する単純な実装力もないなと実感しました。後述する理由もあってオンタイムで参加できなかったので、解決にかける時間がそもそも少なかったというのはありますが、それでももっとサクサク実装できればよかったなと思いました。
使い慣れてないライブラリやツールであっても使えるように日頃から素振りとかしているとこういう時に役に立つんだなと思います。

そのほか

差し込み業務があって参加が遅れてしまいました...(これは仕事なので仕方なし)

まとめ

基本的なところでつまづいてしまった ってところが大きいです。
実力不足を痛感しました。

それに気づけたいい機会でした。
どうしても普段仕事をしていると、自分がどれくらいの実力なのかわからなくなる時があるのでこういった競技に参加してみるのも自分を客観視できるいい機会だなと思いました。

今回予選問題を作成した karupanerura sanが「isuconはweb開発の総合格闘技」と言う言葉を使ってますが、その意味が少しわかりました。

アプリケーションレイヤーだけでなく、インフラや、ボトルネックを見つける力、それに対して最短でアプローチし、かつ最速で実装するスキルを求められましたし、どれを取っても全く不足してました。。。
(もちろん業務で書かないようなコードの書き方もしたので、仕事においてはこれができればいい、ってわけでもないんですが...)

反省ばかり並べても悲しくなるのでよかったところを最後にまとめます。

よかったこと

  • isuconと言うイベントを体験することが出来たこと。
    • 知ってはいましたが参加するのにおよび腰だったのでこういった社内で開催される機会があってよかったです。イベントへの心理的なハードルを下げることが出来ました。
  • 速度アップという目的に特化したことで普段業務で意識しないところまで意識せざる得ず、よく言われる問題(N+1問題とか)を実際に体験出来たこと。
    • ベタなIaaSの構成だったのがとてもよかったです。リソースが限られている中で高速化について考える機会がクラウドを使っているとそんなに訪れるわけではないですが、無駄な実装をしているとそもそもいくらお金かけても意味がないので。
    • 無駄な実装を取り除くとスコアが上がる、単純なことですが定量的に評価されるとなぜやるのか?ということの意味がわかります。

余談

今回はGoで参戦しましたがこういったイベント(予選ではなく社内開催ではありましたが)に参加したことで、言語ごとに多少に有利不利はあれど、言語で差が出る以前の基本的なweb開発のスキルが大事であることを実感しました。
実際、講評にもある通り、Goは言語的には有利だったにも関わらず全くその良さを出すことは出来ませんでした。そのレベルに行く以前のところでつまづいたので全くスコアを出すことは出来ませんした。
問題解決に最適なツールを選択すること、そしてそれを使う自分の基本的なスキル上げる方が大事だなと思いました。

精進しよ....

IntellijでRailsを開発する準備をする

仕事でRailsを書かないといけないことになったのでIntellijRails開発する準備をした手順をまとめました。

やりたいことは以下の2つです。 - IntellijRubyの補完が効くようにしたい - IntellijRailsの補完が効くようにしたい

IntellijRubyの補完が効くようにしたい

これは PluginRuby をググってインストールしてください。もしかしたら最初から入ってるかもですが。

IntellijRailsの補完が効くようにしたい

とりあえずおもむろに Open から Railsのプロジェクトを指定して開いてみましたが、それだけだと Rails の補完は効くようになりません。
なんでだろう? と思ったんですがそもそも Rails って bundle install で入れるものだから、デフォで補完が効くわけないんですよね。

プロジェクトに対して bundle installRailsを入れた状態にしないといけないわけです。

ではどうやって bundle install できるようにするかって話なんですがこれには Import Project から作業を始めます。

Import Projectの手順1
image1

サクサクと手順を進めます。以下の画面でデフォで選択されてる既存ソースからImportするを選んでください(特に何もすることはないです。)

Import Projcetの手順2
image2

プロジェクト名と配置したい場所を指定します。(これも基本そのままでいいです。)

Import Projectの手順3
image3

ここは何もせずに NEXT

Import Projectの手順4
image4

これでRailsプロジェクトとして Intellij にプロジェクトが import されたのでプロジェクト内で bundle install を行います。
(Toolsからいけます。)

Toolsからbundle installする
image5

bundle install したら Intellijを再起動してプロジェクト内でインデックスを貼りなおします。これで無事Railsのプロジェクトの補完が効くようになります。

※ もしかしたらbundle installした状態のプロジェクトをインストールすることがあるかもしれません。その場合は Import Projectじゃなくて Create New Project からRailsプロジェクトを指定するといいです。ソースをImportしたいプロジェクトに合わせることができます。

JSのSetTimeoutを解除する

.clearTimeout() | JavaScript 日本語リファレンス | js STUDIO に記載されてる通りなんですけど、実装時にちょっとハマったので備忘録。

// timer起動
const timer = setTimeout(() => { /* hogehoge */ }, "1000");
timer();

// timer解除
clearTimeout(timer);

これでsetTimeoutで設定したTimerを解除できるものかと思っていましたが、 timer() とした時点で timeoutIDを返り値に取らなくなってしまい、clearTimeout をしても解除できません。
(Frontend力のなさを痛感しました。。。)

SetTimeoutは変数の中に定義した時点で実行されてTimer登録されます。そのため

const timer = setTimeout(() => { /* hogehoge */ }, "1000");

この時点でsetTimeoutは実行されてTimerがセットされます。 SetTimeout は返り値に timeoutID が入ってくるので、SetTimeout を解除する clearTimeout(timeoutID) の引数にはTimerを設定した時の返り値の timeoutID を指定することでTimerを解除することができます。

GCSのObjectをCopyするときの挙動について調べたこと

業務でGoogle Cloud Strage にObjectをコピーするときの挙動について調べたのでまとめておきます。

GCSでのコピーは cloud.google.com/go/storage 配下にある copy.go に処理の中身が書いてあります。

github.com

実際にGCSでコピーをするときには以下のようなコードを書きます。
※ サンプルコードなので適当です。

client, _ := storage.NewClient(ctx)
defer client.Close()

srcObj = client.Bucket(srcBucketName).Object(srcObjName)
distObj = client.Bucket(distBucketName).Object(distObjName)

// copier の生成
copier := distObj.CopierFrom(srcObj)

// copy の実行
attr, err := copier.Run(ctx)
if err != nil {
    // copy失敗 -> (1)
    fmt.Errorf("copy error. err: %v", err)
}

// copyが成功したら生成される Object Attributes
return attr 

ここで (1) の copy時にエラーが発生したときにcopy途中だったオブジェクトはどうなるのか?ということが気になったので実際の copy 処理を行なっている Run メソッドの中を見ました。

// Run performs the copy.
func (c *Copier) Run(ctx context.Context) (attrs *ObjectAttrs, err error) {
    ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.Copier.Run")
    defer func() { trace.EndSpan(ctx, err) }()

    if err := c.src.validate(); err != nil {
        return nil, err
    }
    if err := c.dst.validate(); err != nil {
        return nil, err
    }
    // Convert destination attributes to raw form, omitting the bucket.
    // If the bucket is included but name or content-type aren't, the service
    // returns a 400 with "Required" as the only message. Omitting the bucket
    // does not cause any problems.
    rawObject := c.ObjectAttrs.toRawObject("")
    for {
        res, err := c.callRewrite(ctx, rawObject)
        if err != nil {
            return nil, err
        }
        if c.ProgressFunc != nil {
            c.ProgressFunc(uint64(res.TotalBytesRewritten), uint64(res.ObjectSize))
        }
        if res.Done { // Finished successfully.
            return newObject(res.Resource), nil
        }
    }
}

新しいオブジェクトにCopyが完了した段階で if res.Done {} の分岐に入ってきてこの時だけcopyした結果をインスタンス化して返しているのでそもそもCopyが行われてる最中に失敗したら Copy 対象は中途半端な状態で残ることなくerrが返されるだけになります。
途中で失敗してもゴミが残らないように作られてました。

GCPの認証設定が切れた時の対処法

いつからかわかりませんが、localのコンソールからGCPの各サービスのAPIを叩くときに以下のエラーが出てAPIが叩けないと言うことがありました。

err: dialing: google: could not find default credentials. See https://developers.google.com/accounts/docs/application-default-credentials for more information.
exit status 1

GCPの Application Default Credentials(ADC) が古い設定のまま更新されていなかったことが原因でした。
しかし、エラーメッセージにある Setting Up Authentication for Server to Server Production Applications  |  Authentication  |  Google Cloud には認証設定が必要である、とだけ書いてあって、その認証設定の仕方までは書いてません...(困った)

そこで GCP の Application Default Credentialsの再設定方法を調べたところ以下のコマンドで再設定可能でした。

$ gcloud auth application-default login 

実際に再設定してみます。

$ gcloud auth application-default login 

The environment variable [GOOGLE_APPLICATION_CREDENTIALS] is set to:
  [/USERS_PATH/.config/gcloud/legacy_credentials/[AUTHORIZED_USER]/adc.json]
Credentials will still be generated to the default location:
  [/USERS_PATH/.config/gcloud/application_default_credentia
ls.json]
To use these credentials, unset this environment variable before
running your application.

~/.config 配下に adc.json と言う認証されたユーザーのcredentialsが入ったjsonファイルが生成されます。GCPAPIを叩くときはこのadc.json の内容を参照するので、これが正しく設定されている必要があります。

このGCPの認証設定については以下の記事が参考になります。

dev.to

他に困ってる人いないのかなーと思ってざっと調べてみたら関連しそうなQ&Aが上がってました。

stackoverflow.com

この回答に記載してあるところの

gcloud auth login
You haven't application default credentials

gcloud auth login しただけではADCは取得できなかったみたいです。(いつからこれが適用されたかまではわかってません。)

これを機会にGoogle Cloud SDK周りの使い方のドキュメントをざっと見直してみました。

承認について

Authorizing Cloud SDK tools  |  Cloud SDK Documentation  |  Google Cloud

gcloud auth application-default login について

gcloud auth application-default login  |  Cloud SDK  |  Google Cloud