Thanks Driven Life

日々是感謝

データモデルをチームメンバーに共有する時に TypeScript の型定義風にしている話(お試し中)

SmartHR Advent Calendar 2020 の3日目です。

本記事の概要

例えばこういう感じでテーブル設計(物理データモデル)したものを

f:id:gongoZ:20201201192641p:plain

エンジニア含めデザイナーやプロダクトマネージャー(以下、PdM)に共有する論理データモデルは、以下のように TypeScript の型定義風にして共有するみたいなことをやってみている、というお話です。

type UUID = string

type Book = {
  id: UUID
  authorId: UUID
  publishedAt: Date
}

type Author = {
  id: UUID
  name: string
  penName: string | null
}

またはこんな感じ

type UUID = string

type Book = {
  id: UUID
  author: Author
  publishedAt: Date
}

type Author = {
  id: UUID
  name: string
  penName: string | null
  books: Book[]
}

TypeScript 風なので、なんだったらこれでも OK

type 書籍 = {
  id: uuid
  著者: 著者
  出版日: Date
}

type 著者 = {
  id: uuid
  氏名: string
  ペンネーム: string | null
}

補足

  • SmartHR 全体の開発フローとして採用(試用)しているものではありません。個人的な活動です 👍
  • この施策で何が変わったかとかまでは書いてないです。まだ始めたばかりなので 💪

経緯

1. 前提

SmartHR における開発の流れは、ざっくりこうなっています(本当にざっくりです)。

  1. なんやかんやあって、スタート 🏁

  2. 抽出されたユーザーストーリーから、PdM が *1 概念データモデル を作成する

    f:id:gongoZ:20201203003813p:plain

  3. チームメンバーでレビューしていく

  4. 設計していくぞー

  5. エンジニアが 物理データモデル を作成(テーブル設計)する

    f:id:gongoZ:20201201192641p:plain

  6. チームメンバーでレビューしていく

  7. 開発していくぞー

  8. なんやかんやあって、リリース 🎉

今回は 5 〜 6 のお話です。

2. 発端

ある日、とある新機能開発のテーブル設計をチームメンバーに共有する機会がありました。 ERD を見てもらい、共有会は無難に進み、なんやかんやありながらも最終的に GoGo の方針が固まりました。

その過程で、とあるメンバーとこういったやりとりがありました。

👳‍♀️ = 僕
👤 = フロントエンジニア

---

👳‍♀️ 「というわけで、こんな感じのテーブルにしようと思います」

    authors テーブル:
        t.string :name, null: false
        t.string :pen_name
        t.timestamps

    books テーブル:
        t.string :author_id, null: false
        t.datetime :published_at

👤 「null: false ってなんですか?」
👳‍♀️ 「データベースの NOT NULL 的なやつの ActiveRecord::Migration での記法ですね!」
👤 「ついてないやつは Nullable?」
👳‍♀️ 「です!」
👤 「t.timestamps って何ぞや」
👳‍♀️ 「created_at と updated_at を同時に生み出してくれるすごい奴です」
👤 「拝承」

何気ないやりとりでしたが、後日あらためて考えた結果、あまり私の共有の仕方が良くなかったなと思い至りました。

  • Ruby on Rails に(この時点ではまだ)不慣れだったメンバーを含めた共有会で、この見せ方は逆にわかりづらい
    • いずれは慣れていってもらうというのが理想系だが、そもそもスタートに見せる形ではなかった
    • そもそも詳細な実装については、このあとのコードレビューで見てもらうことになっている
    • スタートとしては、まずは認識の誤差を埋めるために イメージしてもらいやすい形 にすべきだった
  • 「どうせこのまますぐコードに写して commit するしな」みたいな気持ちで横着していた
    • どうせ詳細に書くなら SQL で書けば良かった
    • とはいえ、あまり物理層に近い書き方をすると、やはりそれもイメージしづらい形かもしれない

