病みつきエンジニアブログ

機械学習、Python、Scala、JavaScript、などなど

gqlgen でのキャッシュ性能向上を考えてみる

NewsDigest ではアプリの BFF として GraphQL を使っていて、ライブラリとしては gqlgen を使ってます。で、なるべく CDN でのキャッシュヒット率を上げたいなぁということで、 gqlgen でできることをプロトタイプしてみました。

github.com

前提として、

  • Apollo とかは使ってなくて、curl でのコンセプトレベルの検証
  • 僕は Go を書く力が弱い
  • 頭の体操であって、実戦投入したものではない

1: Persisted Query をつかう

まず、Persisted Query を使います。

Add File-based Persisted Query · yamitzky/example-gqlgen-cached@5917721 · GitHub

Persisted Query は、簡単に言うと、クエリ自体をハッシュ文字列(0123456789abcdefの列)に置換して送信するものです。GraphQL のリクエストサイズは結構デカく(冗長)、数kb とかになったりするので、これを数文字のハッシュ文字列に置換できれば、送信サイズを大幅に圧縮できます。例えば、次のような curl です。

curl -g 'http://localhost:8080/query?extensions={"persistedQuery":{"version":1,"sha256Hash":"688a6c"}}'

理論上、どんなに大きなサイズのクエリであっても、上記のような定数長のクエリに置換できます。また、上記のように GET でリクエストすれば、CDN によってキャッシュしやすいです。

一般論として、gqlgen に搭載されている APQ の機能は、クライアント側がハッシュ値を勝手に登録することが期待されています。GraphQL のクエリは無限のパターンがあり得るためです。一方で、GitHub に上げた例では、事前にクエリ文字列をコミットしなければならない という前提を置きました(Cache.Add が何もしない)。プライベートな BFF として作るのであれば、どういうクエリを送りたいかは事前にコントロールできるためです。むしろこのように事前にファイルにコミットしておくことで、意図しないクエリ文字列の違いによるキャッシュヒット率の低下を防いだり、クエリ登録のネゴシエーション*1も減らすことができます。まぁ BFF のようなチーム開発するものなら許容できるでしょう。

余談ですが、 http_get.go を模して*2 /queries/abcdef みたいな感じで RESTful っぽくリクエストできるような GraphQL API を作ることもできると思います*3

2: Cache-Control を設定する

Persisted Query を使うだけだと、CDN のキャッシュ時間は固定値でしか設定できません。例えば頻繁に更新される TODO 一覧と、あんまり更新されないユーザー一覧が同じキャッシュ時間だと不便です。そこで、リゾルバー側から Cache-Control ヘッダーをリクエストできるようにしてみました。

Add cache-control · yamitzky/example-gqlgen-cached@f4e956f · GitHub

GraphQL では1度に複数のリソース(例:todos と users)を取得できることになるので、最終的には最もキャッシュ時間が短いものにします。例えば、todos は60秒のキャッシュ、users が1日のキャッシュで良いのであれば、

  • { todos { id } }・・・60秒
  • { users { id } } ・・・86400秒
  • { todos { id }, users { id } }・・・ 60秒

ということです。directiveで設定する方法もあると思いますが、動的に変えられるアプローチのほうが良いと思います。

ちなみに、同じようなコンセプトですが、もっと良い方法がプルリクされてます*4

3: resolver レベルのキャッシュ

2 番目の方法では、1度に複数のリソースを取得した場合、最終的に最も短いキャッシュ時間を採用していました。これは CDN のキャッシュとしては適切ですが、CDN のキャッシュが切れたときに少しもったいないです。例えば、 { todos { id }, users { id } } に対して60秒のキャッシュが設定されていますが、60秒経過した後、1日程度はキャッシュしていいはずの users を再計算することになります(実際には DB へのアクセスになるでしょう)。

そこで、gqlgen の extension の仕組みを使って、resolver レベルでキャッシュできるようにしてみました*5。これであれば、できるだけ CDN でキャッシュされ、CDN のキャッシュが切れたとしても適切な範囲で resolver レベルでメモリや Redis などにキャッシュできます。

Add resolver-level cache · yamitzky/example-gqlgen-cached@05c6e5e · GitHub

その他の採用しなかったアプローチ

Akamai とかの GraphQL 対応した CDN をつかうと、2 と 3 をもう少し綺麗に対応できそうな感じがします。

*1:APQ は Optimistic Request なので、メモリキャッシュであれば分散したサーバー分ネゴシエーションが増え、それを避けるには Redis のようなキャッシュサーバーへの余分なリクエストが必要になります

*2:これも余談ですが、最近の gqlgen はリファクタされたおかげで、あまりコピペせずにハンドラー作れるのですね

*3:RESTful 前提のメトリクスツールが使えるとかのメリットがあります。それ GraphQL である必要ないやんけという意見もありそうですが、リポジトリにクエリ文字列を保存さえすればクライアント側はほしいデータを柔軟にとってこれるので、十分に GraphQL のメリットは享受できてると思います。Apollo みたいな便利 GraphQL クライアントが動くかはわからん

*4:書き終えたあとに気づいた... gqlgen はミドルウェアからヘッダーを動的に書きかえる方法がないので、コアリポジトリを変更するか protocol コピペすることになります

*5:面倒くさかったので、TTL は設定してません

OSS 版 Redash ボットを Slack Bolt で作り直した

Redash のオンプレの bot を、Slack の Bolt というフレームワークを使って作り直しました。

github.com

Redash とは

Redash は様々なデータソースと接続して SQL/可視化に使える OSS/SaaS です。最近、Databricks に買収されたことで話題にもなりました。

redash.io

Redash 本体は OSS で公開されていますが、Slack ボットに関しては OSS ではありません(OSS でないものが公式提供されてる)。そのため、Redash 本体はオンプレ(クラウド)にデプロイできるにも関わらず、ボットがオンプレに置けないという課題がありました。このあたりの課題感は、ボットのフォーク元である、hakoberaさんのQiitaをご覧ください。

また、OSS 版 Redash を運用する工夫はこちらに記載しています。今回のリリースに伴い、JX通信社の Slack にもインストールしています。

tech.jxpress.net

Slack の Bolt とは

Slack の Bolt はボットのためのフレームワークで、とても生産性高く書けます。また、Python 版も最近 α 版がリリースされました。

slack.dev

さて、Slack の bot は、もともと RTM (Real Time Messaging)と呼ばれる、Websocket ベースのアーキテクチャでした。しかし新しい Slack のアプリでは、Events API という Webhook(HTTP) ベースの新しい bot を作る必要があります。これはアーキテクチャ的に後述する安定性などのメリットがあります。

使い方

詳しくは README を読んでください。Docker を使う場合は docker run yamitzky/redashbot:2.0.0 で使えます

書き直した理由

コードを書き直したことにより、次のメリットが生まれました。

  • RTM API から Events API へ移行したことで、安定性が増加した
    • HTTP でロードバランサーにぶら下げればいいので負荷分散しやすい
    • Websocket に比べ、ヘルスチェックがしやすい
  • TypeScript へ移行
  • ソースコードを分割しやすくなった

特に、僕のフォークしたバージョンではダッシュボードが見れるなどの追加機能があったため、ソースコードが地獄でした(ソース)。

また、地味に次のような変更をしています

  • デフォルトブランチを master ではなく main に
  • Chromium 以外に、FirefoxWebkit のサポート (playwright を利用。テストできてない)
  • Serverless 環境へのデプロイ (途中...誰かテストして...)

playwright は Microsoft から出ている Puppeteer の後継パッケージみたいな感じで、マルチブラウザでの Puppeteer 相当のことができます。また、公式で Docker イメージが提供されているので、Docker 化も簡単でした。(文字化け対策で fonts-noto だけインストールした)

できること

公式のボットを使ってないのでわからないですが、

  • グラフのキャプチャ (これは当たり前ですね!)
  • ダッシュボードのキャプチャ
  • 表データを、キャプチャではなく、フォーマットした表テキストとして投稿する

などができます。また Bolt で作られている OSS なので、機能追加も簡単かと思います。せっかくなので、対話機能とかも使いこなしたいかもしれない。

@bot <URL> といった形式で使うので、もちろん Slack のリマインダーと組み合わせて使うこともできます。例: /remind #hoge @redash https://your-redash-url/dashboards/fuga *1

*1:リマインダーの対応については、公式の directMention だと先頭一致となっているため、現在は独自実装しています。こちらのスレッドで話しています

Python の型システムの上で Immutable な Python プログラムを作る