詳細な実装方針とか、言語としての仕様とか、いろいろな作業フェーズで必要な情報は変わってくると思います。 それでいうと、今回の発端となったフェーズでは

今から私たちが作ろうとする機能は、どんなデータを扱うのか

といった、より概念レベルでの、なんというか 身に沁みやすいもの で共有できるといいのだろうなと考えました。

TypeScript の型定義記法を選択した理由

特に深い意図はなくて、単純に「周りにそれを理解できる人が多かったから」です。

元々のきっかけとなった話し相手がフロントエンジニアだったということもあり

👳‍♀️ 「そういえば僕たちは TypeScript を使っています」
👤 「はい」
👳‍♀️ 「↓ こうじゃなくて……」

    authors:
        t.string :name, null: false
        t.string :pen_name

👳‍♀️ 「↓ これならどうよ」

    type Author = {
      name: string
      penName: string | null
    }

👤 「わかりやす! 特に `string | null` あたり」
👳‍♀️ 「なるほど。僕もそう思っていました」
👤 「せやったか」

TypeScript 風で良さそうだな? という気持ちになってきました。


また、デザイナーやPdM も、情報設計を TypeScript 風に記述していたことが判明しました。

新機能追加や改修のための概念データモデルを作成する際にも、既存のテーブル構成に対して積み重ねているため、この時点で既に TypeScript 風になっていると楽してもらえそうだなと考えました。


つまり

f:id:gongoZ:20201203021601p:plain

👳‍♀️ 「みんな TypeScript いけるじゃん! これにしよ!!」

まとめ

(論理)データモデルの共通言語として TypeScript は結構書き心地が良いなと感じました。

気合いが入っている時は図でしっかり書いた方が視覚的にモチベーションが高まりやすいこともあるので、ある程度いい感じに選んでいければと考えています。

今回の試みはまだ試行回数が少ないので何とも言えませんが、これをきっかけとしていい感じになっていけば嬉しい。嬉しい。

*1:"ユーザーストーリーをどのように実現するのか、デザイナーおよびエンジニアと仕様を検討します。概念データモデルの作成や、ワイヤの作成など、作るために必要な仕様の具体化を行います。" https://tech.smarthr.jp/entry/2020/01/23/141628

フォームローラーを使い始めました

f:id:gongoZ:20201027130732j:plain

毎日グリグリして一ヶ月以上経過しました。体の好調度が増した気がします。

※ 使う時はヨガマットを敷いています

経緯

一ヶ月前、会社の Slack ワークスペースでこんな発言をしました。

f:id:gongoZ:20201027130906p:plain

これ以前から、寝起き直後や仕事終わり後の体バキバキ状態をなんとかしたいなと思っていました。 特に肩や首周りは日常的に死んでおり、(最近は更新していませんが)フィットボクシングやリングフィットアドベンチャーを続けていなければ更に破壊されていたのではないかと思えるぐらいです。

そんな中、上の発言後に同僚から「これめっちゃいいですよ」と教えてもらったのが、フォームローラーでした。

amzn.to

「筋膜リリース」というワード自体は何度か耳にしたことがあり、とはいえ「筋膜をリリースってなんやねん」と思ってこれまでは手を出していませんでした。が、せっかくお勧めされたのでこの機会にやってみるかーとやってみました。

効果

ここからは完全に雰囲気の感想です。

  • 肩や首周りの、なんかこう稼動域が広がった気がする
    • 動かしづらいとか、なんか詰まってる感っていうのが柔らいだ
  • 背中がすごいスッキリする
    • 背中こんなに凝ってたのかってぐらい
    • なので、使い始め当初は動かしづらくてそこそこ痛かった
    • 今は気持ちいい
  • 時間はそんなに取られないため、気軽にできる
    • 仕事中に「ちょっと体固まってきた気がするから、ちょっとやるか」みたいな
    • そういう意味ではリモートワークのこの環境も適していたと言えるかも

とりあえず「良き」という感想です。

参考にした動画

「どうやって使えばいいんだ?」「効果的な動きは?」といったことについては、これも同僚から勧められた、以下の Youtube チャンネルが参考になりました。

www.youtube.com

チャンネルの中で、僕が普段のルーチンにいれている奴は以下の奴です。いろいろ合体しているやつなので便利。

www.youtube.com

まとめ

フォームローラーを習慣付けした結果、「それでも肩甲骨が痛くなったら、前日暴食しすぎて肝臓に負担がかかったんだな」って原因の切り分けが可能となりました。

PHPerKaigi 2020 に参加 & register_globals についてトークしました

phperkaigi.jp

経緯

新卒の頃から携わってきた PHP。しかし書かなくなって数年が経過し、そんな僕が PHP にできる恩返しといえば、 register_globals についての記録を後世に伝えることだと日々考えていました。

こんなこともあり、後日 PHPerKaigi の存在を知ったので、Proposal を出してみました。

発表資料

speakerdeck.com

YAPC::Asia Tokyo 2015 で発表した時 の内容と基本的には同じですが、加えて register_globals の歴史みたいなものも記載しました。昔話として読んでいただければ幸いです。

参加者としての感想

さらっと

  • そういえば PHP 系?のカンファレンスに出るの始めてかも
  • ビールがデプロイされる時刻が他イベントよりかなり早い気がした
  • PHPer Kaigi ということで、PHP そのものよりも PHPer がどうやって生きているかみたいな話が多かった
  • 長谷川カスタムバーガー美味しかった
  • トレーディングカードありがとうございます!
  • ゲリラLTで10分ぐらい喋ってる方がいて、後続の人の分が無くなってちょっと残念
    • ゲリラだしちゃんとしたレギュレーションも無かったと思うので、まあそういうものかという感想
  • Tシャツやパーカーは痩せたら着ます

おわり

おつかれさまでした!

インドカレー料理本を買いました

稲田 俊輔「南インド料理店総料理長が教える だいたい15分!本格インドカレーhttps://www.amazon.co.jp/gp/product/B084GJFHV7

会社 Slack に #趣味_スパイス料理 というチャンネル *1があり、そこでは同僚が日々美味しそうなカレーを作っては公開しています。ある日、このチャンネルで今回購入した料理本が紹介されていたので、試しに買ってみました。

本の内容としては

  1. ベースの作成手順(この本では『基本のマサラ』と呼称)
  2. あとはひたすら『ベース + ◯◯』を紹介

というシンプルなものです。とりあえずは最初に紹介されていた鯖水煮缶カレーを作ってみました。

f:id:gongoZ:20200213214833j:plain

スパイスからカレーを作るという体験が久しぶり*2というのもあり、美味しく感じました。本のとおりに作ることができれば美味しいものが出来上がるのだなという気持ちになれてよかったです。

ちなみに昨日作った時は勘でスパイスとかをぶちこんだので、よくわからない味になってしまいました。 反省して計量器具を購入して、まずはしっかり 1g 単位で揃えていきました。計算とは偉大。ちなみにこれ買いました。

https://www.amazon.co.jp/gp/product/B07H55WBWV

これからも時々は作っていこうと思います。

*1:#部活_スパイス探求部という、食べにいく専門の部活もあります

*2:大学時代に研究室でカレーパーティみたいな催しがあり、そこで少しだけやりました。もう忘れました。

Fit Boxing とリングフィットアドベンチャーを始めて3ヶ月が経過しました

リングフィットアドベンチャー Fit Boxing

Fit Boxing のスタンプある場所は、リングフィットアドベンチャーもやっています。

前回(三ヶ月前)はこちら: https://gongo.hatenablog.com/entry/2019/10/01/212238

記録

開始 (10月上旬) 前回 今月 増減 (前回/開始)
98.8 93.6 92.8 -0.8 / -6.0 kg

f:id:gongoZ:20200131234545p:plain:w450

  1. 予定どおり10月で継続記録が途絶えた
  2. そのあとリングフィットアドベンチャーを購入する
  3. 「毎日はもういいから、今度はできる日にがっつりやろう!」の精神で、がっつりやっていくつもりでいた
  4. 続かなかった