まえがき

今年の PyCon JP 2020 にて「Python 3.9 時代の型安全な Python の極め方」というタイトルで登壇させていただきます。本稿は、発表の補足となる「型ヒントを使って Immutable な Python を実現する方法」について紹介したものです。

Python の型ヒント

Python には「型ヒント」という機能があり、型をプログラム内に宣言することができます。

age: int = 28
name: str = 'Bruce Wayne'

Python は動的型付き言語であるため、この情報はランタイム(実行時)にはあまり意味がないのですが、 mypy などの型チェックツールをつかうと、型の誤りをチェックすることができます。

def check_batman(name: str) -> bool:
    return name == 'Bruce Wayne'

age = 28
check_batman(28)  # NG: 文字列型の引数に int を入れているため

さて、この機能をつかうと、 list や set のような mutable (要素が書き換え可能) なデータ構造であっても、型システム上においては imutable なものとして扱うことができます。ランタイム時は基本的なデータ構造である list や set などを使い続けながらも、型のチェック時だけ書き換えを防ぐことができるのです。

Immutable Python とは

例えば、 list を immutable にしたいというのは、こういうことを防ぐのが目的です。

def check_list(li: list):
    li.append('fuga')  # ← 勝手に書き換えないでえええええ

users = []
if check_list(users):  # ← 意図せず書き換わってしまう
    ...

さすがにこんなにことはしないと思いますが、例えば numpy の shuffle や JavaScript の sort など、配列に変更を加えて嫌な気持ちになるケースは結構紛れています。

これをランタイム(実行時)時に Immutable にする方法もあります。同僚の書いた記事をご覧ください。

tech.jxpress.net

しかし、あくまでデータ構造としては一般的なものを使い続けたり、ランタイムの挙動を変えずに Immutable にしたいこともあると思います。今回は クラスやリスト等のデータ構造が不変な状態を保つこと を目的とし、変数の書き換え防止や副作用全般のコントロール、ランタイム時の Immutability などは目的としていません。

Immutable な list とは

例えば、list に対する破壊的な変更は、次のようなものがあります。

name_list.append('New User')
name_list[1] = 'Renamed User'

つまり、変更できるメソッドが生えているから、list が変更できてしまうのです。逆に、変更できるメソッドが生えていない list を型宣言すれば、その list は型システムの世界においては実質 immutable です。

Protocol による型宣言

Python では 3.8 から Protocol という「継承によらない部分型」を宣言できるようになりました。つまり「何を継承してるか(名前的部分型)じゃなくて、何を持っているか(構造的部分型)で自分を語れよ!」ということです。

from typing import Protocol
class Vehicle(Protocol):
    def run(self):
        ...

class BatMobile:
    def run(self):
        ...

def run_vehicle(v: Vehicle):
    v.run()

run_vehicle(BatMobile())  # 継承してないけどOK

ここで、BatMobile と Vehicle の間には継承関係はありません(名前的部分型でない)。しかし、 run という共通のメソッドを持っていて、関数内ではそれを呼び出しています。したがって、構造的部分型としては OK なのです。

では「Immutable な list となるような構造的部分型」を定義してみましょう。「Immutable な list」は、「'a' in hoge」とか「for x in hoge」とかはできるのに、「 hoge[0] = fuga 」 はできないような list です。これらは Python では __contains____iter____setitem__ といったメソッドを class に定義いしてあげることで実現できます。

from typing import Protocol

class ImmutableList(Protocol):
    def __contains__(self, x):
        ...
    def __iter__(self):
        ...

def check_list(li: ImmutableList):
    print('a' in li)  # OK
    for x in li:  # OK
        ...
    li.append('fuga')  # NG

users: ImutableList = []
if check_list(users):
    ...

今回は ImmutableList というのを作ってみましたが、 Python 公式で用意されている ので、これを使いましょう。

from typing import Sequence

def check_list(li: Sequence):
    print('a' in li)  # OK
    for x in li:  # OK
        ...
    li.append('fuga')  # NG

users: ImutableList = []
if check_list(users):
    ...

同様に、Mapping (immutable な dict 相当)やSet(immutable な set 相当)なども用意されています。

Immutable な class

dataclass(frozen=True) や NamedTuple をつかうと、mypy はチェックしてくれます。もちろん、ランタイム上でも immutable になります。