というのが2019年末。やはり継続する方を優先させた方がいいな、ということで現在は

  • リングフィットアドベンチャー
    • 運動負荷を減らして(今は23)、最初に「クールダウンしますか?」って聞かれるタイミング(約10分)で終わり
    • ものすごい余裕ある時は10分追加
  • Fit Boxing
    • デイリーは体力強化・全身・10分
    • ものすごい余裕がある時は10〜20分追加
  • これらをセットで

こんなプランにしています。ちなみに体脂肪と筋肉量は下図:

体脂肪 筋肉量
f:id:gongoZ:20200131235647p:plain:w400 f:id:gongoZ:20200131235636p:plain:w400

いい感じのような、あまり変わらないような

体調

  • 問題なし
  • 数値は変わらないけど、体感はなんとなく絞れてる気はする

ネクストアクション

91kg 台!

2019年ふりかえり

来年もよろしくお願い致します

仕事

引き続き SmartHR でやっております。
仕事内容は 転職2年後ふりかえり の2019年分を御参照ください。

来年からは今までと違ったものを触ることになりそうなので、やっていきます。

健康

1年間で約10kg減りました。えらい。

体重 体脂肪
f:id:gongoZ:20191231100811p:plain:w350 f:id:gongoZ:20191231100800p:plain:w350

5月あたりから FitBoxing を、加えて10月下旬よりリングフィットアドベンチャーを開始しました。 12月に入ってからは諸事情によりサボり気味なのですが、来年から、やっていきます。

ちなみに12月末の急下降ですが、いろいろあってファスティングをやっておりました。

食事

今年印象深かったのは幡ヶ谷に密集するカレー屋です。幡ヶ谷最高

来年も、やっていきます。

技術

6月下旬、久しぶりに登壇というものをしてきました。

来年の話ですが、2月に PHPerKaigi で登壇予定なので、 register_globals について聴きたいという方は是非

ゲーム

あいかわらずスマホゲーが中心です。恒常プレイリストとして今年からメギド72が加わりました。現在は以下をプレイしています。

据置きゲーとしては以下をプレイしました。

  • ライザのアトリエ
    • ライザ…おまえ…
  • デスストランディング
    • 荷物運びよりも国道復旧やジップライン建設がメイン

JVM (Java Virtual Machine) on Emacs

メリークリスマス!本記事は Emacs Advent Calendar 2019 の25日目の記事です。

まずはこちらをご覧ください。

f:id:gongoZ:20191225010105g:plain

java コマンドと同様、Emacs でも "Hello, World!" を出力していますね。 HelloWorld.java を書き換えてコンパイルしたあとも、java コマンドの結果と同じ文字列を出力しています。

これはどういうことかというと、 純度 100% Emacs Lisp.class ファイルを解析・実行しています。 つまり EmacsJVM となった瞬間です。おめでとうございます 🎉

当然ながら JVM 全てをカバーできているわけではなく、基本的な部分だけを実装してあります。 今後も開発は続くかもしれないし、続かないかもしれません。


本記事の概要

  • 書いてあること
    • Emacs Lisp でそれっぽく動くところまでの話
  • 書いていないこと
    • JVM とは何か
    • .class ファイルフォーマットの詳細な解説

開発に至った経緯

Twitter を眺めていたある日、下記スライドが目に止まりました。

🤔「ふむふむ、PHPJVMエミュレーターを…」

いわゆる JVM 言語と呼ばれている JRuby や Kotlin とかと気持ち的には同じものかな? みたいなふわっとした認識でした。 その後、他の言語でも同じことやってるものが無いか軽くしらべてみたら、いろいろありました (参考資料)

🤔「なるほどね」
😳「ところで Emacs でもできるのかな!?」

JVM 言語における Lisp 実装といえば Clojure を思いつきますが、その流れで Emacs Lisp で探してみました。 ぱっと見つからなかったのでおそらく無いのでしょう。よし、ならば作ってみよう!