from dataclasses import dataclass

@dataclass(frozen=True)
class User:
    name: str

batman = User(name='Bruce Wayne')
batman.name = 'Dick Grayson'  # NG

この方式の微妙な方法

__contains__ を持っているかどうかでしか宣言できないので、「真に set や dict がほしい、immutable で」というケースには使えません。例えば、 x in []x in {} では、計算量が違うので、contains を持っていても list は困る、という場合には使えません。

型の上で Immutable なデータ構造のメリット

と、ここまで書いて「Immutable な Python って意味あるの?」という疑問を持たれたかもしれません。このメリットは3つあります。

一つめは、意図しない副作用を発生させない(安全なプログラムを作る)という観点です。

2つめの理由は、list などのジェネリクスは、共変ではないということです。例えば、「犬リスト」は「動物リスト」として扱えるでしょうか?

from typing import List

class Animal:
    ...

class Dog(Animal):  # 継承関係にある
    ...

def check_animal(animal: List[Animal]):
    ...

dogs = [Dog()]
check_animal(dogs)  # NG: List[Dog]はList[Animal]として扱えない!

実は、「犬リスト」は「動物リスト」として扱うことはできません。なぜかというと、 check_animal の中で勝手に「うさぎリスト」等に書き換えてしまことができるからです。逆に、書き換えられないようにしてあげれば、「犬リスト」を「動物リスト」として扱うことができます。

from typing import Sequence

class Animal:
    ...

class Dog(Animal):  # 継承関係にある
    ...

def check_animal(animal: Sequence[Animal]):  # List から Sequence に変えただけ
    ...

dogs = [Dog()]
check_animal(dogs)

ある程度 Python で型ヒントをちゃんと書いていくと、ジェネリクスのこのあたりの挙動に引っかかることがあるかと思います。

(追記) 3つめのメリットとして、「デフォルト引数にミュータブルなものを入れてバグる」ということがなくなります。デフォルト引数には [] などは入れず、None などのイミュータブルなものだけ入れるべきというプラクティスがありますが、型チェックをするならばこのプラクティスは意味がなくなります。

宣伝

...みたいな Python の型の話を、PyCon JP 2020 にて「Python 3.9 時代の型安全な Python の極め方」というタイトルで 8/28(金) 11:50〜 発表します。チケットを買ってない方も、YouTube にて生配信を見れるとのことです。

pycon.jp

同僚の id:shinyorke も同日 16:50〜 「スポーツデータを用いた特徴量エンジニアリングと野球選手の成績予測 - PythonとRを行ったり来たり」というタイトルで発表がありますので、こちらも是非ご覧ください!

Serverless Components を使った少しだけ実践的なアプリケーションの作り方

Serverless Framework の新しい機能「Serverless Components」を使って、サーバーレスなアプリケーションを作ってみました。

いくつかつまずくところもあったので、ブログに残します。ちなみに今回作ったアプリケーションは(特に紹介しませんが) Nature Remo の API を監視するアプリケーションです。

github.com

また、今回は AWS Lambda やサーバーレス自体は知っている読者を対象にしています。

Serverless Components とは

Serverless Framework という、サーバーレスなアプリケーションを作るためのツール/SaaS に新しく搭載された新機能(?)です。今年の4月にGAとなりました。

www.serverless.com

Serverless Components は無料で使えますが、GA 版に伴い serverless.com へのログインが必要になっています。

Serverless Components は今までの Serverless Framework と違い、

  • CloudFormation に非依存でデプロイが速い
  • 自作 Component (プラグイン的な)の定義が容易で、ベンダー非依存 (紹介しません)
  • 新機能「dev mode」

といった特長があります。dev mode は、 serverless dev を走らせておくと、自動デプロイ(≒ Lambda へのリアルタイム反映)をしてくれる開発用機能 です。便利!

定義ファイルもかなりシンプル で、もっともシンプルな serverless.yml の定義は

component: aws-lambda
name: your-lambda-function-name

だけです。便利!

使える Component のリストは、次の GitHub のグループにリストアップされているものです。

github.com

一方で、 Serverless Component の GA 版では、1つの serverless.yml に 1 つのデプロイ対象(関数等)しか書けない制約があります。これは制限というよりは、規約という方が近く、そういう思想で作られているように見えます*1。したがって、Serverless Components を使ってアプリケーションを作る場合は、Component を組み合わせるような作り方です。