という気持ちが沸きあがり、勢いで着手しました。去年と似たような気持ち です。

開発のおはなし

JVM 実装の流れは先人達の資料に書かれているので、ここでは

  • Emacs Lisp ではどうやって実現したか
  • 苦労したところ

あたりをささっと記していきます。

1. .class ファイルの解析

これができなければ先に進めません。早速 .class ファイルのフォーマットを確認しました。

Chapter 4. The class File Format

例えば1つの .class ファイル全体のフォーマットはこう定義されています。

ClassFile {
    u4             magic;
    u2             minor_version;
    u2             major_version;
    u2             constant_pool_count;
    cp_info        constant_pool[constant_pool_count-1];
    u2             access_flags;
    u2             this_class;
    u2             super_class;
    u2             interfaces_count;
    u2             interfaces[interfaces_count];
    u2             fields_count;
    field_info     fields[fields_count];
    u2             methods_count;
    method_info    methods[methods_count];
    u2             attributes_count;
    attribute_info attributes[attributes_count];
}

via 4.1. The ClassFile Structure - Chapter 4. The class File Format

Emacs で決められたフォーマットを持つバイナリファイルを解析する時によく使われるライブラリとして bindat が挙げられます(要出展)。 そこで、今回 ClassFile に対応する bindat spec を下記のように定義してみました。

;; bindat の spec で指定する u8 は 1byte を表しており
;; JVM の spec に記載されている u1 と同じ
(defconst jvm--classfile-spec-classfile
  '(
    (magic u32)
    (minor-version u16)
    (major-version u16)
    (constant-pool-count u16)
    (constant-pool repeat (eval (- last 1)) (struct jvm--classfile-spec-cp-info))
    (access-flags u16)
    (this-class u16)
    (super-class u16)
    (interfaces-count u16)
    (interfaces repeat (interfaces-count) u16)
    (fields-count u16)
    (fields repeat (fields-count) (struct jvm--classfile-spec-field-info))
    (methods-count u16)
    (methods repeat (methods-count) (struct jvm--classfile-spec-method-info))
    (attributes-count u16)
    (attributes repeat (attributes-count) (struct jvm--classfile-spec-attribute-info))
    ))

定義した bindat spec で実際に .class ファイルを解析してみましょう。