DynamoDB を使った、AWS Lambda のアプリケーション

Serverless Components の考え方を紹介するため、 DynamoDB を使った Lambda のアプリケーションを考えてみます。

通常の Serverless Framework の serverless.yml は、次のように resources の下に生やしていったはずです*2

service: app.yml

provider:
  name: aws
  ...

resources:
  Resources:
    hogeTable:
      Type: AWS::DynamoDB::Table

Serverless Components では、1つの serverless.yml にリソースをどんどん生やしていくのではなく、複数の Component を組み合わせていきます。今回は、Lambda のための「aws-lambda」と、DynamoDB を定義するための「aws-dynamodb」、さらに、Lambda から DynamoDB にアクセスできる IAM Role を定義するための「aws-iam-role」の3つの component を組み合わせます。

これらのインフラの依存関係は、次のようになっているので、この順番に紹介します。

f:id:yamitzky:20200810172149p:plain

DynamoDB のテーブル定義

次のように serverless.yml を作ってください。inputs の詳細などは、公式ドキュメントに乗っています、attributeDefinitions などはテーブルのキー定義なので、ユースケースによって異なります。

ここで大事なのは、 org や app を指定することです。Component は組み合わせて使うので、app を指定してグルーピングをしやすくするのです。(ちなみに文字列決め打ちじゃなく、引数や環境変数でも指定できます。)

component: aws-dynamodb@1.1.2
name: your-table-name
org: your-org-name
app: your-app-name

inputs:
  attributeDefinitions:
    - AttributeName: id
      AttributeType: S
  keySchema:
    - AttributeName: id
      KeyType: HASH

この状態で sls deploy をすると、AWS 上に DynamoDB のテーブルが作られます。また、 app.serverless.com を見に行くと、次のようにデプロイしたインフラの情報が保存されています。CloudFormation の代わりに、serverless.com 内に状態保存されるといった感じです。

f:id:yamitzky:20200810173040p:plain

IAM Role から DynamoDB を使う

DynamoDB は、他の Component に依存していないので簡単でした。しかし、IAM Role は「このテーブルARNに書き込んでもいいよ!」という指定をしたいと思います。つまり、コンポーネント間で変数(この場合は、テーブルのARN)をやり取りする必要があります。

また、1つの serverless.yml には 1 つの Component のことしか書けないので、フォルダ分けをして複数の serverless.yml を定義する必要があります*3。つまり、こういう状態です。

.
├── table
│   └── serverless.yml
└── iam-role
    └── serverless.yml

iam-role/serverless.yml は、次のように定義をします。inputs の詳細などは、公式ドキュメントに乗っています。

component: aws-iam-role@2.0.2
name: your-iam-role-name
org: your-org-name
app: your-app-name

inputs:
  policy:
    - Effect: Allow
      Action:
        - sts:AssumeRole
      Resource: '*'
    - Effect: Allow
      Action:
        - logs:CreateLogGroup
        - logs:CreateLogStream
        - logs:PutLogEvents
      Resource: '*'
    - Effect: Allow
      Action:
        - dynamodb:GetItem
        - dynamodb:PutItem
      Resource: ${output:your-table-name.arn}

ここで、output という概念が出てきました。output を使うと、同じ app/organization/stage でグルーピングされた別リソースから、出力結果を持ってくることができます*4。このために、app、organization の指定をすべきだったのです。

こちらも同様に、serverless deploy すると、AWS 上に変更がデプロイされ、app.serverless.com 内にインフラの情報が登録されます。

Lambda のデプロイ

ここまで来るともはや面白いことはないですが、次のようなフォルダ構成で、

.
├── table
│   └── serverless.yml
├── function
│   ├── package.json
│   ├── serverless.yml
│   └── src
└── iam-role
    └── serverless.yml

serverless.yml を定義し、

component: aws-lambda@2.0.0
name: your-lambda-name
org: your-org-name
app: your-app-name

inputs:
  src: ./src
  roleArn: ${output:youriam-role-name.arn}
  env:
    TABLE_NAME: ${output:your-table-name.name}

src/index.js に適当に関数を書いていくだけです。無理やり TypeScript に対応したバージョンのサンプルもあります。

app.serverless.com には、こんな感じで登録されていきます(名前などは異なりますが)。

f:id:yamitzky:20200810175110p:plain

デプロイ方法まとめ

少し長くなりましたが、

  • フォルダをわけて serverless.yml を定義
  • 順番に serverless deploy を実行

していくだけです。Tips として、 .env ファイルは、親ディレクトリまでさかのぼります(organization を .env に入れるときとかに便利)。

また、一部の Component は、複数のリソースを1度に定義できるので(例:API Gateway と S3 と Lambda と...)、さらに簡単な場合もあります。

良かったところ

実践的な例では、Serverless Components のメリットが伝わりづらかったかもしれませんが、 デプロイの速さや、 dev mode などは便利です。

また、Component ごとにドキュメントが別れているのもわかりやすいなと感じました。DynamoDB の定義なども、旧型の resources に書くよりも、Serverless Component で書いた方がわかりやすいのではないでしょうか。

良くないところ

Serverless Components は GA になったとはいえ、Component が量/質ともに十分に用意されているとは言えません

Component の種類はここに定義されているものだけしかありません。たとえば GCP 用のものはまだ定義されていないようです。

そして各 Component の実装もまちまちです。例えば aws-lambda では IAM Role をカスタマイズする機能はありますが、aws-lambda-cron にはない機能です。また、aws-lambda はネイティブに TypeScript をサポートしていなかったり、Python もサポートされていません。

最後に、注意点として、GA 版とベータ版の Serverless Components には定義ファイルの互換性がありません。例えば、Next.js をサーバーレス環境にデプロイする serverless-next.js は GA 版に未対応です。(といっても、β版のまま使い続けることはできます)

まとめ

今までの Serverless Framework は、ある種完成品でしたが、 Serverless Components はまだ発展途上であるように感じます。タイトルに「少しだけ実践的」と書いた通り、実務では不足があるケースもあるかと思います。そのため、無理に移行する必要はないのではないでしょうか(事故りそうだし)。

とはいえ、デプロイの速さ、dev mode、定義ファイルの書きやすさなど、ユースケースにぴったりとハマることもあるかと思います。

それでは、よいサーバーレスライフを!

*1:リソースを1つのインフラスタックに詰め込むのではなく、分割するのが大事であるため、これを推奨している、と書いてある

*2:動かしてないので自信はない

*3:本当にこの方法しかないんでしょうか、、、

*4:ちなみに app、organization、stage を明示的に指定することもできます

買ってよかった Kindle Oasis

課題感

読書量を増やす必要性があって、「追加のお金を払ってでも、本を読みたい」というのがあって、色々な方に相談した結果 Kindle Oassis を買ってみました。8GB、広告なしモデルです。

Kindle Oasis、電子書籍リーダー、防水機能搭載、Wi-Fi、8GB

Kindle Oasis、電子書籍リーダー、防水機能搭載、Wi-Fi、8GB

完全なリアル書籍派だったんですが、結果的には Kindle Oasis 買ってよかったです。

購入理由:電子書籍でしかできない体験がありそう

  • お風呂でも読める
  • 夜、ベッドで電気が暗くても読める(バックライトがある)
  • 暗い夜道でも読める
  • 片手で読める
  • ポケットに入る
  • 机に置いて食事中に読む
  • カバンに複数の本を入れる

「スキマ時間で読む」というのができるようになって、1日あたりの読書スピードがすごく上がった感じがある。

ある程度大きな本(例えば最近だとHacking Growth読んだ)は、机の上に置いて読んだり、片手に持って読むのは難しい。けどこれが Kindle だと楽にできるので嬉しい。

事前の懸念

1. 会社の経費で本を買えない

今回は「お金を払ってでも読むこと」に重きを置いたので妥協した*1

あと、ちょろちょろ寄稿とか登壇の収入があるので、そっちに関係しそうなものはそっちの経費につけようかなとも思っている。

2. 良かった本を他の人に貸せない

「貸す必要があったら、会社の経費で買って渡せばいいや」と思って解決した。貸す相手は会社の人が多いし。

3. 技術書読みづらい説 (まだ遭遇してない)

PCで読んだりして使い分けると良いとのこと

結論

買ってよかった Kindle Oasis

*1:会社経費で本を買える制度があるんですが、開発に直接関係しないビジネス書とかだと、ちょっと抵抗あって普通に自腹で買ってたりもします

2018 -> 2019