(let* ((data (with-temp-buffer
               (insert-file-contents-literally "./examples/HelloWorld.class")
               (buffer-substring-no-properties (point-min) (point-max))))
       (decoded (bindat-unpack jvm--classfile-spec-classfile (encode-coding-string data 'raw-text))))
  (bindat-get-field decoded 'magic))
;; => #xcafebabe

うまくいきました。

1-1. 苦労したところ

ClassFile は一部可変領域があります。それが ConstantPool です。(ConstantPool が何か、という説明は今回は割愛します)

ClassFile {
    u4             magic;
    u2             minor_version;
    u2             major_version;
    u2             constant_pool_count;
    cp_info        constant_pool[constant_pool_count-1];  ← こいつ!!!
    u2             access_flags;
    attribute_info attributes[attributes_count];

   (省略)

一見、 constant_pool_count の数だけ cp_info なるものを用意すれば良いのじゃろ? と思わせてきますが、実はこの cp_info が厄介で

cp_info {
    u1 tag;
    u1 info[];
}

via https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.4

この info[] というメンバ、サイズが指定されていません。何かというと tag の値によって領域が変わる というものです。

例えば CONSTANT_Class_inf だとこうなり:

CONSTANT_Class_info {
    u1 tag;
    u2 name_index;
}

CONSTANT_Fieldref_info だとこうなります:

CONSTANT_Fieldref_info {
    u1 tag;
    u2 class_index;
    u2 name_and_type_index;
}

このようなパターンを bindat spec にどうやって落とせば良いか。今回は下記のように定義しました。

(defconst jvm--classfile-spec-cp-info
  '((tag u8)
    (union (tag)
           (7 ;; CONSTANT_Class_info
            (name-index u16))
           (9 ;; CONSTANT_Fieldref_info
            (class-index u16)
            (name-and-type-index u16))
    ;; 省略
    ;; 全体はこちら https://github.com/gongo/emacs-jvm/blob/ea239628d77a893ba28a950c6dc304b2ea5cd5d8/jvm-classfile.el#L13-L53

union キーワードを使い 引数に指定した値によって、その後の spec を切り替える という方法で実現できました。

2. 命令を実装していく

なんやかんやあり、なんとか .class ファイルの解析が完了しました。 あとは解析結果から main 関数の処理 を抽出し、それを1つずつ順番に実行していくだけです。

その実行していく命令も、当然実装していかなければなりません。やりましょう!

Chapter 6. The Java Virtual Machine Instruction Set

まずは、どの命令の実装が必要となるかを調べる必要があります。これは javap -v あたりで確認できます。

$ javap -v HelloWorld
  (省略)
  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #3                  // String Hello, World!
         5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return
  (省略)

javap -v の出力から

  • 0: getstatic
  • 3: ldc
  • 5: invokevirtual
  • 8: return

これらが必要であることがわかります。あとはこれらを仕様書どおりに実装していくだけです。

https://github.com/gongo/emacs-jvm/blob/ea239628d77a893ba28a950c6dc304b2ea5cd5d8/jvm-instruction.el

2-1. 苦労したところ

HelloWorld 系でよく目にする System.out.println は標準パッケージであり、.class ファイルが無い(どっかで探せばあるのか?)ため、どのように実装すれば良いでしょうか。 他の言語で実装している先人を参考にした結果、「とりあえずそれっぽいものを用意する」のが主流らしいので emacs-jvm でもそのようにしてはいるのですが、正直うまく書けませんでした。。。

https://github.com/gongo/emacs-jvm/blob/ea239628d77a893ba28a950c6dc304b2ea5cd5d8/jvm-class.el#L25-L33

納得いかないので、いつか綺麗に書きたいです。こういう奴って Emacs Lisp だとどう表現するのがすっきりするのだろうか。

3. おまけ

3-1. なぜ ring を使っているのか

https://github.com/gongo/emacs-jvm/blob/ea239628d77a893ba28a950c6dc304b2ea5cd5d8/jvm-classfile.el#L173-L176

operand-stack は文字どおり stack なので、当初は pop push できる list で実装していました。 しかし各 instruction 内で pop push した結果が、呼び出し元 (今回でいえば jvm--classfile-run-method) に反映されない的なよくある話になり

「dynamic binding とかで参照できるようにしてやろうぜ」
「それ用の struct を定義して渡してあげればいいんじゃね?」

とかいろいろあったのですが、面倒くさくなって今回は ring にしました。動きもそれっぽいのでひとまずはこれで。

ここもいつかスッキリしたいポイント。

3-2. Signature の解析

Signatures encode declarations written in the Java programming language that use types outside the type system of the Java Virtual Machine. They support reflection and debugging, as well as compilation when only class files are available.

https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.9.1

例えば main(String[] args) の Signture は ([Ljava/lang/String;)V になります。

なぜこいつの解析が必要になるかというと、こいつに記述されている引数の型や数を確認しなければならないのです。(引数の数だけ stack から pop しないといけない、とかそういう実装がある)。

今回は php-java の実装 を参考に、無理矢理 Emacs Lisp に落としました。(感謝。。。)

jvm--util-parse-signature

まとめ

EmacsJVM にもなれる!お疲れ様でした!!

余談

それっぽく動きはしました。しかしまだまだ足りない機能があることは当然ながら、実装そのものもうまくいかずに結構やっつけコードになってしまったので悔しい限りです。さすがに全ての JVM エミュレートは無理なのですが、いつか「お、結構いけるな」ぐらいのレベルまで再現できるようになるといいな。

参考資料