振り返りと、ささやかながら今年の目標を。

アウトプット編

WEB+DB PRESSエンジニアHubの寄稿、デブサミDevelopers Boostの登壇があって、大変だった(小並感)

こういうのって、「誰に対して何を伝えたいのか」が本当に難しい。例えば、PyConでPythonの話するときは「Pythonを知ってる人」に対して伝えればいいだろうけど(中級者向けと宣言した上で)、汎用的なカンファレンスだと「コンテナって何」って人も「Docker使ってます」って人もオーディエンスに含まれるので、抽象(お気持ち)→具体(テクニック)に話を展開する必要がある気がして、なかなか慣れない。

仕事編

執行役員/VPoEになった。転職のオファーを何件かいただいたのもこのあたりで、意識的に自分のキャリアを選ぶのって大事だなと思った。会社のステージがさらに進むまでは同じ理由で断るかもしれない。知らんけど。

マネジメント的仕事が多くて、あんまりプログラムが書けなかったのが心残り。ただ、マネジメントは「仕事として」楽しい感じはあって、嫌とは明確に違う。前職では業務委託2〜3人+正社員エンジニアがマネジメントみたいなチーム編成で、それが嫌で転職したところもあったけど(※その方々は本当にいい人で転職してからも飲んだりしてる)、その人数を超えるとマネジメントの目的が変わってくるので、楽しい。

12月は他社のマネジメントの方と飲む機会があり「なるほど〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜」となったり、他社の開発のミーティング参加させてもらって「なるほど〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜」となったりしたので、そういう機会を増やしていきたい。あともっとコード書きたいね。

買い物編

いい買い物であった。分割キーボード壊れたけど。悲しい

GoProはダイビング用に買ったけど、日常使いでも、友人のライフイベントを広角で撮れるのってすごい素敵だなって思った。光が暗いとあんまり役に立たないんだけど。

↓石垣旅行で一番気に入った一枚

f:id:yamitzky:20190101162514j:plain

本編

ダントツで「ブランディングの科学」が面白かった。エビデンスのない話に結構飽き飽きしていたけど、その点科学的で良かった(もちろん科学的に批判される余地もあり、書評では賛否両論っぽい)。自分は科学的なアプローチ好きなのかもしれないので、ノーベル賞取った人の本とか読もうかなと思っている。

食べ物編

  • 初めてペアリングのコース
  • 初めての日比谷ビアフェス
  • 初めての牡蠣を食べるホームパーティー
  • 初めてのチェアリング

ペアリングとチェアリングは似て非なるもので、屋内で料理に合わせてワイン飲むのがペアリング、屋外で好きなところに椅子置いて飲むのがチェアリングです。

牡蠣はこちら。

manualog.net

f:id:yamitzky:20190101163256j:plain

旅行編

↓渾身の一枚「隈飲み」

f:id:yamitzky:20180625134259j:plain

石垣島は2回めだったけど、初スキューバダイビングとか、初ジェットスキーからのシュノーケリングとかが楽しかった。今年は一人で旅行行きたい。

プライベート編

  • 結婚式4回ぐらい行った
  • 会社の同僚とボードゲームいっぱいした
  • 朝まで飲むのいっぱいした
  • 花火見た
  • 合コンした

振り返ると、これが噂の結婚ラッシュか・・・ってなる

↓マンション・オブ・マッドネスおすすめです

f:id:yamitzky:20180525203415j:plain

結婚式では 結婚パーティーのため Nuxt と Firebase でサーバーレスなご祝儀を納品した話 - 病みつきエンジニアブログ とかもやった。

2018年振り返って

Google Photos見ながら振り返ると、めっちゃ充実してるじゃん!!!!!!!!!ってなった。こんなに楽しかったのか2018年。Google Photos マジ偉大だな。今年も良い一年にしていこうな

2019年の目標

ここで言うビールは「とりあえずビール」のことで、ビール飲みたくなくても頼むのは意志薄弱だし、プリン体摂取だし、良いことないなって。クラフトビールとか「飲みたいビール」は飲む。

あと2018年は学生時代のこと思い出した。知らない人とゲーム作って、知らない人と起業し。。。みたいな謎のテンションが学生時代はすごかったので、そういう感じで生きていきたい。

今年もたくさん泥酔すると思いますが、そのときはどうぞよろしくおねがいします。