TECH PLAY

株式会社モバイルファクトリー

株式会社モバイルファクトリー の技術ブログ

222

はじめに offsetWidth や offsetHeight などの幅や高さを取得する関数たちは、 display: none になっている場合、0を返します。 Vue.jsで要素の表示非表示に v-show を用いている場合、 v-show はその要素に display: none を付与して制御を行うので、非表示中の offsetWidth は0として返ってくることになります。 < template > < div > < p v-show= "false" > ここのoffsetWidthは0pxです! </ p > </ div > </ template > ハマりやすいポイント offsetWidth などは、自分の要素だけでなく、親要素に display: none が付与されていても0を返します。 注意したいのは、親コンポーネントで v-show を用いている(コンポーネントの表示制御を行っているなど)場合です。 Parent.vue < template > < div > < div v-show= "flag" > < Child /> </ div > </ div > </ template > < script > import Child from "./Child" ; export default { name: "Parent" , components: { Child, } , data () { return { flag: false , } ; } , mounted () { setTimeout (() => { this .flag = true ; } , 1000) ; // 描画されて1秒後にtrueになる } , } ; </ script > Child.vue < template > < p ref= "target" > hello, world! </ p > </ template > < script > export default { name: "Child" , mounted () { console.log ( `pタグのoffsetWidthは ${ this .$refs.target.offsetWidth} px です!` ) ; // pタグのoffsetWidthは 0px です! } , } ; </ script > この場合、 v-show では表示非表示に関わらず描画が行われ、その後 display: none で制御を行います。そのため子コンポーネントの mounted のタイミングでは v-show が false なので、結果は0pxになってしまいます。 2つの解決方法 offsetWidth を取得できるようにする方法を2種類紹介します。 フラグを受け取り v-show が true になった直後に取得する 親で用いているフラグを受け取って watch することで、 true になった瞬間に offsetWidth を取得します。 mounted のタイミングでは取得できませんが、 watch と組み合わせれば任意のタイミングで利用できるようになります。 Parent.vue < template > < div > < div v-show= "flag" > < Child :flag= "flag" /> <!-- flagを子コンポーネントに渡す --> </ div > </ div > </ template > < script > // 省略 </ script > Child.vue < template > < p ref= "target" > hello, world! </ p > </ template > < script > export default { name: "Child" , props: { flag: Boolean , } , watch: { flag ( newFlag ) { if ( newFlag ) { console.log ( `pタグのoffsetWidthは ${ this .$refs.target.offsetWidth} px です!` ) ; // 取得できた! } } , } , } ; </ script > v-if に変えて遅延描画にする 切り替えコストが上がってしまいますが、 v-if の遅延描画を活用することで、 mounted フックで取得できるようになります。 Parent.vue < template > < div > < div v-if= "flag" > <!-- v-ifに変更する --> < Child /> </ div > </ div > </ template > < script > // 省略 </ script > Child.vue < template > < p ref= "target" > hello, world! </ p > </ template > < script > export default { name: "Child" , mounted () { console.log ( `pタグのoffsetWidthは ${ this .$refs.target.offsetWidth} px です!` ) ; // 取得できた! } , } ; </ script > まとめ offsetWidth が取得できない場合の解説と解決方法の紹介でした。親コンポーネントを探しに行かないといけない点は盲点だったので、同じ問題にハマらないように気をつけたいですね!
アバター
チーム開発で、指標をうまく使えると便利ですが、この「うまく使う」というのはなかなか難しく、罠はあると思っています。 今回は、この罠を、信号機に例えて、説明してみたいと思います。他にも、罠があればぜひ教えてください! 壊れた信号 壊れた信号 データが壊れていて、信用できない そのまま放置されている 信号無視 信号無視 人によって、良し悪しの判断基準が異なる 基準を用意しても無視をする 信号だらけ 信号だらけ 指標がありすぎて、どこを見れば良いかわからない 分かる人にしか分からない状態 工事のいる信号 工事のいる信号 信号を見るのに、工事業者に依頼をしなければならない つまり、データを見るのに手間がかかる さいごに ここでは行動を妨げる罠を紹介しました。 裏を返せば、良い行動を引き出す信号、作っていきたいですね・・!!現場からは以上です。
アバター
こんにちは、新卒エンジニアの id:kaoru-k_0106 です 何をしたか 私のチームでは、コミットメッセージの先頭にチケット番号を入れるルールがあります。 例えば、 PROJECTNAME-123 〇〇の処理を変更した だと、 PROJECTNAME-123 の部分がチケット番号です。 最初はこれを手動で入力していたのですが、時々入力を忘れることがあり自動挿入しようと思いました。 ちょうど、トピックブランチ名がチケット番号だったので、ブランチ名を使う方針で実装をしました。 導入手順 その1 Gitのコミットテンプレートを設定する Gitのコミットテンプレートとは、コミットメッセージエディタを開いたときにデフォルトで設定されている文字列です。 この設定は、次のようにテンプレートファイルを作成し、 git config で設定できます。 % echo ' [branch-name] ' > ~/.commit_template % git config commit.template ~/.commit_template その2 Git Hooksでコミットテンプレートをブランチ名に置き換える 次に、このコミットテンプレート内の [branch-name] をブランチ名に置き換えるためにGit Hooksを使用します。 具体的には、 .git/hooks/prepare-commit-msg を実行権限をつけて置きます。 #!/bin/sh current_branch = $( git rev-parse --abbrev-ref HEAD ) sed -i " s/ \[ branch-name \] / $current_branch / " $1 # Macだと以下のようにする # sed -i "" "s/\[branch-name\]/$current_branch/" $1 これで、ブランチ名が自動で挿入されるようになりました。 実際に挿入されるか確かめてみます。 動作確認 git commit を叩くと、 [branch-name] がブランチに置き換わった状態でコミットメッセージエディタが開きます。 ちなみに、Visual Studio Code (VSCode)のソース管理機能でコミットした場合もブランチ名に置き換わります。 入力欄ではテンプレートの [branch-name] がそのまま表示されていますが、コミットするとブランチ名に置き換わります。 おわりに これだけでコミット時にブランチ名を自動挿入することができました。 ミスを減らすことができ、チームの開発効率にも貢献できると思います!
アバター
はてなさんのコンテキストクイズに拙作の Contextual-Diag で答えてみました。これでコンテキストマスターですね!? github.com use Test2::V0; use CDD; like( warnings { length ( cdd ) }, [ qr/wanted SCALAR context/ , qr/evaluated as STR/ ], 'length <ここ> ... STR in SCALAR' ); like( warnings { if ( cdd ) {} }, [ qr/wanted SCALAR context/ , qr/evaluated as BOOL/ ], 'if (<ここ>) ... BOOL in SCALAR' ); like( warnings { for my $i ( cdd ) {} }, [ qr/wanted LIST context/ ], 'for my $i (<ここ>) ... LIST' ); like( warnings { package Hoge { sub new { bless {}, $_[ 0 ] } sub hello {} } my $obj = Hoge->new; $obj->hello ( cdd ) }, [ qr/wanted LIST context/ ], '$obj->method(<ここ>) ... LIST' ); like( warnings { my $x = cdd }, [ qr/wanted SCALAR context/ ], 'my $x = <ここ> ... SCALAR' ); like( warnings { my ( $x ) = cdd }, [ qr/wanted LIST context/ ], 'my ($x) = <ここ> ... LIST' ); like( warnings { my @y = cdd }, [ qr/wanted LIST context/ ], 'my @y = <ここ> ... LIST' ); like( warnings { my %hash = ( key0 => 'hoge' , key1 => cdd, ) }, [ qr/wanted LIST context/ ], q!my %hash = ( key0 => 'hoge', key1 => <ここ> ); ... LIST! ); like( warnings { scalar ( cdd ) }, [ qr/wanted SCALAR context/ ], 'scalar(<ここ>) ... SCALAR' ); like( warnings { cdd }, [ qr/wanted VOID context/ ], '<ここ>; ... VOID' ); # 注: sortの幾通りかの使い方を知っている前提で、テストしている subtest 'sort <ここ>' => sub { # sort BLOCK LIST like( warnings { my @a = sort { cdd } ( 'a' , 'b' ) }, [ qr/wanted SCALAR context/ ], 'sort <ここ> / CASE: sort BLOCK LIST ... BLOCK context is SCALAR' ); # sort SUBNAME LIST like( warnings { my @a = sort cdd ( 'a' , 'b' ) }, [ qr/wanted SCALAR context/ ], 'sort <ここ> / CASE: sort SUBNAME LIST ... SUBNAME context is SCALAR' ); # sort LIST like( warnings { my @a = sort (cdd( 'a' , 'b' )) }, [ qr/wanted LIST context/ ], 'sort <ここ> / CASE: sort LIST ... LIST' ); }; done_testing;
アバター
TSの型迷宮に迷い込んだエンジニアの id:dorapon2000 です。 ディレクトリのパーミッションを調べるとき、皆さんどうしていますか。例えば /etc のパーミッションを確認したいとき、以前の僕はこうです。 ❯ ls -l / total 104 drwxr-xr-x 2 root root 4096 6 月 28 18:34 bin drwxr-xr-x 3 root root 4096 6 月 28 18:35 boot drwxr-xr-x 15 root root 3640 11 月 16 10:02 dev drwxr-xr-x 128 root root 12288 11 月 2 10:55 etc drwxr-xr-x 4 root root 4096 4 月 6 2021 home drwxr-xr-x 22 root root 4096 6 月 15 2020 lib drwxr-xr-x 2 root root 4096 6 月 28 18:33 lib64 drwx------ 2 root root 16384 4 月 7 2020 lost+found drwxr-xr-x 2 root root 4096 4 月 7 2020 media drwxr-xr-x 2 root root 4096 4 月 7 2020 mnt drwxr-xr-x 4 root root 4096 6 月 1 16:33 opt dr-xr-xr-x 173 root root 0 11 月 16 10:02 proc drwx------ 9 root root 4096 10 月 6 19:26 root drwxr-xr-x 30 root root 1120 11 月 16 10:03 run drwxr-xr-x 2 root root 12288 6 月 28 18:34 sbin drwxr-xr-x 6 root root 4096 3 月 9 2021 snap drwxr-xr-x 2 root root 4096 4 月 7 2020 srv dr-xr-xr-x 13 root root 0 11 月 16 10:24 sys drwxrwxrwt 10 root root 12288 11 月 16 10:25 tmp drwxr-xr-x 11 root root 4096 6 月 1 16:33 usr drwxr-xr-x 14 root root 4096 3 月 11 2021 var うっ、数が多くてため息が出てしまいます... -d オプションを使うと次のようになります。 ❯ ls -ld /etc drwxr-xr-x 128 root root 12288 11 月 2 10:55 /etc もっと早く知りたかった...
アバター
 次のコードの通り、 DBIx::Sunny を利用した際、 $dbh->select_one を呼び出せるにも関わらず、 $dbh->can('select_one') が false となります。   can の挙動が、デフォルトと異なり、一見すると困惑します。 use Test2::V0; use DBIx::Sunny; use Types::Standard qw(HasMethods) ; my $dsn = "dbi:SQLite:dbname=test.db" ; my $dbh = DBIx::Sunny-> connect ( $dsn , 'root' , '' ); ok ! $dbh->can ( 'select_one' ), '不思議な挙動' ; ok $dbh->select_one ( 'SELECT 1' ); ok $dbh->UNIVERSAL ::can( 'select_one' ); done_testing;  これは、DBIが can を上書きし、Driverで実装されているか、あるいはDBIでデフォルトで提供されているメソッドかどうか判定するためです。デフォルトの挙動で動かしたいのであれば、 UNIVERSAL::can で確かめられます。 metacpan.org
アバター
VSCode でホバーして型情報を見ようとすると、交差型はプロパティが展開されません プロパティの型を展開する Mapped Types を通すことで省略せずにプロパティを見ることができます。プロパティが交差型になっていることもあるので、再帰的にプロパティを Mapped Types に通すような型を定義しておいて、通すことでプロパティを全て確認できます type Expand < T > = T extends object ? T extends infer O ? { [ K in keyof O ] : Expand < O [ K ] > } : never : T type Temp = Expand < `確認したい型` > 厳密でなくとも簡単にどんなプロパティがあるか把握したいだけなら組み込みの Required や Partial を使うのが手軽です。ただし、あくまでデバッグ用途でそれぞれ省略可能プロパティになる/でなくなるので注意が必要です プロパティが多い時に省略しない これで展開した型が見られるようになりましたが、展開した型を表示するとプロパティの数が多い時に省略されてしまいます noErrorTruncation オプションを有効にすることでプロパティ数が多いときでも全ての型情報を確認できます // tsconfig.json { compilerOptions: { + "noErrorTruncation": true, } } 拡張機能で確認する 一度型変数に置くのがめんどくさければ、展開された型を表示できる VSCode 拡張機能を作ったので、これを使うことで手軽に確認することができます ts-type-expand - Visual Studio Marketplace からインストールできます 画像のように、VSCode の UI 上から展開されたプロパティを確認することができます
アバター
こんにちは。ブロックチェーンチームのソフトウェアエンジニアの id:odan3240 です。 ブロックチェーンチームでは NFT を販売するための Uniqysマーケットプレイス (以下、ユニマ)と、その NFT を販売するための管理画面(以下、管理画面)を開発しています。ユニマはブロックチェーン上の NFT を日本円で売買可能なマーケットプレイスです。 ユニマの開発はブロックチェーンに関するサービスなので、ブロックチェーンに関する技術をゴリゴリに使って開発しているのでは?と考える方がいるかもしれません。しかし実際はそうではなく Web アプリケーションに関する開発は普通の技術スタックで開発しています。もちろん実装するロジックによってはブロックチェーンに関する知識が必要となる場合もありますが、マネージドサービスやフレームワークやツールを活用してコア機能の実装に集中できるように技術スタックを選択しています。 この記事ではユニマと管理画面の主に Web アプリケーションに関する技術スタックと、その採用理由について紹介します。 技術スタック TypeScript ユニマと管理画面のフロントエンドとバックエンドの全てで TypeScript を使用しています。モバファクはプロダクトのバックエンドには Perl が採用されることが多い企業でしたが、開発の効率化のために静的な型がほしいのと文法が JavaScript とかなり近くメンバーの学習コストも低いという点から、2018年に立ち上がったブロックチェーンチームでは立ち上げ当初から各プロダクトに TypeScript が採用されることが多いです。 ユニマと管理画面もこの流れをくんで TypeScript を採用しました。 NuxtJS ユニマと管理画面のフロントエンドの実装に NuxtJS を使用しています。Vue.js は他のチームでの採用実績があり社内のノウハウやリソースを流用できそうな点と、プロダクトの立ち上げ当初はエンジニアに比べるとデザイナのリソースが浮くことがたまにあり、いざとなればデザイナもフロントエンドの実装に参加できるように書き方が HTML/CSS に近い点が採用理由です。 TypeScript との相性を少しでもよくするために Vue.extend を使用してコンポーネントを定義して、VSCode の拡張機能に Vetur を使用しています。Vue.js は TypeScript と相性が悪いと言われますが拡張機能に Volar を利用することで、SFC の template の型推論やコンポーネントの props の指定の有無を静的にチェックしてくれます。また、Vue.js の v2 でも Composition API の defineComponent を使用すれば v3 と同様の開発体験を得ることができそうで、Vetur からの置き換えを検討中です。 ユニマは SNS からの流入が想定されているのでOGP のために NuxtJS を使って Lambda で SSR しています。 一方で、管理画面はどの画面もログインして使用するページであり、SNS からの流入も想定されていないため、SPA として S3 に静的ファイルをホスティングしています。SPA でもユニマとの開発体験を揃える、ビルド周りのシステムを隠蔽するなどの理由で NuxtJS を選択しています。 Vuetify 管理画面のフロントエンドの実装に UI ライブラリの Vuetify を使用しています。ユニマのフロントエンドは自由度の高いデザインを実現するために Atomic Design をベースにフルスクラッチで UI コンポーネントを実装しています。一方で、管理画面に求められるのは管理画面的な UI なので、Vuetify を採用してフロントエンドの実装コストを削減することを目的としています。 NestJS ユニマと管理画面のバックエンドは同じサーバを参照していて、 NestJS を使用して実装しています。 NestJS は生の Express を採用するよりも、規約の従って開発が可能なこと、DI の仕組みによって依存関係をモックしやすくテストが書きやすいこと、周辺のモジュールが整っていてコアの開発に集中できそうなこと、などが採用理由です。 OpenAPI フロントエンド向けに REST API を提供しています。NestJS にはバックエンドのコントローラーから OpenAPI の定義書を自動生成するモジュールがあります 1 。自動生成された定義書は openapitools/openapi-generator-cli を使用してフロントエンド向けの TypeScript のコードをさらに自動生成しています。つまり、バックエンドでコントローラーを定義すると、それを呼び出すフロントエンドのコードが自動生成される仕組みを用意しています。GraphQL を採用しなかった理由は、バックエンドの API を SaaS 用の API として公開する計画がプロジェクトの立ち上げ当初からあったからです。公開用の REST API とフロントエンドで使用する GraphQL の両方を実装、管理するより、公開用の REST API を使用してフロントエンドを実装するほうが作成するべき API が1つだけになるので、メンテナンスのコストが下がると判断しました 2 。 TypeORM ORM には TypeORM を使用しています。過去のプロジェクトで採用実績があったのと、NestJS に TypeORM のモジュール 3 があったので採用しました。TypeORM を採用した感想は、マイグレーションの仕組みが用意されていたりと便利ですが、レコードを select してくるときの型安全性がないことや、テーブルを多数 join すると SQL の実行速度とは別でパフォーマンスが劣化するなどの問題 4 が知られていて一長一短だなと思いました。特に select 時の型安全性がないのは、TypeScript を採用しているメリットを下げることにつながるので、次に新しいプロジェクトが立ち上がるなら他のライブラリを検討すると思います。 Terraform AWS のリソースの IaC 化に Terraform を使用しています。IaC 化の手段として、フロントエンドとバックエンドと言語を揃えるために AWS CDK も候補にありました。しかし、Terraform が会社の横断的にインフラを担当しているチームによって全社的に導入されそうな雰囲気があったことと、 terraform import コマンドの存在から Terraform を採用しました。 terraform import は AWS にある既存のリソースを Terraform 管理下にするためのコマンドです。ユニマの開発におけるリリースの流れは、ステージング環境で動作確認を行った後に本番環境にデプロイを行う流れです。新しくインフラのリソースを作成するときは、AWS のマネージドコンソールでステージング環境にリソースを作成して terraform import で Terraform 化した後に、この Terraform をベースに本番環境にリソースを作成します。この terraform import を使用した一連の流れで作業を行うことで、ステージング環境と本番環境が限りなく一致する、マネージドコンソールから直感的にリソースを作成できる、といった利点があります。 MySQL データストアには MySQL (Amazon Aurora) を使用しています。まず最初に「Ethereum 上の一部データをデータストアに取り込み、集計処理を行う」という用途がメインになると考えて RDB を選択しました。そして社内の他のチームでの採用実績もあり、MySQL の設定や運用のノウハウを参考にできると考えて MySQL を選択しました。 バックエンドにマルチテナント機能を実装するときになって PostgreSQL の Row Level Security 機能を知って「PostgreSQL の方が良かったのでは?」と後悔しました。次データストアを選ぶときは PostgreSQL の機能も含めて検討したいです。 Truffle/TypeChain これらはどちらも Ethereum 上で動くスマートコントラクトの実装を補助するツールです。Truffle はコントラクトのデプロイやマイグレーションの管理を行い、TypeChain はスマートコントラクトのメソッドを TypeScript から呼び出すときに、型を自動生成するためのツールです。 Truffle は当時はコントラクトの管理を行うツールのデファクトスタンダードでしたが、最近は Hardhat がその座を奪いつつあるという印象です。Hardhat はデバッグの容易さや多数のプラグインがコミュニティによって開発されており、コントラクト実装時の開発体験の改善が期待できます。機会を見て Truffle を Hardhat に置き換えるか、新規プロジェクトでは Hardhat を採用したいと考えています。 TypeChain に関しては以前に紹介記事を書いたのでそちらを御覧ください。 tech.mobilefactory.jp Infura/IPFS/Torus これらはユニマで使用しているブロックチェーン関係の SaaS やプロトコルです。 Infura はマネージドな Ethereum API を提供する SaaS IPFS は分散してファイルを管理できるプロトコル Torus は非カストディに秘密鍵を管理する SaaS ブロックチェーン関係の技術スタックについて後日別の記事で詳細を紹介できればと考えています。 終わりに ユニマと管理画面の技術スタックの紹介をしました。 これらのスタックに興味を持った方やもっと詳しく話を聞いてみたい方はカジュアル面談をしてみませんか?以下からエントリーしてフリーコメント欄に「アドベントカレンダーを見た」と記載してエントリーしてください。 open.talentio.com OpenAPI (Swagger) | NestJS - A progressive Node.js framework ↩ Auth0 の API ドキュメント の anything that can be done through the Auth0 dashboard (and more) can also be done through this API という状態が理想です ↩ Database | NestJS - A progressive Node.js framework ↩ Performance of query with many relations · Issue #3857 · typeorm/typeorm ↩
アバター
こんにちは!エンジニア組織開発責任者の id:kfly8 です。 今年もモバイルファクトリーのAdvent Calendarをお送りします🎉 今年のアドベントカレンダーは「 今日から使える技術 」をテーマにコンパクトにお届けしていきます! *1 今予定しているキーワードを見ると・・「TypeScript」「Vue.js」「Perl」「CLI」「MySQL」「AWS」「地図」「プロダクトマネジメント」などがあります。ぜひお楽しみください! 一覧 12/1 tech.mobilefactory.jp 12/2 tech.mobilefactory.jp 12/3 tech.mobilefactory.jp 12/4 tech.mobilefactory.jp 12/5 tech.mobilefactory.jp 12/6 tech.mobilefactory.jp 12/7 tech.mobilefactory.jp 12/8 tech.mobilefactory.jp 12/9 tech.mobilefactory.jp 12/10 tech.mobilefactory.jp 12/11 tech.mobilefactory.jp 12/12 tech.mobilefactory.jp 12/13 tech.mobilefactory.jp 12/14 tech.mobilefactory.jp 12/15 tech.mobilefactory.jp 12/16 tech.mobilefactory.jp 12/17 tech.mobilefactory.jp 12/18 tech.mobilefactory.jp 12/19 tech.mobilefactory.jp 12/20 tech.mobilefactory.jp 12/21 tech.mobilefactory.jp 12/22 tech.mobilefactory.jp 12/23 tech.mobilefactory.jp 12/24 tech.mobilefactory.jp 12/25 tech.mobilefactory.jp 過去のアドベントカレンダー qiita.com qiita.com qiita.com qiita.com qiita.com *1 : 140字くらいの記事でもOKというルールを今年は設定しています。
アバター
こんにちは、21卒エンジニアの id:d-kimuson です。 先日、プロダクトで使用している lodash を lodash-es に置き換えることで、バンドルサイズの削減をしました。 lodash を lodash-es に置き換える話はよくありますが、今回のプロダクトは運用歴が長く CommonJS と ESModules が混在している少し特殊な環境での試みだったので、知見を共有したいと思います。 利用されていないコードを消し、バンドルサイズを減らす lodash はバンドルサイズの大きなライブラリです。minified な状態で 69.9KB のサイズになります。 参考: lodash v4.17.21 ❘ Bundlephobia webpack にはデッドコードを削除する TreeShaking という機能があり、ライブラリのデッドコードを削除することができるのでバンドルサイズの削減に効果的です。 しかし、lodash は CommonJS で記述されていて、TreeShaking の効果を得にくいので ESModules で書かれている lodash-es を使用することで、効果的にバンドルサイズの削減をすることができます。 長いので以降 CommonJS は CJS, ESModules は ESM と記します。 lodash から lodash-esに TreeShaking が効くように置き換える このプロダクトでは、CJS で記述されたモジュールと ESM で記述されているモジュールが混在しているので、個別に置き換えていきます。 ESModules で記述されているモジュールを置き換える ESM の置き換えはシンプルです。 lodash-es を使うようにする 全体ではなく、使う関数のみ個別でインポートする 形に変更します。 -import _ from 'lodash'; +import { defaults } from 'lodash-es'; // ... -const obj = _.defaults(obj1, obj2); +const obj = defaults(obj1, obj2); あとは webpack が自動的にデッドコード( defaults 以外) を削除してくれます。 CommonJSで記述されているモジュールは、CommonJS のまま TreeShaking が効くように置き換える TreeShaking が効く条件は webpack が TreeShaking に対応していること webpack4: ESM のみ webpack5: ESM, CJS 両対応 (効果は ESM のほうが大きい) 使用する値のみ import していること です。 このプロダクトでは CJS のコードも多く残っていますが、webpack5 を利用しているので「CJS のままでも TreeShaking の効果を得られるが、ESM にしてから置き換えるとより効果が大きい」という状態でした。 ですので CommonJS のまま置き換える (楽だけど効果は小さい) ESM に置き換えつつ、lodash-es に置き換える (大変だけど効果が大きい) の選択肢があります。 詳しくは触れませんが、webpack 環境で ESM に置き換えるときには、default export の場合、同時にそのモジュールを参照している他のモジュールも ESM に置き換える、あるいは require 文を書き換えなくてはいけないという制約があり、2の内容で修正すると変更範囲が大きくなってしまうという懸念点がありました。 したがって、CJS で記述されている場合には 置き換えファイルを参照しているファイルに CJS で記述されているものが存在するか default export に置き換える必要があるか を確認して、変更範囲がそのファイルのみに閉じている一部のものは ESM に置き換えますが、それ以外の大部分のソースコードでは CJS のまま置き開ける方針を取ることにしました。 変更範囲がファイル内に閉じているものは以下のように ESModules にしつつ置き換えます。 // 参照元がCJSでかつdefault_export -const _ = require('lodash'); +const { map } = require('lodash-es'); -const obj = _.defaults(obj1, obj2); +const obj = defaults(obj1, obj2); 変更範囲がファイル外に及ぶものは、ESModules には置き換えず、CJS のまま TreeShaking だけ効くように個別の値をインポートします。(※後述しますが、この方法ではTreeShakingが効かないので、別の方法を取る必要があります) // それ以外 -const _ = require('lodash'); +import { map } from 'lodash-es'; -const obj = _.defaults(obj1, obj2); +const obj = defaults(obj1, obj2); これで CJS でも置き換えが完了しました。 CJS から lodash-es を読むとむしろバンドルサイズが増える問題に遭遇 上記の指針で置き換えを完了したのですが、いざバンドルサイズを計測してみるとむしろバンドルサイズが増えていることがわかりました。 エントリ 元サイズ 置き換え後のサイズ entry1 493.95KB 534KB entry2 369.18KB 408.39KB entry3 362.57KB 401.97KB バンドルサイズが増えてしまっては置き換えた意味がないので、原因を調査しました 原因①: ESModules を CommonJS で利用するための変換コードが増えてしまった バンドルサイズを個別に見てみると、lodash のサイズが増えてしまっていることが分かったので、詳細な原因を探すために、以下のパターンでそれぞれ lodash 関係のバンドルサイズを計測してみました。 lodash を CJS で全体/個別 に読む lodash を ESM で全体/個別 に読む lodash-es を ESM で全体/個別を読む lodash-es を ESM で全体/個別に読む 以下が調査結果です。 ライブラリ 呼び出し 全体サイズ tree-shaking lodash CJS 69.2 KiB X lodash ESM 69.4 KiB X lodash-es CJS 148 KiB O (24.2 KiB) lodash-es ESM 81.1 KiB O (7.19 KiB) この結果から「lodash-es を CJS で依存解決しても TreeShaking でデッドコードを削除することができるが、総バンドルサイズが増えてしまうので一定数読んだ時点で lodash 時よりバンドルサイズが増えてしまう」ということがわかりました。 ただ、lodash-es を CJS で依存解決すると総バンドルサイズが増える原因はよく分からなかったので、実際に ESM, CJS で記述されたモジュールをそれぞれ ESM, CJS で依存解決するときの webpack のバンドルがどういう形になるのかを確認してみました。 webpack では概ね以下のようなコードにバンドルされています。 ※そのままでは読みにくいので、読みやすいようにかなり簡素化・改変しています。 ;(() => { var moduleMap = { 100: (initilizedModule) => { initilizedModule.exports = "export text" } } , moduleCache = {} function require(id) { if (id in moduleCachle) return moduelCache [ id ] ; const mod = moduelCache [ id ] = { exports: {} } moduleMap [ id ] (mod) return mod } (() => { // エントリーポイント const resolved = require(100) } )() } )() ESM だとエントリーポイント箇所に直接展開されますが、CJS や、CJS のモジュールを ESM から読んだり、ESM のモジュールを CJS から読むときには moduleMap にモジュール定義が書かれ、それをエントリーポイントから require を使って解決するようです。したがって、require や moduleMap 部分が ESM のみの場合と比べて余分なコードになります。 また、ESM で書かれたモジュールを CJS で読むときには、モジュール定義箇所で default export に改変するような処理が追加で必要になるため、以下のようにコード量が増えます。 var moduleMap = { - 100: (initilizedModule) => { + 100: (initilizedModule, _exports, require) => { + "use strict" - initializedModule.exports = "export text" + require.r(_exports), require.merge(_exports, { default: () => t }) + const t = "export text" }, }, この辺りがサイズ増加の原因になって増えているようです。実際のバンドルでは変数名は1文字なので use strict も比較的コード量が増える部分です。特に lodash-es の場合は、関数ごとにファイルが分かれているので、関数ごとに上記の余分なコードが生成されるため影響が大きくなっていると思われます。 全体のバンドルサイズが増える問題については、CJS から依存解決する以上必要なコードが増えているだけなので、CJS を使うのであれば避けようがないサイズの増加であることが分かります。 原因②: そもそも TreeShaking が効いていなかった また、原因①の調査をしていて分かったのですが、そもそも今回の書き換えのように const { findIndex } = require( 'lodash-es' ) の書き方だと、TreeShaking が働かないことも分かりました。 webpack では上記のコードを以下のように展開します。 ;(() => { var moduleMap = { 100: (initilizedModule) => { // lodash-es // ここに全 lodash-es のバンドルが書き出される } } , // ... 省略 (() => { // エントリーポイント const { findIndex } = require(100) } )() } )() 見てわかるように全てソースコードがバンドルに含まれてしまっています。 TreeShaking が効く書き方に直して、バンドルサイズを減らす 原因①については、将来的に ESM への置き換えが終われば解消しますが、CJS の状態では依存解決のための必要なコードが増えているだけなので許容するしかありません。 一方、原因②については以下のような書き方ならきちんと TreeShaking が働いて使われている箇所だけバンドルしてくれます。 // その1: require からメソッドチェーンで関数を呼ぶ require( 'lodash-es' ).findIndex() また、TreeShaking ではありませんが個別のモジュールを指定して依存解決することで使用する関数のみバンドルに含めることもできます。 デッドコードさえ削除できれば良いので、こちらの方法でも原因②は解消できます。 // その2: 使用する関数を指定して import する const { default : findIndex } = require( 'lodash-es/findIndex' ); findIndex() その1の書き方では、使用箇所に require を書く必要があることに対して、ESModules は基本的にモジュールの先頭でまとめて依存解決を書くので、将来的に ESM を置き換えることを考えてより近いその2の書き方で置き換えることにしました。 バンドルサイズを削減することができた CJS で個別に import するようにしたことでやや可読性が悪くなりましたが、これでプロジェクトの lodash を全て置き換えることができましたので、再びバンドルサイズを計測しました。 エントリ 元サイズ 置き換え後のサイズ 割合 entry1 493.95KB 459.74KB -6.9% entry2 369.18KB 327.9KB -11.2% entry3 362.57KB 325.01KB -10.4% バンドルサイズを 6-11% 程度削減できていることが分かります。 今回のプロジェクトでは無事削減することができましたが、lodash-es の総バンドルサイズが増える原因①については解消していないので、lodash-es の関数の使用量が一定量を超えるとむしろバンドルサイズが増えてしまうと推測されます。実際にバンドルサイズがどうなったか計測することをおすすめします。 また、このプロジェクトではフロントエンドエコシステムのアップデートに取り組んでいて、その中で CommonJS で記述されたモジュールを ESM に置き換えようとしています。置き換えが完了すれば lodash-es の総バンドルサイズが増える問題も解消されるので、さらにバンドルサイズの削減が期待できます。 おまけ 基本的には、この記事で書いた形で置き換えていけば良かったのですが一部特殊対応が必要だったので紹介します。 wrapper 記法を置き換える lodash には wrapper 記法という関数をメソッドチェーンで呼ぶことができる書き方があり、一部でこの記法が使われていました。 import _ from 'lodash' const arr1 = _.map( [ 1, 2, 3, 4, 5 ] ) // 基本的な書き方 const arr2 = _( [ 1, 2, 3, 4, 5 ] ).map((num) => num * 2) // wrapper 記法 メソッドチェーンで書かれているので、一意な置き換えができませんでしたが数が多くなかったので手動で書き直すことにしました。 chain を利用しない lodash に存在する chain 関数が lodash-es には存在しませんでした。 How to use chain with lodash-es while supports tree shaking? · Issue #3298 · lodash/lodash · GitHub 上記の Issue に書かれているように TreeShaking をサポートできないため追加してない様でした。 こちらも1箇所しか使われていなかったのと、無駄な処理が多く書かれていたことから、lodash を使わずに書き換える形になりました。 まとめ 今回は ESM と CJS が共存する環境下で lodash-es を置き換えることでバンドルサイズを削減する知見を共有しました。 バンドルサイズが大きいので lodash-es 使うようにしてサクッとサイズを減らしたいくらいの温度感だったのですが、実際やってみると CJS と ESM 周りで詰まることがあり、webpack で CJS と ESM 周りがどう依存解決するのか、webpack がどういう形でコードをバンドルするのか等学びが多かったです。 CJS 環境下では webpack5 を利用している lodash から使用している関数の量が一定以下 という条件付きではありますが、lodash-es に置き換え、TreeShaking が使えるようにすることでバンドルサイズを削減できることが分かりました。
アバター
こんにちは、エンジニアの id:mp0liiu です。 8月28日(土)の Learn Languages 2021 というイベントの Language Update というセッションで @charsbar さんと一緒に2018年以降のPerl5やPerlコミュニティの最新動向について話してきたので、そのとき話した内容に補足などしつつ記事にしていきたいと思います。 配信アーカイブは こちら から見れます。 時系列 2019/5/22 Perl5.30 リリース 2020/6/20 Perl5.32 リリース 2020/6/24 Perl7の発表 2021/5/21 Perl5.34 リリース Perl5.30 の変更点 正規表現や文字周りの細かい改善などはありますが、正直めぼしい変更点が見られないです。 Perl5.32 の変更点 isa 演算子の実装 値があるクラスのインスタンスもしくはそのサブクラスのインスタンスかを調べる演算子です。 use feature qw( isa ) ; package A { use Moo } my $obj = A->new; print '$obj is instance of A.' if $obj isa A; 今まででも isa メソッドで判定できましたが、メソッドの場合クラス名からでも呼び出せてしまうので、インスタンスの場合のみ判定したい場合は長い条件や経験者でないとわからないような workaround を書く必要がありました。 これを使えばその必要がなくなります。 連鎖比較機能 例えば $x < $y && $y <= $z を $x < $y <= $z といったふうに、同じ値を比較する条件式を簡潔に書けるようになりました。 間接オブジェクト記法を無効化できるように 間接オブジェクト記法は method $object @args というふうにメソッドを呼び出せる記法です。 Perl は割と英語として読み下せることを重視する文化があった *1 ので、昔はこのような書き方をするのが主流でした。 次のようなコードを動かしてみると無効にできるのが確認できます。 no feature 'indirect' ; new Foo; # Bareword found where operator expected, near "new Foo" 無効にしていない場合、次のようなわかりにくいエラーメッセージがでます。 new Foo; # Can't locate object method "new" via package "Foo" (perhaps you forgot to load "Foo"?) 間接オブジェクト記法は関数の括弧を省略し呼び出した場合の文法と似ていて、関数がインポートされているかどうかで間接オブジェクト記法呼び出しとして解釈される関数呼び出しとして解釈されるかが変わり、わかりにくいエラーメッセージがでる場合があります。 これがよく開発者を混乱させていたので無効にできるようにしたようです。 Perl7の発表 Perl5.32 のリリースの少し後、Conference in the Cloud にて当時のPerl5のリリースマネージャー(Pumpking)であるSawyer X氏によってPerl7が発表されました。 *2 Perl7の内容は、以下のようなものでした。 機能的には5.32と同じ デフォルトで strict, warnings などのよく使われるプラグマや最近追加された新機能をONにし、Perl5.0以前からあるPerlの文法を複雑にしている構文を無効にする 目的 初心者がいろんなプラグマを覚えなくてもすぐにPerlコードを書き始められるようにすること 20世紀に書かれたコードの互換性を残すことを大事にしすぎない Perl6がRakuに改名され名前的にも完全に別の言語となったことで、ちゃんとPerl5の後継バージョンをだしたいというモチベーションもあった 最初に書かなければならないボイラープレートがなくなるとコードが書きやすくなりますし、初心者もとっつきやすくなるのでかなり良さそうに見えますね。 少なくとも発表後、日本のPerl界隈ではかなり好反応だった印象があります。 しかし、Perl7やPerl7の発表には様々な問題がありました。 過去のPerl5で書かれたコードを書き換えないと動かなくなる やはり一番目立つ問題として過去のPerl5で書かれたコードを書き換えないと動かなくなることがあげられます。 Perlには後方互換性を重視する文化があり、これはPerlコアにまつわるさまざまな方針を文書化した perlpolicy にも記述されています。 発表された内容のとおりにPerl7が実装されてしまうと perlpolicy に反するわけです。 他にも後方互換性がなくなることで、次のような問題が発生することが考えられます。 /usr/bin/perl は常にPerl5互換だとおもっていたら、OSディストリビュータの都合で任意のタイミングでPerl7に切り替わり過去の資産が壊れてしまう #!/usr/bin/perl でどのバージョンのPerlで実行しているのかが明確でなくなって、7で書いていたつもりのコードを5で実行してしまいONになっていたつもりの機能が実はOFFになってしまう Linuxディストリビューションのパッケージ管理が大変になってしまう コミュニティ内で十分に議論せずに突然発表された コミュニティ内で十分に議論せずに Pumpking が突然発表したことも問題になりました。 従来ならこのようなPerlコアの方針の重要な変更を行う場合は Perl5 Porters(Perl5開発者向けのメーリングリストに参加しているPerlの開発に興味のある人たち)に発表してから公にするものですが、これによってコミュニティの一部からはPerl7が受け入れられにくい雰囲気になってしまいました。 より正確に言うと、コミュニティ内で十分に議論せずに突然発表されたというよりは、Pumpking は perlpolicy によって定義される Perl Porter の一人に過ぎず perlpolicy を超越した立場ではないはずなのに perlpolicy に明確に反する方針を打ち出したことが問題視されました。 Pumpking = 行政機関、Perl Porters = 立法機関 と考えてもらうとわかりやすいかと思います。 要は行政を担当する人が法律そのものを変えるような行動をしてしまったというわけですが、これによってコアチームやコミュニティ内で混乱が発生してしまい、Perl5の正式なガバナンス制定が必要だという流れになりました。 Perlコミュニティのガバナンス制定 そういった流れでガバナンス制定についていろいろ話し合いが行われ、2020年12月頃ガバナンスが制定されました。 この内容は perlgov に文書化されています。 ざっくり内容を紹介すると、コアチームとPSC(Perl Steering Council)と呼ばれる運営評議会について明確に定義されるようになりました。 コアチームはPerlの継続的な開発に携わるグループです。 基本的にはコアのコミッターで、運営評議会のメンバーを選出し、コアチームのメンバーを管理する権限を持ちます。 PSCはPerl5の新しいバージョンのリリーススケジュール、プロセスの管理など最終的な決定をする人たちのことで、コアチームから選ばれた3人のメンバーから構成されます。 従来の pumpking を置き換えるものです。 すでにPSCのメンバーはいろいろ入れ替わっていますが、現在のメンバーは Paul Evans 氏, Ricardo Signes 氏, Neil Bowers 氏の3人のようです。 Perl5と7の今後 今後の方針について混乱していたPerl5と7ですが、2021/4/11 にPSCのミーティング *3 が行われ、そこで以下のような方針が決まりました。 以前発表された Perl7 の内容は取り下げて、従来のバージョンによる機能ガードを続けていく( use feature version の形で新機能を有効に) Perl7はそのうちリリースするが、少なくとも来年リリースされることはなくて、当分はこれまでと同じように毎年安定版のリリースを続けていく 十分な機能が揃ったら Perl7 と呼ぶことになるかもしれない Perl5.34 の変更点 コミュニティのゴタゴタがあったのであまり大きな変更点はないですが、大きな変更点としては次のようなものがあげられます。 try-catch 構文 次のように try-catch で例外処理を記述できるようになりました。 use feature qw( try ) ; try { die 'hogehoge' ; } catch ( $e ) { print $e ; } これにより今まで eval-if で書いていた例外処理をわかりやすくかけるようになります。 また、Try::Tiny などと違って ; 忘れがなくなったり、例外処理のモジュールも乱立しがちだったのでどれを使えばいいのかあまり悩まなくて良くなります。 更に自分でコードを動かしてみた限りだと Try::Tiny などの外部モジュールとコンフリクトしないので、既存のプロジェクトにも取り入れやすそうです。 8進数数値リテラルの新構文 2進数が 0b1111 、 16進数が 0xFF といったように記述できていた感じで、 8進数が 0o777 というふうに記述できるようになりました。 今までは8進数を記述する場合先頭に0をつけて 0777 というふうに書いていたので、以前より8進数の記述が見やすくなったかなと思います。 barewordファイルハンドルを無効化できるように barewordファイルハンドルとは型グロブに格納されたファイルハンドルのことです。 昔はファイルハンドルは型グロブに格納するのが一般的でしたが、今は変数に格納するのが一般的なのでそれを強制するような機能ですね。 次のようなコードを動かしてみると無効にできるのが確認できます。 no feature 'bareword_filehandles' ; open FH , '<' , 'tmp.txt' ; my @lines = <FH> ; close FH ; 擬似的な多次元配列(multidimensional)を無効化できるように 擬似的な多次元配列というのは、ハッシュの要素を $foo{$a , $b , $c} というふうにアクセスすると $foo{ join ( $; , $a , $b , $c ) } というふうに動作することで擬似的に多次元配列をエミュレートする機能のことです。 昔のPerlでは多次元配列を使えなかったために実装された機能で、今は使うことも目にすることもないと思いますが、ハッシュスライスの構文 @foo{$a, $b, $c} と似ていて混乱を招くので無効化できるようにしたのだと思います。 次のようなコードを動かしてみると無効にできるのが確認できます。 no feature 'multidimensional' ; my %foo ; $foo{ qw( a b ) } ; #Multidimensional hash lookup is disabled Perl5.36以降はどうなるか Perl5.35 での変更 開発版の Perl5.35 ですでに新機能の追加や言語機能の改善が行われています。 これらはこのまま特に問題がなければ Perl5.36 にも取り込まれると思います。 defer構文の追加 Goなど他の言語などでも見られる構文と同じような感じで、スコープを抜けたときに実行される処理を defer { ... } 内に記述することができるようになります。 この構文は perl5.35.4 で追加されました。 *4 真偽値の扱いの改善 条件式などから返される値や、 !!0 や !!1 などの真偽値を変数に代入しても真偽値としての性質を保つようになりました。 これによって他の言語との相互運用やJSONのエンコードなどデータのシリアライズなどが楽になります。 この改善は perl5.35.4 で行われました。 *5 RFCとして提案されているもの Perlの変更の提案は こちらのリポジトリ を見ればわかるのですが、これらのうちからいくつか取り上げます。 forループの繰り返しごとに複数の要素を参照する構文の追加 現在のfor文だと繰り返しごとにリストの先頭から1要素ずつしか参照できませんが、 for my ($foo, $bar, $baz) (@array) { ... というように複数の要素を順番に参照できるようになります。 この構文が実装されるとハッシュ内の全key, valueのペアを for my ($key, $value) (%hash) { ... } で参照できるようになります。 *6 Everything Slices ハッシュや配列に含まれる値やキー/値の完全なセットを含むスライスを取得する構文を追加しようという提案です。 具体例をあげると %hash{ * } ; # %hash{ keys %hash } と同じ %array[ * ] ; # %array[ 0 .. $#array ] と同じ @hash{ * } ; # @hash{ keys %hash } と同じ @array[ * ] ; # @array[ 0 .. $#array ] と同じ といった感じになります。 {} , [] の中はコードも書けるようで、例えば %array[ grep {; $_ > 5 } * ] といったコードも書けます。 forループの繰り返しごとに複数の要素を参照する構文の追加と一緒に追加されると便利になりそうで、組み合わせて使うと for my ($i, $v) (%array[ *]) { ... } と書くと配列のindexと要素をイテレーションすることができるようになります。 その他 既にカンファレンスでPSCのメンバーが5.36以降どうしたいかなどといったことを話しているので、いくつか取り上げます。 FOSDOM2021 での Paul Evans氏 のトーク 2021/02/06~07 の FOSDOM2021 でPSCのメンバーである Paul Evans 氏が「Perl's Amazing Time Machine」 *7 というタイトルでトークしており、その中で2025年には次のような機能を実装したいという話をしていました。 match-case構文 の追加、現状の壊れた given-when を置き換える 厳密比較演算子の追加、暗黙的な型変換をせずに値を比較する演算子 静的/動的に型チェックする構文の追加 サブルーチンのマルチディスパッチを可能に、同名でシグネチャが違うサブルーチンを書けるようにする Paul Evans 氏はXS(perl処理系の記述に使われているCのマクロ言語)をバリバリ書ける開発者です。 5.34 の try-catch 構文や 5.32 のisa演算子もこの方が実装しています。 Perl開発の意思決定者にバリバリコード書ける人が入るのは久しぶりのことで、これによりPerlコアの開発が活発化しそうだという期待があります。 Conference in the Cloud! A Perl and Raku Conf での Ricardo Signes 氏 のトーク 2021/06/10 の Conference in the Cloud! A Perl and Raku Conf で PSCのメンバーである Ricardo Signes 氏が「What's new in Perl? 」 *8 というタイトルでトークしており、その中で5.36では次のようなことをしたいと話していました。 use version でベストなコードが書けるようにしたい warnings, utf8 の有効化 barewordファイルハンドル, 間接オブジェクト記法などの混乱を招きやすい機能の無効化 現在20近くある実験的機能の整理、experimental を外せるものは外し、Perlに追加すべきでない機能は削除する Perlコア以外の動向 Perlコア以外にも面白い動きがあるのでいくつかとりあげます。 Corinna Ovid氏 によるオブジェクト指向構文をPerlコアに入れようという提案です。 github に提案や実験的実装がまとめられています。 Perlコアに現状あるオブジェクト指向プログラミング関連の機能は貧弱だったり、CPANにはそれを補うようなモジュールが乱立しているので、このような動きは学習コストが下がったりコードが書きやすくなって嬉しいです。 Perlコアにオブジェクト指向構文を入れようとする挑戦は過去にも何度かありました。 有名なものでは Stevan Little 氏が Moose をPerlコアに入れようと頑張ってたりしていましたが、Moose が大きすぎるなどでうまく行きませんでした。 仕様的には Moose や Raku の影響を大きく受けています。 Moose、Moo などのOOPフレームワークモジュールとは違って keyword plugin の仕組みを使い新しい構文を追加します。 なので ; 忘れがなくなる、記述量が減る、 $self を介さず変数から直接インスタンス変数にアクセスできてインスタンス変数の宣言がなければコンパイル時エラーになるのでtypoに気づきやすい、などといったメリットがあります。 Paul evans 氏が Object::Pad という実験的実装モジュールを作っており、既にCPANにあがっています。 彼の会社では使い始めたりもしているようです。 UV2.0 のリリース libuv のPerlインターフェースを提供する UV が Perl foundation の助成を受けて開発が進み、バージョン2.0がリリースされました。 まとめ ここ最近はPerlコミュニティでいろいろゴタゴタがありましたが、それもようやく落ち着いてきていて今後開発が活発になっていきそうだという感じです。 これからのPerlに期待ですね! *1 : 「実用 Perl プログラミング」にはPerlで詩を書く話がでてきますが、そのことを考えると当時の雰囲気が少しわかると思います *2 : https://www.perl.com/article/announcing-perl-7/ *3 : https://www.nntp.perl.org/group/perl.perl5.porters/2021/04/msg259789.html *4 : https://metacpan.org/release/WOLFSAGE/perl-5.35.4/view/pod/perldelta.pod#defer-blocks *5 : https://metacpan.org/release/WOLFSAGE/perl-5.35.4/view/pod/perldelta.pod#Stable-boolean-tracking *6 : 現状でも while (my ($key, $value) = each %hash) { ... } でできますが、途中でループが止まった場合などでハマりどころがあり for my $key (keys %hash) { my $value = $hash{$key}; ... というふうに書かれることが多いです *7 : https://www.youtube.com/watch?v=Kc_bP73xNyM *8 : https://www.youtube.com/watch?v=FlGpiS39NMY
アバター
モバイルファクトリーのエンジニアは、NFTマーケットプレイスの「ユニマ」や位置ゲームの「駅メモ!」など様々な事業の開発をしています。そんなエンジニアたちが、普段、どのようなことを考えているのか。3人のエンジニアに聞いてみました。 この記事に出てくる人たち チームによって性質は異なるのだから、課題に応じて、開発の進め方を変える ワークログを書くのは楽しい 可視化するのは、組織開発においても、当然サービス開発、エンジニアリングでも大事 可視化は目的ではない 楽しく仕事をする。そのために斧を研ぐ 理想を知らないと妥協してることに気づかない 自分が望んでいる環境があるなら、そう変えればいい 三者三様のキャリア @kfly8 : 今日は、モバファクのエンジニアが普段どんなことをしているのか、考えているのか、大切にしているのか、みたいなことを聞いていきたいです。よろしくお願いします! まずは、みんなが普段どんなことをしているのか、自己紹介兼ねて教えてください。 @tsukumaru : エンジニアとして入社し、いまはマネージャーをしています。チームビルディングやピープルマネジメントに興味があるので、今回はその辺りを中心に話せればと。 @odanado : ブロックチェーンチームでソフトウェアエンジニアをしているodanです。チームではテックリードのポジションでブロックチェーン技術のキャッチアップや機能開発の設計などしています。ブロックチェーンの他にWeb 技術やプログラミングが好きです。 @maeken : 開発とスクラムマスターをしていて、普段はビールを飲んでいます。 @kfly8 : マネージャー、テックリード、スクラムマスターだね。 遅れたけれど、自分も簡単に自己紹介を。エンジニア組織開発責任者をしていて、堅く言えば、人材開発、組織開発、採用、技術広報など開発パフォーマンスにかかること何でもできればと思って動いています。 今日は3人のイイ話を楽しみにしています! @maeken : まかせてください。 この記事に出てくる人たち @tsukumaru 2016年新卒でモバファクに入社。駅奪取、ブロックチェーンチームのエンジニアを経て、現在はマネージャーとして、チームビルディングやピープルマネジメントなどに注力している。 去年生まれたこどもと過ごす時間が一番の癒し。 @odanado 2018年新卒でモバファクに入社。入社当初に立ち上がったブロックチェーンチームにソフトウェアエンジニアとして在籍。現在はブロックチェーンチームのテックリード。 @maeken 沖縄県出身。琉球大学大学院理工学研究科情報工学専攻修了後、モバイルファクトリーにエンジニアとして2019年に入社。アワメモ!の新規開発・運用を経験し、現在はステーションNFTチームの開発・スクラムマスターを行う。ビールを飲むのが得意。 @kfly8 ゲーム開発やプロダクトマネージャーを経て、現在はエンジニア組織開発責任者。エンジニアの育成、採用、組織課題などに取り組む。また、技術コミュティ好きをこじらせて、Japan Perl Associationの理事もやらせてもらっている。 チームによって性質は異なるのだから、課題に応じて、開発の進め方を変える @kfly8 : まずは、普段の働き方、開発環境だったりを教えてほしい。 @tsukumaru : フルリモートで、コアタイムありのフレックスで働いていますね。東京だけじゃなくいろんな所で働いている。 @kfly8 : 支給されているPCはどうだろう? @tsukumaru : 会社支給のMac Book Pro。スペックは不足している印象はない。M1 Macなども使い始めているので、新しいものも反映されていて良いなと。 @kfly8 : odanはどう? @odanado : あー、それで言うと足りていなくて(笑) tsukumaruさんのチームだと、AWS上に開発環境があって、大丈夫ですけど、自分のチームはローカルでバックエンドも、フロントエンドも開発している。 メモリ16GのMacBookProを使っているんですが、メモリが足りない。 これは会社にどうこうでなく、Appleシリコンで32GB積んだやつをAppleに出してもらえればと(笑) @kfly8 : 2台PCがあればいい? @odanado : あー、開発用とSlack用?(笑) 一同 : (笑) @odanado : リモートワーク前提だと、デスクトップマシンを支給するのはもしかしたらあるかもしれない? @kfly8 : 1周回っている感じだね。以前はデスクトップPCを使っていて、会議室へ持ち込みしやすくするためだったり諸々の理由で、ノートになった。フルリモート環境だと、デスクトップに回帰する選択肢もあるのかもね。 各々の在宅環境もそれぞれだよね。 tech.mobilefactory.jp @kfly8 : 開発の進め方についても聞かせてほしい。 これもチームごとに個性があるけれど、スクラムマスターのmaekenから聞こうかな。 @maeken : 前提として、開発の進め方は、チームに任されている。「会社として開発の進め方は決まっているんですか?」と聞かれることがあるけれど、そうではなくて、自分のチームと他のチームでも異なる。 @maeken : それで、うちのチームはイテレーションが1週間のスクラム。週に各種セレモニーがあって、それを繰り返している。作ったものをレビューしてもらって、開発の方向性を決めていくのを大切にしている。スプリントレビューといった定例もあれば、もう少しまとまった単位でレビューを行う場も用意していたりする。ただ、ミーティングの量はスクラムのセレモニー以外は減らすようにしている。これには失敗談があって、なるべく全員で、共有する場を設けていたけれど、それだと、ベロシティが伸び悩んでいた。振り返りをして、まとまった作業時間がエンジニアは必要だと思った。細切れの時間だと集中ができない。ミーティングをガッとまとめて、作業時間もまとめてとるようにしてみて、スゴイ改善した。やるならミーティングを固めるようにする。 普段の開発の進め方も随時、ふりかえり、改善していっています。 @odanado : 会議を1日に集中させる、というのはうちのチームも取り組んでいるプラクティスですね。実際、メンバーからも好評の声が。 @maeken : odanさんのチームとスプリントの長さも違いますよね。 @odanado : あー、ですね。1週間だったスプリントを、2週間のスプリントに変更した。それはミーティングを減らしたり、まとめたりする意図もあった。スプリントのセレモニーを、毎週にすると実質開発する日が4日となるので。 @kfly8 : odanのチームは、1週間だと収まり切らない大きいチケットがありがちだったよね。小分けにするプランもあったけれど、価値の切り口が難しかった。結局、2週間で計画を立てる形にして、うまくいっていそう。 @maeken : チームによって性質が異なるから、それによって開発の進め方が変わる良い例ですね。 @tsukumaru : 開発チームとしてミーティングをまとめるのは良さそうではありつつ、違う切り口で、ひとつ。今の自分のマネジメントの役割だと、1on1をすることがあるけれど、それを1日にまとめると、喋りつかれちゃう(笑) 自分の場合は、意識的にずらしたりしている。 @kfly8 : マネジメントあるあるだ(笑) ワークログを書くのは楽しい @kfly8 : フルリモートになって工夫していることはある?コミュニケーションなど、新たに出てきた課題などあれば。例えば、新しい人が入ってきた時に工夫していることとか、コミュニケーションするために何かしていることとか。 @maeken : コミュニケーションは、意外とそこまで変わってないですね。 ミーティングは先程の通り時間をまとめたり、合間で雑談をしたり、あとは、Slackでのチャットコミュニケーションと、オフラインの時とあまり変わっていない。 @maeken : チケットの整理など、物理ホワイトボードが使えなかったのが一番大きい変化。みんなで図を書いて議論したり、付箋ぺたぺたしてふりかえりするのとかしづらい。最近はホワイトボードのWebサービスを使っていて、すごい良かった。ふりかえりや、チケット同士の依存関係、あとは朝会の連絡事項なども掲示板的な感じで使っている。逆にオフラインになったとしても使うと思う。どこでも見れるし、無限にでかいホワイトボードで記録が残る。 @kfly8 : コミュニケーションに変わりないのは興味深いね。意外。 @odanado : maekenさんのチームは、リモート前とメンバーが変わっていない、というのは大きいかも?リモート前の関係値がある状態で、リモートをやっているので、そんなに困らなかったのかなぁと思った。チームとして成熟している印象。 @maeken : たしかに。2年間くらい同じメンバーで、チームや関係値ができている状態なので。以前、気軽に雑談できる場所を用意してみようと、コミュニケーションツールを導入してみたが、結局だれも利用しなかった。様子を見ていると、コミュニケーションも悪い感じじゃなさそうだから、それはそれでいいのかなぁと。 @kfly8 : チームメンバーが変わって、課題やトライしたことがあれば。だれか教えて。 @maeken : tsukumaruさんのチームだと、毎日定期的に雑談する予定を設定していますよね?どんな感じ? @tsukumaru : それは、2,3名で話すくらい。これがもっと機能していればリモートワークの工夫として良いかもしれないけど、まだ工夫が必要そう。 自分のチームでやっていたのは、オンボーディング用のドキュメント整備を行った。長年のドキュメントが溜まってきて、どこから読んだら良いのか、どこまで読んだら良いのか困っていた。それを、新卒は"まずここだけ読めば良い"という案内をわかりやすくした。これは好評ですね。 @kfly8 : イイネ。ドキュメントが多いというけれど、ドキュメントを書く人が多い? @tsukumaru : みんな書いてますねー。 仕様だけでなく、ワークログとか。 @kfly8 : ポジショントークになってしまうけれど、ドキュメントをよく書くのは、モバファクの特徴的な良い文化と思っている。 @maeken : たしかに。入社してからドキュメントがあるなと思った、先輩も書いているし、自分でも書こうと思った記憶ある。 @odanado : ワークログという概念は、入社して初めて知ったが、良いなと思った。個人開発でも書くようになったし、Zennがスクラップ機能を出して、そこにワークログ書いている。どうして書くか言語化が難しいけど、メモを残すのは楽しい。 可視化するのは、組織開発においても、当然サービス開発、エンジニアリングでも大事 @kfly8 : 最近、直面した課題があれば、教えてほしい。 @maeken : ステーションNFTに関連するサービスを最近作っていて、そこのスクラムマスターとして開発している。最初の数週間が思うように開発が伸びなかった。そこで、数週間分スプリントを回した内容踏まえ、大きなまとまりのふりかえりを行った。何か開発のボトルネックはないか?って見直した。その時に、さっき話した、ミーティングが多くて、開発に集中しずらい課題が出てきた。 @maeken : もう一つが、要件決めを何からやれば良いかわからない課題が出てきた。スプリントでやることを決めるためには、何をやるのか決まっていなくちゃいけなくて、さらにその前に、要件を決めていく必要がある。で、当時、開発途中で別の要件が発覚したり、変わったり、逆に削除になったりで、そういったことが原因で開発のスピードが伸びないことがわかった。  それをどうにかするために、いくつか先のスプリントまで、いつ何をしないといけないのか、依存関係はどうなっているのか可視化した。スクラムでいうとバックログの透明性をあげよう、とした。可視化したことで、自ずと「このタイミングでこれをするから、その前にこれの要件を決めておこう」といった会話がしやすくなった。それはすごい効果があった。  問題をチームで見えるようにすることで、動きやすくなる。それは、開発のパフォーマンスであったり、開発の着地の見込みであったり、同じかなって思ってる。 @kfly8 : 普通にいい話。今度、詳しくブログに書いてほしい。 @tsukumaru : 可視化、に関連して、最近やっている、DX Criteriaの話がある。 自分のチームは、かなり大所帯。エンジニアはたくさんおり、それ相応にやることも多いし、要件を考えるディレクターの余裕もない状況。 それを改善するために何かできるかと、がっとふりかえってみて、チームの課題点を洗い出し、改善できるところを可視化しようとして、DX Criteriaを使ってみた。やってみた感じ、直感的に「ここまずいだろうなぁ」と感じていたところが、ちゃんと実際に赤く問題があると可視化された。これから改善の行動を取る必要があるけれど、一旦可視化って所はできたなと。 @kfly8 : イイネ。 こんなことを言うと、良くないかもしれないけれど、すでに直感的にまずい所はあって、可視化せずとも出来るところから進めたいと感じる人もいると思うんだ。なぜわざわざ遠回りしたのか? @tsukumaru : 直感的に感じている課題だけが、課題なのかわからなかった。全体像を把握したかった。全体像を把握した上で「いくつか課題があるけれど、今はこの課題に取り組んでいる」と、全体像を共有しながら、改善していきたいと思った。局所解にならないようにしたいと思った。 @kfly8 : イイネ。もう少し聞かせてもらいたいのだけれど、大所帯のチームだし、チームの人を動かすのに苦労はしなかった? @tsukumaru : 計測をするのは、チームに相談して、反発はなかった。むしろ「そういうの良いと思うんでやってみましょう!」といった反応。 多分、問題になるのはこの先。こういう課題があるのでやっていきましょう、と言ったときにみんなが取り組んでくれるか。そこをどう進めていくか、は問題。 @kfly8 : 今後に期待だね。応援したい。 テックリードとして別の切り口で、odanから最近課題になっていることあれば。 @odanado : 最近チーム内でWeb APIのパフォーマンス分析で、どうやってボトルネックを探すか、が課題になった。こういうパフォーマンスの文脈だと場合は「推測するな、計測せよ」と良く言われるけれど、これもさっきの可視化の話に繋がりますね。可視化するのは、組織開発においても大事だと思うし、当然サービス開発、エンジニアリングでも大事。 @odanado : 元々ある程度計測しやすい状態にはあった。というのも、ISUCONのおかげ。ISUCONでの学びを活かして、プロダクトに事前にAPI呼び出しをトレースできるようなものをバックエンドやフロントエンドに仕込んでいた。そのおかげで、ボトルネックの可視化という面で役に立って、何時何分にAPIのリクエストが始まり、何時何分に終わったか、というのが全部データで取れていて、さらにその中で何時何分にクエリが始まっているかだとか、何時何分から外部APIを呼び出しているかなどある程度計測できる状態にあって、すごい助かったというのはあった。 けれど、全てを計測できるわけではなくて、足りないオブザーバビリティの改善をした。オブザーバビリティの向上はこれからもやっていこうと感じています。 @kfly8 : odanもイイネ。 可視化は目的ではない @kfly8 : 3人とも可視化をして、チームを動かす、システムをよくしていく話だったね。 敢えて聞くけれど、可視化に落とし穴もあると思うんだ。例えば、人によって解釈が違う、数字が1人歩きしてしまったり、数値の低さに注目が集まり本質的な話しから逸れたり。 可視化するときに気をつけていることはある? @maeken : 落とし穴は、計りすぎること。計ることや数値自体が目的になってしまうのは良くないなと。例えば、ベロシティの話であれば、1人1人のベロシティは計っていない、チームのベロシティだけ計っている。個人個人のベロシティを計ることで、その人を評価しているようにみえるし、比較になってしまう、それによって、低い人のショックに繋がる。 ベロシティを計っているのは予測をするため。今これくらいのペースでアウトプットを出しているから、未来のここではこれくらいのことが出来ていそうと予想が立てられる。手段と目的が逆転しないように、計測する際は、目的を添えて伝えるようにしている。 一同 : 大事大事。 @odanado : うーん、なんだろう。 自分は対象がシステムだから、そういう懸念はあまり起きないなーと。 @maeken : APIのパフォーマンスで、数値のやばさの基準はどうやって作った? @odanado : 相対的に見るなら、同じアプリケーションのAPIの平均、分散や呼び出される回数などをみて、API間のグラデーションを見る。 そもそも、パフォーマンスが絶対的に悪いかどうかは、フィーリングかなぁと。実際にユーザーになって、マウスを動かしたり、スマホでボタンタップをしてみたりして、まぁ雑ですけど、もっさり感を感じるか、とか、フィーリングに今は頼っていますね。 @maeken : 実際にさわってみて、そのフィーリング。 @odanado : ですねー。ここは今後、プロダクトマネージャーと話さないといけないところで、例えば、このAPIは何ms未満にすることが、このプロダクトの価値提供なんだと、決めていきたい。プロダクトによって、例えば「100ms」を早いと取るか遅いと取るかは異なる。50ms or dieとか、場面場面というか、どんなサービスやっているかに寄ってきますね。 @maeken : 変わってくるからこそ、例えば「100ms」を遅いと決めつけたり、それも落とし穴なのかなと思った。 @odanado : ですね。maekenさんが言っていた、目的の話に繋がる思っていて、プロダクトのゴールや体験してもらいたいことがあって、そのためにどこまでが許容ラインなのか決める話なので。 @kfly8 : 可視化するだけでなく、目的志向でありたいと。 楽しく仕事をする。そのために斧を研ぐ @kfly8 : 可視化にフォーカスされているけれど、他に開発で大切にしている価値観があれば聞きたい。 @maeken : 全てひっくるめて、開発、というか、仕事は楽しくできた方が良いと思っていて、命大事に、楽しく開発するために、頑張る。仕組みづくりやチーム作りを頑張る。余計なことをやらない。結局、楽をしたいじゃないですか。ラクするというのは、サボるという意味じゃなく。大切なことに集中したいので、そのために頑張る。 @tsukumaru : 自分も楽しく、というのはある。そのために、斧を研ぐ時間は大事にしたいと思っている。木こりのジレンマの話。第一領域に集中するだけでなく、もちろん大事なんですけど、それだけでなく、第二領域、チームの改善や振り返りなど、そこもやっていかないとチームが良くなっていかない。チームがよくなっていかないと、個人も楽しくなっていかないと思うので、その時間は大事にしたい。第二領域をやれるようチームを動かすのも自分のマネジメントの仕事の一部かなと思っている。 理想を知らないと妥協してることに気づかない @kfly8 : odanは全然違う視点で話してくれるはず。 @odanado : そう思っていました(笑) ソフトウェアエンジニアのマインドとして、理想を追い求める。これは個人的に大事にしている思想で。理想は誰にも見つかっていないとしても、絶対に理想はあると思っていて。設計や実装をするのは、そこに向かうための道だと思っている。業務というのは、それを見つける作業だと信じているところがある。 @odanado : 基本的に100%妥協しないように仕事をしていることが多い。例えば、外部ライブラリを使う場面で、とりあえず目的は達成できているから、ライブラリの挙動がわからないけどokとするのではなく、ドキュメントを読み理解をした上で、ライブラリがどうやって動作するのか、それを解釈した上でコードを書いたりとか、そういう部分は強く持っている。 @odanado : ただ、もちろん、ビジネス的な要求だったりで、システム的な妥協をすることもある。けれど、理想を知らないと妥協してることに気づかない。それは成長とか、プロダクトの品質向上には繋がらない。ので、常に理想を持ち、それを追求できれば追求できるくらいの気持ちでいる。 @kfly8 : ふわっとした質問になるけれど、システムの理想ってなんだろうね?もちろんこれも目的次第だけど。システムはどんな特徴を満たしてると良いのか、特に気にしているところがあれば聞きたい。さっき出てきた話なら、オブザーバビリティとか。 @odanado : あー難しいですねー。 理想を、フレームワークに閉じ込める、というのは1つあるかも。仕組み化することによって、常に万全の状態にする、というか。チームのフレームワーク、規約に沿って開発すれば、ベストプラクティスに乗った状態で開発できるようになっていると良いなと思う。例えば、この関数で、Web APIを呼び出せば、バックエンドにログが残り観測性が担保できるとか。 @kfly8 : フレームワークに乗れば、ベストプラクティスに乗れる、というのは発想としては、Easy?中身がわかった上でライブラリを使えた方が良いという話と、若干相反する? @odanado : Easyというより、開発者体験。1回目は中身を見て理解して、2回目以降はEasyの恩恵を預かれて、開発者体験が良くなるくらいのイメージでしたね。 自分が望んでいる環境があるなら、そう変えればいい @kfly8 : 話が変わるけれど、どうしてモバファクに入社したのか教えてくれるかな? @maeken : 就活をしている時、自分がやりたいことがある程度できるところが良いなと。そのために雰囲気を見ていた。会社の人数で100名を超えてくると分からない人が増えてきたり、風通しが…といった話を聞いたことがある。ので、100人くらいの規模で探していた。そんな中、話を聞いて、面白そうだと思ったのがモバファク。 @odanado : もともと駅メモ!のライトユーザーで、もっさり感を感じていた。競技プログラミングもやっていたので、パフォーマンス改善、最適化に興味があった。自分が触ったことがあるゲームで、なおかつ、パフォーマンス改善でおもしろそうなことができそうだなと思った。 ただ入社直後にブロックチェーンチームが立ち上がり、手を挙げ現状こうなっている。入社してから全く駅メモ!に関わっていないですね(笑) @tsukumaru : 社長のメッセージと位置ゲームに惹かれましたね。 社長のメッセージに「家族を守る力がほしい」と書いてあった。そんなこと言っている会社って他にいなかった。他だと「会社を成長させる」「社会に還元」っていうのが多かったけれど、家族に注目している社長はいなかった。珍しいと思った。自分自身も家族を大事にしつつ働きたいと思っていたので良さそうだなぁと。 あと位置ゲームに関しては、もともと自分がゲームを作りたいわけではなかった、サービスが作りたかった。位置ゲームは、人のおでかけするっていう実際の行動に影響を与えられるゲームだと思った。これも珍しいなぁ、良いなぁ、と思った。 @kfly8 : 入社してみて、思っていたことと違ったことはあった?良い意味でも悪い意味でも。 悪いギャップがないのも不自然だし、それは改善すれば良いこと。 @tsukumaru : 悪い方のギャップはなかった。いい意味で予想通り。 面接で、みんな強そう、いい人そうという印象。実際入ってみるとみんな強くて、いい人たち。予想通りだし、予想以上にそういう会社なんだなとおもった。 唯一驚いたのは新卒同期が3人しかいなかったこと(笑) @kfly8 : 例年はもう少しいるからね。 @maeken : 良かったギャップは、思ったよりやらせてもらえること。入社して、新卒で配属されるのが、いきなり新規開発になると思ってなかった。配属言われた日に驚いたことを覚えています。 悪いギャップは、現場と経営層で思ったより距離があった。100人規模の会社なので、社長であったり直接話す機会は多いと思っていた。 @odanado : まず良いギャップについて。 就職活動の時、モバファクか従業員10名くらいのベンチャーで悩んでいた。研究室の先生や周りの友人に相談していて、「同期は一生の繋がりだからいいよ」と言われていて、「ホンマか?」と思っていたが、今でもプライベートでゲームするくらいの仲。そういう所が良いギャップ。 悪いギャップは、シニアエンジニアがもっと欲しい。当初、シニアエンジニアがチームにいたが、半年で退職してしまった。チームメンバーの歳が割と近くて、びっくりした。 @kfly8 : 確かに。若い人が多いね。 2つの悪いギャップに関して動いていることはある? 現場と経営層のギャップに関してはどうだろう? @maeken : 月次の全社締め会で、社長に質問をして回答してもらうコーナーが始まったけれど、社員の経営と距離が遠い、という社員の声を拾って始まった。きっかけとしては良い取り組み。直接話せる場があるかないかで全然違う。結果踏まえ変えていくところはあると思うけれど、どんどん取り組んでみるの良いと思う。 新型コロナのこの状況的に直接会って飲むのは難しいけれど、逆にリモートだから、オンラインでパッと集まってパッと聞ける、なども良いなと思った。 @kfly8 : シニアエンジニアが、もっと欲しい件に関しては、どう? @odanado : とりあえず自分がシニアエンジニアになった(笑) @kfly8 : かっこいい @odanado : 去年からフロントエンドランチ会を定期的に開催している。フロントエンド技術のキャッチアップしながら、雑談する場。教育というか、会社全体のレベル感を引き上げるための取り組みを行なっていますね。 きっかけは、他社でもフロントエンドランチ会をやっているのを聞いていた。そういう会があることや、そこに参加する社員がいる会社がとても魅力的に感じた。まずは自分がいる会社にそういう文化を作るのが良いかなと思った。他社の真似をしながら、モバファクでもやってみようと思った。 自分が望んでいる環境があるなら、そう変えればいい。 一同 : エライ! @odanado : 今年の新卒もフロントエンドランチ会に参加してもらえているので、脈々と続けられるかなと。 三者三様のキャリア @kfly8 : 最後に、今後どうしたいのか、今後のキャリアについて聞かせてもらいたい。モバファクにどんなエンジニアがいるのか伝えるためにぜひ。 @maeken : 今のスクラムマスターはエンジニアリングマネージャーの一つの要素だと思っていて、そこに興味がある。今、イメージしやすくて、成功体験や楽しさを感じられているから。 自分だけじゃなく、チーム全員で楽しくできるようにしたい。 @tsukumaru : エンジニアリングマネージャーの話が出たけれど、元々自分もEMに憧れてはいて、最近管理職になったことでそういった動きがしやすくなった。ただそこで終わりではなく、経験、知識が足りないので、まずはEMとして成果を出していきたいし、自分が成果を出すことで、maekenとか他のメンバーにEMっていいね!と思ってもらいたいし、その結果会社に良い影響を与えられると良いなと思う。 EMを広めつつ、最終的には、会社の事業の変化を楽しめるような、変化に強い会社をつくることに貢献できたら嬉しいですね。 @odanado : キャリアプランは、最強になることで。 @kfly8 : それどこかで聞いたな(笑) @odanado : (笑) 理由はより良い妥協をするには、理想を知っている必要があって、仕事の上でより良い妥協を繰り返して、選択していくことで、任意のテクノロジートピックについての理想を知っている状態。つまり最強になりたい。 @maeken : 全知全能に。 @odanado : 全知全能に。 @kfly8 : はい!それでは今回はこの辺で。ありがとうございました! モバイルファクトリーでは、エンジニア組織をより強くするために、採用を行っています。 興味を持っていただいた方は、カジュアル面談も実施していますのでぜひお気軽にご連絡ください。 カジュアル面談のお申し込み あわせて、こちらの採用サイトもご覧ください。 recruit.mobilefactory.jp
アバター
はじめに こんにちは。ブロックチェーンチームのエンジニア、 @nanamachi です。 tech.mobilefactory.jp 前回の記事ではたくさんの方に閲覧&コメントいただきありがとうございました。この記事から1年。モバイルファクトリーは日本のどこからでも働けるようになり、書籍購入、資格取得、セミナー参加、懇親会の支援制度などフルリモートに適応できるよう多くの変化をしてきました ( https://recruit.mobilefactory.jp/work-style/ )。その中で社員の環境もさまざま変わったことでしょう。 この変化を記事にすれば、 閲覧数を稼げる 弊社の魅力を発信できるに違いない!という目論見で、初めてバズった記事にすがるエンジニア組織開発責任者の @kfly8 から次のようなチャットが送られてきました。 kfly8: インターネット識者 *1 の @nanamachi さん、よかったら在宅開発環境の記事の2回目書きませんか? nanamachi: インターネット有識者……🤔🤔🤔 ということで今回はリモートワーク開始から1年半、さらなる進化を遂げた在宅環境をごらんください! 前回の記事も合わせて読むとさらに楽しめます♪ 01. デザイナー @momoyagi さん 在宅環境の様子を教えてください 社用のディスプレイ2枚+Mac Book Pro 電動昇降デスク ハーマンミラーのチェア 無線のヘッドセットマイク Alexa echo dot バランスボード 推したいポイント、改善したいポイント オフィスと同じものではないですが、電動昇降デスクを導入して立ち仕事を可能にしました。バランスボードでユラユラしながら仕事してます。 どんな変化があったか教えてください 仕事環境の改善のために書斎を設けられる広さの家に引っ越しました。電動昇降デスクを導入、モニターやチェアも一新して開発環境をグレードアップしています。MTGなども毎日あるので、音周り環境もう少しよく出来るんじゃないかなーと思ってます。 フリーコメント BGMはDJAlexaです。 02. エンジニア @nanamachi 在宅開発環境の様子を教えてください すでに在宅で仕事ができる環境に満足してしまったので、次はモバイルモニターとコンパクトなキーボードでノマド環境を整備しました。 推したいポイント、改善したいポイント モニター: EVICIV 13.3インチ モバイルモニター 持ち運び可能なモニターです。サイズとしてはA4用紙とほぼ同じくらい。ベゼルが薄いためMacとシームレスに作業できGOODです。 また、USB Type-Cケーブル1本で給電と映像出力の両方ができるのでケーブルがごちゃつかない利点もあります。 キーボード: VORTEX Core 47キー メカニカルキーボード テンキーレスのキーボードから更にFunctionキーと数字キーなどを除いた、40%キーボードと呼ばれるジャンルのミニサイズキーボードです。 カスタマイズ性が非常に高く、設定次第ではフルサイズキーボード以上の使い勝手にすることが可能です。 自分はなるべくフルサイズキーボードに近い操作感を保つため、下記の配置にしています。 青字で書かれたキーは左親指に割り当てられたFnキーと同時に押すと入力されるキーです。 標準では右Spaceの一つ右のみなのですが、Fnキーを多用する配列にしたため押しやすい位置に移動しました。 その他こだわりの点はこんな感じ。 フルサイズキーボードの最上段をそのまま一つ下に下げた位置に配置 コーディングでも日本語入力でもよく使うハイフンを同時押し無しで押せる配置 ホームポジションで矢印キー *2 にアクセスできる 特に数字・記号・矢印などのキーを短いストロークで打てる点が便利で、慣れたら戻れないという意見も納得です。 また、設定を変えたことによってキーキャップに印字されているキーと異なる配置になったためキーキャップも改造しました。 無地のキーキャップにプラモデルなどに用いる装飾シール *3 をちまちま貼っていけば、オリジナルキーキャップの完成です。 アナログなモノづくりが久しぶりだったことありなかなか楽しかったです。 整備してみて 外に出る機会は少ないのですが自宅の中で場所を変えて仕事するだけでも気分転換になるのは良い発見でした。展開に時間もかからないのでおすすめです。 03. 事業部長 Sさん 在宅開発環境の様子を教えてください ディスプレイ: EIZOの4Kディスプレイ キーボード: HHKB HYBRID TYPE-S 無刻印 机: FlexiSpotの電動式昇降デスクの脚にイケアの150x75の天板 椅子: アーロンチェア オーディオインターフェイス: UNIVERSAL AUDIOのAPOLLO TWIN X QUAD スピーカー: GENELECの8320 GLM Studio ウェブカメラ: ロジクールのC922n マイク: オーディオテクニカのAT4040 照明: Profoto C1 Plusを複数台 推したいポイント、改善したいポイント 前回記事からの進化としては、スタンディングデスク導入と、ビデオ会議時の音声品質の向上が大きなところです。 リモートワークが始まった当初は、映像の方に力を入れていて、ミラーレスカメラを使用したり、Blackmagic Design ATEM Miniを導入したりしましたが、結局、大事なのは映像より音声だという結論に至り、マイクやオーディオインターフェースなど試行錯誤を重ねました。 去年の夏頃にオーディオテクニカのAT4040とUNIVERSAL AUDIOのAPOLLO TWIN X QUADの組み合わせにしてからは非常に満足しています。 APOLLO TWIN Xでは、Neve 1084、Teletronix LA-2A、PULTEC EQP-1Aといったプラグインをリアルタイムにかけていて、ビデオ会議の相手に聞きやすい音声となるよう調整を行っています *4 。 自分で壁に吸音材も取り付けました。 映像については、ロジクールのC922nでビデオ会議には充分なクオリティと判断して、机の上をなるべく広くするために映像関連の機材は撤去しましたが、Elgato MULTI MOUNTというスタンドを使って、カメラや照明などは必要に応じていつでも取り付けができるようにしています。 スタンディングデスクは気分転換で時々利用します。また、会社からハーマンミラーのセイルチェアと32インチモニターを送ってもらったので、リビングの隅にも作業スペースを作りました。 MacBook ProとHHKBだけ持っていけば自室とほぼ同じ環境で作業できるように、マウスやウェブカメラの機種、机の天板の色などは同じもので揃えています。 04. エンジニア @the96 さん 在宅環境の様子を教えてください モバワークを活用して実家からの参戦です *5 。 そこそこ田舎なので、休みの日には5kmくらい散歩して神社に行ったりしています。 左ディスプレイ: LG ULTRAGEAR 27GN800-B (27インチWQHD) 右ディスプレイ: IO-DATA LCD-MQ321XD (32インチWQHD) ディスプレイアーム: エルゴトロン LXデュアル デスク マウント アーム スタッキング MBPスタンド: Twelve South BookArc for MacBook キーボード: Mistel Barocco MD770 キーボードの間にあるもの: Anker PowerWave 10 Stand (ワイヤレス充電器), タニタ TT-580 WH (質温度計) 机: 幅145cm 奥行き60cmのL字デスク 椅子: 中古のバロンチェア オーディオインターフェース: UR12 + MG10XU スピーカー: JBL 104-BT-Y3 マイク: sE Electronics X1 推したいポイント、改善したいポイント おすすめ: 分割キーボード キーボードが左右に分離しているので、肩を開いて自然な姿勢でタイピングすることが可能です。 普段は背もたれに身を預けて楽な姿勢で仕事しています。 あと、体の正面が空くので、資料を置いたり時計を置いたりワイヤレス充電器を置いたりできて便利です! お気に入り: スピーカーとイヤホンの同時接続 スピーカーとイヤホンから同じ音を流していて、スピーカーの音量つまみを操作するだけでMTGに対応できるのがお気に入りです! MG10XUはWin機とMBPでマイクやスピーカーを共有するために使っています。 フリーコメント 推しに囲まれて仕事するのたのしいです 05. エンジニア うっひょいさん 在宅環境の様子を教えてください 開発者自身の筋肉のスケールアップ、体の軽量化を行った *6 。 Before After 逆立ちしながら考えている様子 Before 驚異の-8.2kg! 推したいポイント、改善したいポイント 在宅開発環境で推したいポイントとしては僧帽筋と三角筋です。日々の自重トレーニングによって、僧帽筋や三角筋を鍛え上げ、長時間デスクワークをしても肩こり知らずです。 また、三角筋や体幹を鍛えればコーディングのときの実装で悩んだときは逆立ちしながら考えることが可能です *7 。 在宅開発環境で改善したいポイントとしては腸腰筋です。デスクワークのときの眠気覚ましにVシットや逆立ちをやってるのですが、腸腰筋が弱いので安定しなくて困っています。 06. エンジニア組織開発 @kfly8 さん 在宅環境の様子を教えてください 200cmの机をDIYして使っています。 夫婦で並んで作業するだけでなく、子供もお絵描きをしたりして気に入っています。 子供は横並びになったり、向かいに座ったりと好きなところに座ります。 1年が経ち、次の点を変えました。 腰が痛かったので、セイルチェアに変えた。デザインも生活空間にあっても違和感なし。最高。一脚は会社から譲渡してもらった。 机の角が痛かったので、削った。娘が楽しそうにヤスリがけをしていた。 ディスプレイ背面、モニターアーム、ケーブル類を、スプレーで黒塗りをした。 最近は、モバイルプロジェクターのAnkerのNebula Capsule IIを購入して、子供が動画を観るのにはかどっています。左右の台形補正が入ったら、嬉しいなーと思う人生を過ごしています。 みかんは「愛媛まどんな」という品種で、美味しい 元々は白いディスプレイ背面、アルミ色のモニターアームでした。塗りました。 モバイルプロジェクターで動画を選んでいる様子 推したいポイント、改善したいポイント 正直、塗装は酔狂な人がすると思っていました。ですが、スプレー缶数本で生活環境に統一感を出せるので、非常にコスパが高く合理的。 買い換えるコストと比較したら、「いい仕事をした」と妻に褒めてもらえました。 ※塗装は自己責任でお願いします。 フリーコメント 塗装風景はコチラです。趣味が活かせて良かったです。 https://twitter.com/kfly8/status/1343786958313594880 07. 採用担当 @overallfactory さん 在宅環境の様子を教えてください ◆ディスプレイ ディスプレイは、もともと持っていたLGのモノと会社から譲り受けたDELLのモノを使用しています。採用の仕事柄Slackやメールをすることが多いため、ディスプレイの一つは縦型にしています。 ◆机 机はリモートワークスタート共にFlexiSpotのスタンディングデスクに変更しました。眠くなったら立って仕事をしています。 ◆キーボード 机にモノを増やしたくない+浅いストロークのキーボードが好みということで、Magic Keyboardを利用しています。ただ、キーボードに傾斜が欲しかったので、裏にキーボードスタンドを取り付けています。 ◆配線 一日中家にいると、机や床が汚れるスピードがとても早いです。 毎日掃除をしたい人なので、コードやティッシュ、文房具などは全て浮かせるようにしています。浮かすは正義です。 ◆ライト 会議や面談でテレビ電話をすることが多いので、デスクライトを買いました。 机の上のモノを増やしたくなかったので、USB給電でディスプレイ上につけられるライトを選択。 また、家でON/OFFを切り替えるのが得意ではないため、仕事の時と休憩の時で色を変えられるものを選びました。 推したいポイント、改善したいポイント 二点あります。 一つは照明へのこだわりです。 ライトの色で自分はON/OFFを切り分けています。 ONの時は昼白色、OFF時は電球色にして過ごしています。 もう一つは部屋から色を無くしたことです。 集中力が増すように、デスク周りはもちろん、家にある家具(カーテン、絨毯、小物)を全て茶色か白黒に変えました。 08. エンジニア @odan3240 さん 在宅環境の様子を教えてください 開発の作業はMacBook Pro と31インチのディスプレイがメインです。この31インチのディスプレイは会社の移転に伴い、昔会社で使っていたものを抽選で譲ってもらいました。基本的には MacBook Pro でコードを書いて、ディスプレイは Slack の周回、ブラウジング、コードリーディングに使用しています。 机は Amazon で横幅120cmのものをポチって使っています。FlexiSpot も検討したのですが、昇降機能は MUST ではないと考えてこちらにしました。 椅子は最近セイルチェアを購入しました。これは椅子について詳しくないため、会社で使用していて座り心地が良かったので同じセイルチェアに決めました。 Before After 推したいポイント、改善したいポイント 推したいポイントは31インチのでディスプレイです。サイズが大きいため左右に分割してブラウザと Slack を開いていることが多いです。1枚のディスプレイで実質2枚分の働きをしてくれるのが良いところです。 改善したいポイントは机と配線です。 机を購入した当時は立って作業することはなかったので、昇降機能は MUST ではないと判断しました。しかし実際に机を使用しているうちに高さではキーボードのポジションに違和感を覚えるようになりました。なので次机を購入するときは FlexiSpot などの昇降機能が付いているものを検討したいです。 また配線はちょっとぐちゃぐちゃしているのをなんとかしたいです。HDMI の分配器など机の下に隠せるものはいくつかあるんですが、ゲームのイヤホンのコードなど、どうしても有線で繋ぐ必要があり机の上に配線を置く必要があるものを、いい感じに整理したいです。 フリーコメント 左の一回り小さいディスプレイはゲーム用です。お昼休みや退勤後即ゲームができる環境が整っています。息抜きも大事です。 Ex. @NortonSam1 さん 前回の記事で異色の存在感を放っていた @NortonSam1 さんは惜しくも先日退職されましたが、特別に回答していただきました! 今回はどのようになっているのでしょうか……? 在宅環境の様子を教えてください 職が変わり自宅での作業が皆無となった現在、転居先の住居は食事睡眠入浴以外の用途を果たさなくなりました。その結果荷ほどきもろくにせず、段ボールはそのまま簡易テーブルに早変わり。されど前回デスクの役割を果たしていたアイロン台は依然として健在です。 Before After 推したいポイント、改善したいポイント 気に入っているポイントは特にありません。エアコンをつけるとくしゃみが止まらなくなるので転居先を探しています。強いてあげるならば山が近いことでしょうか。最近は仲良くなった地域住民に空き家を譲ってもらおうと頑張っています。 どんな変化があったか教えてください 職が変わりました。現在は木こり見習いとして活動しています。椎茸栽培にも手を出しています。夏にはわな猟の免許を取得します。昨日は乗っていた軽トラックの後輪が走行中に外れて田んぼにすっとんでいきました。 おわりに ここまで読んでいただきありがとうございました! 「仕事しやすい」から一段先のこだわりが見えてきて、1年前よりも更に個性が出てきたように思います。それぞれ違う最適な環境を作り上げていけるのはリモートワークならではの醍醐味ですね。 モバイルファクトリーに働き方についてさらに詳しく知りたい方は下記のサイトからどうぞ! 在宅環境こだわる方もこだわらない方も募集中です♪ まずは気軽にお問い合わせください。 recruit.mobilefactory.jp *1 : 本当にこう書いてありました。違うのに! *2 : 当然HJKLです *3 : デカールシールというそうです *4 : 実際に聞いたところ非常に音質がクリアで良かったです *5 : ここ2年の新卒の方は特に実家からリモートワークしている方が多いようです *6 : これも広い意味では在宅環境の様子ですね!!!!! *7 : タイピングも逆立ちしながらするのでしょうか
アバター
こんにちは。ブロックチェーンチームのソフトウェアエンジニアの id:odan3240 です。 この記事では昨年の9月から社内で取り組みを続けているフロントエンドランチ会について紹介します。 フロントエンドランチ会とは 第2木曜と第4木曜のランチの時間 (13:00-14:00) にフロントエンド技術に興味がある有志のメンバーがオンラインで集まってランチする会です。 この会の目的は以下の通りです。 フロントエンドに関する技術の情報交換 フロントエンドに興味がある人の親睦を深める この会で実際に行っていることは以下の通りです。 13時頃にみんなぼちぼち集まってくる その日のファシリテーターを2人決める ファシリテーターは約30分で交代する ファシリテーターの画面を共有しながら JSer.info の最近の記事を開く ファシリテーターが記事の内容を読み上げつつみんなで深堀りしていく わからない単語/概念はないか? 実際に仕事に活かせそうか? 企画しようと考えたきっかけ 物理出社時代のランチでの雑談の再現 東五反田オフィスには「リフレッシュルーム」と呼ばれるランチを食べるための部屋がありました。そこで相席になったエンジニア同期とゆるいテックトークをするのが当時の楽しみでした。しかし昨年の2月からのリモートワーク化に伴いこの時間が失われてしまいました。 なんとかゆるいテックトークを会社の人とオンラインでやりたいという気持ちを抱えて半年が過ぎていました。 他社のフロントエンドランチにあこがれて ゆるいテックトークで思い出したのはサイボウズさんのフロントエンドランチ 1 でした。自分がこの催し物を知ったのはコロナ禍以前で、参加者の方が Twitter でその様子を実況しているのを見て、「羨ましい」「いつか参加してみたい」などと考えていました。この理由としてはお互いの知見を広げられることや単に自分がテックトークをするのが好きだからです。 このときはもし転職するならこういう文化がある会社が良いなぐらいの気持ちでした。しかし上記のランチでのテックトークをオンラインで復活させたいというモチベーションと掛け合わせたときに、文化がある会社に行くより今いる会社で文化を作るほうが面白いという考えに至りました。 この流れがあり、去年の9月にフロントエンドランチ会を企画することにしました。 実際の様子 2021-07-08 に開催されたフロントエンドランチ会の様子を紹介します。 この日は 2021-07-06のJS: TypeScript 4.4 Beta、immutable-js 4.0.0-rc.13、petite-vue - JSer.info をみんなで読みました。このときのサマリーを紹介します。 TypeScript 4.4 beta の useUnknownInCatchVariables は顧客が求めていたものと言う話になりました imutable-js は OSS の運用周りでなにかあったらしい 大変そう 最近だと immer をよく聞くよね 社内で Vue.js を採用しているプロダクトが多いため petite-vue に関心が集まった HTML の構文に違反しないように v-effect が新しく導入されたのでは コミットログを見返すと Evan You が約1週間で petite-vue をフルスクラッチから実装していてやばい actions/setup-node にキャッシュ機能が入ったのは便利そう ただ actions/setup-node の存在と UNIX 哲学ことを考えると諸説あるのでは The State of WebAssembly 2021 について The State of Hoge を見ると年末感出るけどまだ6月w やっぱ Rust が多いよね polyfill とは別に ponyfill という概念があるんだ 実際にフロントエンドランチ会を開催してみた感想 第一に企画当初の狙い通り、定期的にフロントエンド技術についてキャッチアップすると技術的な雑談をする時間を確保できたのが嬉しい点でした。 企画当初には考えてなかった嬉しい点もありました。それは会社に入ってきた新しいメンバーとの交流の場にもなった点です。会社のフルリモートワーク化に伴い別のチームのメンバーとの交流の機会が減る中で、チームを超えた交流が促進されました。 運営で心がけていること ゆるく継続して開催することと、参加者の意見を取り入れて内容をブラッシュアップすることを心がけています。 ゆるく継続して開催するために、ファシリテーターを交代制にすることと、開催頻度を隔週にしました。ファシリテーターを交代制にすることで主催者である自分自身の負荷を軽減しています。開催頻度については、毎週開催にすると「木曜日はフロントエンドランチ会」という固定概念とともに参加に対する義務感が生まれてしまうのではという懸念から隔週にしています。 また参加者の意見を取り入れるために、開催初期は 表明じゃんけん を使って最後に参加者に良かった点と改善点の声を募るようにしていました。この結果記事に対する深堀りの方針として「実際に仕事に活かせそうか?」という視点が追加されました。 終わりに フロントエンドランチ会の概要、企画のきっかけ、実際の様子を紹介しました。フロントエンド技術のキャッチアップだけでなくチームを超えた人との交流も促進されるため個人的な満足度は高くおすすめの取り組みです。 サイボウズのフロントエンドエキスパートチームの紹介 - Cybozu Inside Out | サイボウズエンジニアのブログ ↩
アバター
こんにちは、ブロックチェーンチームでソフトウェアエンジニアをしている id:odan3240 です。 来月に予定されている Ethereum Berlin Upgrade の調査を行う中で発見した EIP-2718: Typed Transaction Envelope が、Ethereum の未来を感じさせる提案だったので紹介します。 EIP-2718 の提案内容 EIP-2718 は新しいトランザクションタイプを定義する提案(以下「この提案」と呼びます)です。 この提案での有効なトランザクション ( Transaction ) とトランザクションのレシート ( Receipt ) の定義は次の通りです。 || はバイト列の結合演算子です。 Transaction : TransactionType || TransactionPayload と LegacyTransaction のどちらか Receipt : TransactionType || ReceiptPayload と LegacyReceipt のどちらか LegacyTransaction / LegacyReceipt はどちらも従来の Ethereum のトランザクションとトランザクションのレシートの定義です。 TransactionType はトランザクションのタイプを識別するための 0 から 127 までの128通りの数字です。 TransactionPayload / ReceiptPayload はそのトランザクションタイプの payload です。 EIP-2718 のモチベーション この提案以前ではトランザクションを拡張する場合は後方互換性を保つ必要がありました。例えば EIP-155: Simple replay attack protection では secp256k1 の署名の v の値に chainId を考慮した値を加算することで、要件を満たしつつ後方互換性を実現しました。この提案では TransactionType の値でトランザクションを判別するため、後方互換性を気にする必要がなくなります。 そのため以前では、トランザクションの data の構造を自由に設定できることを活かして EIP-712: Ethereum typed structured data hashing and signing や EIP-1613: Gas stations network などが提案されてきました。今回の提案によりトランザクションの構造についても自由に設定することができるようになり、トランザクションレベルでのマルチシグトランザクションの実装などが可能になります。 EIP-2718 を応用する提案 この提案を使って新しいトランザクションタイプを定義している提案を紹介します。 EIP-1559: Fee market change for ETH 1.0 chain ガスの支払いを変更する提案です。 maxInclusionFeePerGas や maxFeePerGas などのパラメータがトランザクションに追加されています。 EIP-2711: Sponsored, expiring and batch transactions. 以下の3つのトランザクションタイプを提案しています。 Sponsored Transactions: トランザクションの送信者とガスを支払う人を分離可能 Batch Transactions: 複数のトランザクションをまとめて実行可能 Expiring Transactions: トランザクションに有効期限を設定可能 EIP-2733: Transaction Package トランザクションの success や gas_used などの実行情報を次のトランザクションに渡せるようにする提案です。 EIP-2930: Optional access lists トランザクションがアクセスする予定のあるアドレスとストレージを事前に渡せるようにする提案です。事前申告されたアドレス以外にアクセスするとコストが増加します。 EIP-2938: Account Abstraction コントラクトが EOA と同様に料金の支払いやトランザクションの実行ができるようにする提案です。 EIP-2972: Wrapped Legacy Transactions 従来のトランザクションタイプをこの提案の形式で定義し直す提案です。chainId が明示的にトランザクションのパラメータに含まれています。 EIP-2976: Typed Transactions over Gossip devp2p が新しいトランザクションタイプを受け入れるようにする提案です。 終わりに EIP-2718 はこれまで固定だったトランザクションの構造を可変にする提案で、その紹介をしました。トランザクションの構造が柔軟になることによってこれまでなかった提案がされ Ethereum の自由度は更に高まっています。特に EIP-1559 からは目が離せないですね。
アバター
あけましておめでとうございます。ブロックチェーンチームのソフトウェアエンジニアの id:odan3240 です。 この記事では Google Docs を用いたエクストリームリーディング形式の社内勉強会を1年間継続できた記念に、その形式を紹介をします。 エクストリームリーディング とは エクストリームリーディングとは黙読フェーズと議論フェーズの2つのフェーズを繰り返す読書会の形式の1つです。 黙読フェーズでは次のことを行います。 1節、1章、数ページなどのある程度のまとまった文章を読む範囲として決定 この範囲を参加者で並行して黙読 その次の議論フェーズでは黙読した結果について次のことを行います。 文章の解釈を合わせる 文章中で分からないところを教え合う エクストリームリーディングの利点は次の通りです。 読書会への参加に必要な事前準備が必要ないため参加のハードルが低い お互いの知識を補え合える 1つ目の利点に関しては、輪講形式の読書会の準備に苦労したことがある方にとっては理解しやすいと思います。 Google Docs を用いたエクストリームリーディング モバファクでは Ethereum Layer2 勉強会という社内勉強会を開催しており、この勉強会では Google Docs を用いたエクストリームリーディング形式の読書会を採用しています。この勉強会では Ethereum の Layer2 技術に関する Web 上のサイトやドキュメントを Google Docs に転記してみんなで読み進めています。 この Google Docs を用いたエクストリームリーディングでは、上で紹介した進め方の黙読フェーズ中に Google Docs のコメント機能で文中の感想、解釈、分からないところをコメントします。これは従来のエクストリームリーディングと比べて次の利点があると考えています。 気軽にコメントができる Google Docs を使う前は対象の文を引用してコメントを書いていた 議論箇所の目安になり議論フェーズが活発になる 文章の流れを妨げない 実際に Layer 2 勉強会では画像のような形で進行しています。 Making Sense of Ethereum’s Layer 2 Scaling Solutions: State Channels, Plasma, and Truebit | by Josh Stark | L4 blog | Medium を読んでいる様子 Optimistic vs. ZK Rollup: Deep Dive | by Alex Gluchowski | Matter Labs | Medium を読んでいる様子 参加者の声 Layer 2 勉強会の参加者に Google Docs を用いたエクストリームリーディングについて感想を質問してみました。 リモート環境だと読書会をしても他のメンバーがどこを見ているのか分かりにくく議論に支障が出ていたのですが、Google Docsを使うと誰がどこに言及しているのかはっきりして捗りました 集中して読む時間を確保しつつも、その場で生じた疑問を忘れないうちにすぐ議論できるのがよかったです。 文の意味が理解しづらかったときや技術特有の文脈などで不明なところがあったときにすぐ議論できてよかった 読みながら疑問点や感想を書くので、書き忘れたり読んでいた箇所を見失ったりせずスムーズに進められました 終わりに Google Docs を用いたエクストリームリーディングを紹介しました。 社内では Layer2 勉強会以外にも Google Docs を用いたエクストリームリーディングを使った社内勉強会をやっていこうという話になり、 web.dev の記事を読む社内勉強会が立ち上がり始めています。 この記事を読んでいる方も Web 上の文章の読書会に Google Docs を用いたエクストリームリーディングはいかがでしょうか?
アバター
この記事は モバイルファクトリー Advent Calendar 2020 25日目の記事です。長かったアドベントカレンダーもこれがラストです。今年も25日まで毎日技術記事を楽しみに過ごせました。 こんにちは、ブロックチェーンチームのソフトウェアエンジニア id:odan3240 です。 ERC721 の extension ERC721 は Ethereum における Non-Fungible Token (以下 NFT) の規格です。ERC721 には様々な extension が存在しており、 OpenZeppelin では次の種類の extension が実装されています。 Mintable NFT を mint できる Burnable NFT を burn できる Enumerable NFT を数えられる Metadata NFT とオフチェーンのメタデータを繋げられる Pausable NFT の転送を停止できる この中でも Enumerable は NFT を数えられるようになる一方で、gas used (以下 コスト) 増加することが知られています。 speakerdeck.com 今回 Enumerable 以外の extension に対してコストの増加を調べたので、これを共有します。 実験の設定 実験の各パターンは次の通りです。Mintable をベースに他の extension を追加しています。 Basic (Mintable) CaseBurnable (Mintable + Burnable) CaseEnumerable (Mintable + Enumerable) CaseMetadata (Mintable + Metadata) CasePausable (Mintable + Pausable) バージョン solidity: 0.5.17 Ethereum の hardfork: Muir Glacier @openzeppelin/contracts: 2.5.1 1 ソースコード github.com 実験結果 deploy/mint/transferFrom の各コストは次の通りです。 deploy mint transferFrom Basic 2,113,681 67,978 61,602 CaseBurnable 2,259,122 67,978 61,602 CaseEnumerable 2,455,003 153,561 92,696 CaseMetadata 2,538,433 67,978 61,624 CasePausable 2,541,757 67,978 62,497 わかりやすく gas price が 70Gwei、円と ETH のレートが 62608円/ETH としてコストを日本円に換算すると次のようになります。 deploy mint transferFrom Basic ¥9,263 ¥298 ¥270 CaseBurnable ¥9,901 ¥298 ¥270 CaseEnumerable ¥10,759 ¥673 ¥406 CaseMetadata ¥11,125 ¥298 ¥270 CasePausable ¥11,139 ¥298 ¥274 すでに知られているように Enumerable を実装するとトークンの mint/transferFrom のコストが約2倍に増加することがわかりました。 また deploy のコストについては、各 extension を実装すると増加し、Pausable を実装するのが約1.2倍と一番大きな増加率になることがわかりました。 まとめ ERC721 の extension の違いによるコストの増加について調べました。 deploy はどの extension でもコストが増加しました。トークンの mint/transferFrom については Enumerable を実装するとコストが増加しました。 どの extension を ERC721 に実装するかは作りたいトークンの要件にもよりますが、実装すると何かしらのコストが増加する可能性を考慮しておくと良さそうです。 無事にモバイルファクトリー Advent Calendar 2020は25日完走できました。それでは皆さん良いお年を! @openzeppelin/contracts の最新版は v3 系ですが、v3 系は Enumerable がデフォルトで組み込まれていて実験に適さないので v2 系 ↩
アバター
この記事は CTOA Advent Calendar 2020 と モバイルファクトリー Advent Calendar 2020 の24日目の記事です。また先日の Gaiax Technical Meetups の登壇内容を元にした内容になります。 こんにちは。エンジニア組織開発責任者のkobaken( @kfly8 )です。 明日はクリスマスですね。娘4歳はサンタさんにレゴをリクエストしていました。届くといいですね😊 今年の2月から、モバファクは新型コロナの影響でフルリモートの働き方に変わりました。 その影響もあり、組織開発観点では社内のコミュニケーションに課題を感じた1年となりました。 エンジニア組織に限らず、組織全体において、 「他のチームがどんなことをしているのかわからない」 「どんな人かわからなくて、話かけづらい」 「さみしい..!」 なんて声を聞きました。特に、今年入ったメンバーからはよく聞きました。 新人の1人に聞くと、定期的に雑談する工夫をしたそうです。 tech.mobilefactory.jp そんな話を聞くと、個人やチームの工夫があって組織はうまくいっていることを改めて感じます。一方、組織開発の担い手として、前提が変わってしまったことによる組織課題を丁寧に解決することが求められました。例えば、オンボーディング、リモートワークでのコミュニケーション、1on1、エンゲージメント改善のためのガイドや新人研修といった研修プログラムのオンライン化、新しい働き方に合わせた人事制度・福利厚生の改変などが挙げられます。 前置きが長くなりましたが、こういった組織開発の一環として、社内勉強会の改善事情について書きたいと思います。社内勉強会はスキルアップに注目されがちだと思いますが、コミュニケーションを促進し、シナジーを生み出す実感があり、フルリモートならではの課題をいくらか解決する施策だと思います。シナジー効果により、1人で達成できないことを組織で協力して達成しやすくなっていると嬉しいですよね。 他方で、社内勉強会の運用は良いことだけでなく問題もたくさんありました。運用に悩んでいる方もいらっしゃると思います。どう改善したのか一つの事例として読んでもらえればと思います。 モバファクの社内勉強会の概要 モバファクの社内勉強会は、1日1時間、コアタイム外はいつ誰でも勉強会して良い制度です。名前は「シェアナレ!」です。 最近だとこんな勉強会がありました。 最強の〇〇環境プレゼン大会 エラー設計ワーキンググループ スクラムガイド2020読書会 TCPにダイブ! Certified Jenkins Engineer 2020になるまで UX探検隊 先週金曜日に開催された勉強会 先週金曜日に開催されていた"最強の〇〇環境"と題したLT会では、最強のノマド環境、最強のインターネット環境、最強の育児環境といった話をしていました。最近だと、アドベントカレンダー執筆のためのもくもく会が多く開催されていたりします。おとといは今年1年をふりかえるエンジニアのLT会がありました。 現状のモバファクの社内勉強会の状況を簡単にまとめると、次のような具合で組織にいくらか浸透していると感じます。 盛んに開催されている 10年以上続いている 知識を得るだけでなく、お互いを知る場にもなっている 問題はたくさん 現状、ほぼ毎日開催され、いくらか組織に浸透しているとは思いますが、問題はたくさんありました。中には現在も進行形の問題もあります。勉強会を運営している人にとって、身に覚えのある問題もあると思います。 問題は複雑に絡み合っている こういった組織の問題を分析する時、面白い所が、一つの原因があるわけではなく、互いに繋がっていて原因をたどろうとしてもうまくいかないところです。典型的な悪手は、誰かのせいにすることです。全員にとっての100点はないですが、どうなったら嬉しいか、少しずつ改善していくことが解決の糸口だと思います。 丁寧に解決し続ける 行ったことのポイントは次の4つです 地続きのコミュニケーション 仲間を巻き込む サーベイ。そして対話 草の根活動 1. 地続きのコミュニケーション 勉強会で話を聞いたら「ハイ!おしまい!」でなく、勉強会の始まる前から終わった後まで地続きでコミュニケーションを設計すると良いと思います。 例えば、誰かがいいアウトプットをしたなら、イイネと素直な気持ちを表明する。めんどうなことは続かないですが、イイネといったちょっとしたリアクションが話した人のやる気や、社内勉強会の活性化に貢献すると思います。 社内バズ?みたいなこともある 最近だと「スクラムガイド2020でましたね」と誰かがチャットで話せば「一緒に読みますか」といった話に繋がっていました。 勉強会の場だけでなく、会社の中に会話が溶け込むのが理想だと思います。 2. 仲間を巻き込む あーしようこうしようと一人相撲をしても、当たり前ですが文化はできないです。社内勉強会の文化を改善していくにあたり、公募で運営メンバーを募りました。どの施策よりも効いたと思います。運営メンバーの皆には感謝です。 運営の仲間を巻き込む狙いは2つありました。 1つ目の狙いは、自分ごとにする現場目線での発信です。それまでは管掌部署のヒューマンリレーションズ部が発信していましたが、メンバーの話を聞くと発信を自分ごとにしにくかった面が正直ありました。会社にはいろんな人がいるので、誰が伝えるかで伝わり方も変わると改めて感じました。同じ現場の人が、会社を良くするために前向きに取り組みをしていたら、良い刺激を受けるんじゃないかと思います。 2つ目の狙いは、文化の担い手づくりが狙いです。組織を変えられる実感を持つ人が増えた方が、自分たちの会社をハンドメイドする感覚が持てて楽しいんじゃないかと思います。そんな実感なく、将来、組織を良くしてくださいと言われても、どこからどうすればいいか困ると思います。組織開発をじっくり実践する場になればと思っています。 こうやって、少しずつ仲間を巻き込んでいきたいです。 3. サーベイ。そして対話 これも当たり前ですが、当てずっぽうで施策を打つわけにはいきません。どれだけ社内勉強会を薦めたいかNPSとフリーコメントを集めて、運営チームで”診断型組織開発”、つまりデータを観察し、対話しながら解釈をして、仮説をたて、改善のアクションにに繋げるといったことをしていきました。 徐々に良くなってきている サーベイの結果を見て、杓子定規に受け取るのではなく、どういう意味があるのか対話をしていきます。例えば、5点が多いのはなぜか?8点は多いけれど、9点が少ないのはなぜか?推奨となるとためらう気持ちが生まれやすいのか?解釈を話します。 同時に、どんな勉強会でありたいかといった話も混じえます。何というか眉間にシワを寄せて話し合っても、勉強会の楽しい雰囲気を作れないと思います。何が好きか、やりたいか、こういった価値観の要素は制度・仕組みの設計以上に育てにくいところなので、大切に拾っていきたいと思っています。 4. 草の根活動 ここまでおおよそ制度・仕組みの改善ですが、やはり、草の根活動はあります。例えば、発表できそうな人を探す、定期イベントを開催する、新人研修に組み込むといったことをしています。 例えば、参加しやすく、発表しやすくを狙いに、社内カンファレンスを開催しています。5月に開催された「新人研修では聞けない〇〇な話」では、キャッチーさ・お祭り感を演出しています。 ちょっとしたお祭り感のある勉強会も開催 全部が全部こんな感じで頑張ると大変ですが、フルリモートの世界観になって、お互いの気配を感じにくくなっているので、お祭り感の演出も大切かなと思っています。今は部屋の移動もなくサクッと勉強会に参加できて、それこそ、ながらで参加することもできるので社員の半数が参加することもあります。皆の様子が見えるのは安心感が生まれると思います。 運営として、意識していること 施策の例を挙げてきましたが、当然、状況次第で施策も異なると思います。また、制度・仕組みといったハード面での改善実例を中心に挙げていますが、そこからきちんと運用し続け、文化・価値観といったソフト面で根付くことが本質的に大事だと思います。そのためにも、どういったことを意識しているのかを書いて終わりにしたいと思います。 元々の文化を活かす 余裕を持つ 丁寧に。丁寧に。 1. 元々の文化を活かす モバファクの社内勉強会の場合「話したいから話す」といった自主性に根ざした色があります。そういった色は簡単には出来上がらず、貴重な価値だと思います。運営が良かれと思った改善としても、文化を殺していないか、様子の観察は忘れないようにしたいです。 2. 余裕を持つ 会社でやっていることなので、効果、成果を求めるところはあります。個人的にも事業インパクトを生み出す事例が生まれないかと楽しみではあります。ですが、求めすぎ余裕がなくなると、発表のハードルが上がり、参加へのプレッシャーが大きくなり、制度の存在意義が不明瞭になります。フルリモートの今なら、チームを超えたコミュニケーションを促せるなら良しと捉え、いい意味で無駄を楽しむ方が良いのかなと思っています。 3. 丁寧に。丁寧に。 組織を変化させるには、丁寧さが必要だと思います。丁寧というのは、実態を見て解決の手立てを考えることや、繰り返し伝えていくことです。組織には多くの人がいて感じ方も人それぞれなので、全員が満点になることはないです。例えば、今年フルリモートに舵を切り、覚悟を持って断行した企業もあると思います。会社のために良かれとやっていることだと思います。だとしても、ぞんざいにして良い理由はなく、変化に適応することは負担になるので、丁寧に、丁寧に行うことを意識しています。 まとめ 組織開発の一環として、社内勉強会の改善事例をお伝えしました。社内勉強会は、スキルアップといった側面だけでなく、お互いを知りシナジーを生み出すコミュニケーション効果もあります。また、仕組みがあればうまくいくものではなく、その運用改善のため、丁寧に自分たちの文化となるよう解決を進めています。 関連 一つ一つの具体的な施策を広報がインタビューしてくれています。よければこちらもご笑覧いただければと。 corpcomn.mobilefactory.jp corpcomn.mobilefactory.jp corpcomn.mobilefactory.jp corpcomn.mobilefactory.jp 明日は最終日ですね!明日の記事は、CTOAアドベントカレンダーはCTOA代表理事の松岡剛志さん、 モバファクのアドベントカレンダーは、 id:odan3240 です。お楽しみに!
アバター
この記事は モバイルファクトリー Advent Calendar 2020 23日目の記事です。 こんにちは、 id:nesh です。 はじめに 今回の記事は2年前の記事と関連して、モバイルアプリのテストを自動化する話です。過去の記事 AppiumでAndroidアプリの自動テストをPerlで書いてみた - Mobile Factory Tech Blog では、Perl + Appium を使ったAndroidアプリのテストについて書きました。 今回の記事には、Appium + AWS Device Farm + Jenkins を使い、Androidのモバイルアプリの動作確認を複数端末における自動化について書きます。 背景 運用中サービスのアプリに変更を入れる場合、当サービスがサポートする端末でアプリの動作確認をするのが理想的だと思います。 コロナ禍前は、会社に検証用の端末があるため、サポートする端末や問題がありそうな端末を複数台確保して、動作確認を行いやすかったです。 しかし、2月からフルリモートで働くようになってから、手元に検証用の端末は(複数台)ない状態になっています。 手軽に複数端末で、モバイルアプリのテストをしたいので、今回目をつけたのは AWS Device Farm です。 AWS Device Farm Device Farm は、実際に Amazon Web Services (AWS) によりホストされている電話やタブレットで、Android や iOS、およびウェブアプリを物理的にテストしてやり取りできるアプリテストサービスです。( AWS公式サイト より引用) モバイルアプリの開発における様々な端末での動作確認を楽にしてくれるAWSのサービスです。 このサービスの使い方は2つあります。 自動アプリテスト リモートアクセスの操作 今回は様々の端末での動作確認を自動化したいので、自動アプリテストを使います。 自動アプリテスト 事前準備 Testing mobile apps across hundreds of real devices with Appium, Node.js, and AWS Device Farm | Front-End Web & Mobile まずやっておくことは、AWS Device Farm上の準備ですが、上記のブログ記事を参考に作業します。 テストするアプリを用意 動作確認をAppium (Node.js) のテストで実装 AWS Device Farm上で設定し、自動テストアプリを実行 テストするアプリを用意 テストするアプリはAWS Device FarmやAppiumが用意してくれたサンプルアプリを使うのもできますが、今回は自分で作ったHelloWorldを表示するアプリを使います。 GitHub - fadlil/HelloWorld app/outputs └── app-debug.apk 動作確認をAppium (Node.js) のテストで実装 テストしたいことは、アプリを起動できるかどうかだけにします。 // テストフレームワーク var expect = require( 'chai' ).expect; // node.js でappiumを使う var wd = require( 'wd' ); var driver = wd.promiseChainRemote( { host: 'localhost' , port: 4723 } ); var assert = require( 'assert' ); describe( 'AWSDeviceFarmReferenceAppTest' , function () { before( function () { this .timeout(300 * 1000); return driver.init(); } ); after( function () { console.log( "quitting" ); } ); // アプリが起動できて、'Hello World!!' が表示されるテスト it( 'test_app_is_loaded' , async function () { const element = await driver.elementById( "com.example.nesh.helloworld:id/change" ); expect(element).to.exist; } ); } ); このテストをそのままローカルで実行すると失敗します。 driver.init() に必要なデバイスの情報が足りないからです。 ただ、AWS Device Farm上で実行される時、これらの情報がよしなに補完されます。 このテストファイルをAWS Device Farmで使うために、 npm-bundle と zip化する必要があります。 AWS Device Farm上で設定し、自動テストアプリを実行 新しくプロジェクトを作成 新しいrunを作成して、必要な項目を設定 アプリの *.apk ファイルをアップロード zip化されたテストファイルをアップロード デバイスを選択 必要設定を埋めたら、自動アプリテストを実行 必要な作業は大体上記の通りです。 ここまでの作業で、モバイルアプリを複数端末で手軽に自動テストできるようになりました。 しかし、AWS Device Farm上の操作自体が手間になると思います。 この手間を無くし、継続的にテストを回せたいと思っているので、Jenkins で自動化することにしました。 Jenkinsで自動化 自動化するのは、 AWS Device Farm上で設定し、自動テストアプリを実行 の操作です。 操作自体は単純で手間ではないのですが、自動にできる部分は自動化したい気持ちです。 自動化するといっても、JenkinsにAWS Device Farm用のプラグインが用意されてるので、簡単に自動化できます。 AWS Device Farm の Jenkins CI プラグイン - AWS Device Farm Jenkinsで使うプラグインは aws-device-farm | Jenkins plugin です。 この記事は上記に用意したサンプルリポジトリを使った場合、設定のスクリーンショットをいくつか貼ります。 Jenkinsのプロジェクトの設定1 Project と Device Pool はAWS Device Farm上に設定されてるものを参照します。 Application はサンプルリポジトリ上のアプリファイルのパス Jenkinsのプロジェクトの設定2 今回のテストは Appium (Node.js) を使うので、該当テストファイルのパスを入力します。 Jenkinsのプロジェクトの設定3 テスト環境の設定は、AWS Device Farm上に手動で自動アプリテストを実行した時のものをそのまま使います。 Jenkinsでの自動化が成功 これで、Jenkinsでの自動化のための設定ができて、実行して成功できました。 試してみた所感 AWS Device Farm の自動アプリテストはアプリ開発時の動作確認に便利 最初の設定も簡単で、テストするアプリさえあればすぐにできる テストデバイスの起動時間が合計1,000分まで無料なので、気楽に試せる アプリ開発時のサポート端末での起動確認などで使えそう 自動化に関しては、アプリのビルドやテストファイルの npm-bundle + zip などの作業も自動化すれば理想的 日々の面倒な作業を自動化して、快適な開発ライブを充実しましょう。 明日の記事は id:kfly8 さんです!
アバター
この記事は モバイルファクトリー Advent Calendar 2020 22日目の記事です。 こんにちは。エンジニアの id:Eadaeda です。普段はサーバーサイドの面倒を見ています。 DocBase 弊社ではドキュメント共有ツールとしてDocBaseを利用しています。Markdown形式で書いた文章を投稿することが出来、記事の埋め込みや検索なども便利です。私もチケットのワークログや議事録、シェアナレ!で書いたものなどを投稿しています。 docbase.io ブラウザ内のエディタでDocBaseの記事(DocBaseではメモとよんでいます)を編集する場合は、エディターへ画像や動画などのファイルをドラッグアンドドロップすることでアップロードすることが出来ます。その時、以下のように自動でMarkdownを挿入してくれます。 画像なら ![ ファイル名 ]( URL ) 動画などならリンク [ 動画 ]( URL ) なので、ブラウザ内のエディタを使っている場合、ファイルを挿入したい位置にカーソルをあわせ、ドラッグアンドドロップでアップロードするだけで良いのです。特段珍しい機能とは思いませんが、嬉しい機能ですよね。 ところで、私は何らかの文章を書くとき、EmacsやVim、nanoといったコマンドライン上で動作するテキストエディタを使っていて、この記事もそこで書いています。ちなみにどのエディタを使っているかはナイショです。 こういったエディタを使うのは、出来るだけターミナルから動きたくないからなのですが、そこでメモを書いていると困ってしまうことが1つあります。メモに貼り付けたい画像などのURLが先にわからないことです。 ![ image ]( ここがわからない ) これはなかなか大変なことです。書いているときは、例えば画像を挿入したい位置に __ここに画像__ などのマーカーを置いておき、あとからブラウザ内エディタにコピペ、マーカーを探して順番通りに画像をドラッグアンドドロップしていかなければなりません。挿入したいファイルが現れるたびにアップロードし、URLを取得する方法もありますが、先に述べたように私は出来るだけターミナルから動きたくありませんでした。 これをなんとかしたい…というよりはなんとかしなければならなかったので、なにか使えるものは無いかと探していました。するとDocBaseが公開しているAPIを見つけました。 help.docbase.io このAPIはレスポンスとして以下のようなJSONを返すようです。 [ { " created_at ": " [ここは投稿した時間] ", " id ": " [ここはID] ", " markdown ": " ![example.png]([ここは画像へのURL]) ", " name ": " example.png ", " size ": 285 , " url ": " [ここも画像へのURL] " } ] 嬉しいことにMarkdownへ埋め込みが返されます。これをメモに貼り付ければ良いので、一度もターミナルから離れずにメモを完成させられそうですね。 CLIツールにする さて、毎回 curl コマンドを叩くのも良いのですが、いささか書き換えが面倒です。アップロードしたいファイルへのパスを与えるだけでアップロードまで行ってくれるシェル関数を書くなどをするのも有りでしょう。 でも今回は、Goで専用のCLIツールを作ってみようと思います。ここでようやく本題です。前置きが長くて申し訳ない。ちなみになぜGoでCLIツールを作るのかと言うと、単純にGoでCLIを作るのが好きなのと社内でGoを使ったCLIツールを作る人が増えてほしいからです。 GoにはCLIフレームワークとして公開されているものがたくさん有りますが、今回は cobra を使います。この間公開された GitHubのCLIツール にも使われていますね。私もよく使いますが、後に紹介する viper との連携が強力で好きです。 github.com github.com まずはどんなものを作ったのか、どんなものが作れるのかを理解してもらいたいので、ヘルプの出力と upload サブコマンドで example.png をアップロードしたときの様子を以下のGIFに示します。 このような感じですね。非常にシンプルです。 実装 さて、早速私が実装したものを見てもらいます。 package main import ( "bytes" "context" "encoding/json" "errors" "fmt" "io/ioutil" "net/http" "os" "path/filepath" "github.com/spf13/cobra" "github.com/spf13/viper" ) // docbaseコマンドの本体 var rootCmd = &cobra.Command{ Version: "0.0.1" , PreRunE: func (cmd *cobra.Command, args [] string ) error { // API Tokenとチーム名は必須なので、どちらかが空な場合はアプリを終了する if len (viper.GetString( "token" )) == 0 { return errors.New( "DocBase API Tokenが空です" ) } if len (viper.GetString( "team" )) == 0 { return errors.New( "チーム名が空です" ) } return nil }, Run: func (cmd *cobra.Command, args [] string ) { fmt.Println( "これはサブコマンドを指定しなかったときに実行されるコードだよ" ) }, } // uploadサブコマンド // docbase uplaod という感じに呼び出せる var uplaodCmd = &cobra.Command{ Use: "upload" , Run: func (cmd *cobra.Command, args [] string ) { err := upload(args[ 0 ]) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit( 1 ) } }, } func upload(path string ) error { filename := filepath.Base(path) fp, err := os.Open(path) if err != nil { return err } defer fp.Close() content, err := ioutil.ReadAll(fp) if err != nil { return err } // viperからAPIトークンとチーム名の値をもらう token := viper.GetString( "token" ) team := viper.GetString( "team" ) // リクエストボディを作る // [ // {"name": filename, "content": content} // ] data, err := json.Marshal([] struct { Name string `json:"name"` Content [] byte `json:"content"` }{ {Name: filename, Content: content}, }) if err != nil { return err } body := bytes.NewBuffer(data) // http.Request{}を作る r, err := http.NewRequestWithContext( context.Background(), "POST" , fmt.Sprintf( "https://api.docbase.io/teams/%s/attachments" , team), body) // ヘッダーに環境変数から拾ったアクセストークンをセット。これがないと弾かれる r.Header.Set( "X-DocBaseToken" , token) r.Header.Set( "Content-Type" , "application/json" ) // ここから実際にリクエストを投げる処理 client := &http.Client{} res, err := client.Do(r) if err != nil { return err } defer res.Body.Close() // レスポンスを読んで、Stdoutに出力 resBody, err := ioutil.ReadAll(res.Body) if err != nil { return err } // Jsonの出力 var b [] byte resJson := bytes.NewBuffer(b) json.Indent(resJson, resBody, "" , " " ) fmt.Println(resJson.String()) return nil } func init() { // オプションの追加 // PersistentFlags に追加すると、追加されたcobra.Commandに追加したサブコマンドでも使えるオプションになる // そうしたくない場合は Flags に追加すれば良い // --tokenオプション、文字列を受け付ける。デフォルト値は ""、最後の引数は --helpで出力されるそのオプションの説明 rootCmd.PersistentFlags().String( "token" , "" , "DocBase API Token" ) // こっちは--teamオプション。 rootCmd.PersistentFlags().String( "team" , "" , "Team name" ) // viperで値を管理 // "token" に、`token`という名前のオプションの値をバインドする viper.BindPFlag( "token" , rootCmd.PersistentFlags().Lookup( "token" )) // "token" に、`DOCBASE_TOKEN` という環境変数の値をバインドする viper.BindEnv( "token" , "DOCBASE_TOKEN" ) viper.BindPFlag( "team" , rootCmd.PersistentFlags().Lookup( "team" )) viper.BindEnv( "team" , "DOCBASE_TEAM_NAME" ) // uploadサブコマンドを追加する。 rootCmd.AddCommand(uplaodCmd) } func main() { if err := rootCmd.Execute(); err != nil { os.Exit( 1 ) } } かなり愚直な実装になりましたが、とりあえずできました。ではこれを使って実際にファイルをアップロードしてみましょう $ export DOCBASE_TOKEN= "ここにあなたのアクセストークン" $ export DOCBASE_TEAM_NAME= "ここにあなたのチーム名" $ go run main.go upload ./example.png これで、以下のようなレスポンスが返ってきました。 [ { " id ": " アップロードしたファイルのID ", " name ": " example.png ", " size ": 285 , " url ": " https://image.docbase.io/uploads/アップロードしたファイルのID ", " markdown ": " ![example.png](画像へのURL) ", " created_at ": " 2020-12-08T17:17:09+09:00 " } ] アップロード出来ていそうですね。ここから "markdown" の値を取り出し、メモにを貼り付けるだけです。 あとは、 go build や go install でバイナリを得れば良いですね。 $ go build -o docbase $ mv ./docbase " $PATH の通っているところ" $ go install オプション 今回の実装では、 cobra と viper を組み合わせて、APIトークンとチーム名をオプションで切り替えられるようにしました。ちょうど実装の以下の部分ですね。 func init() { // オプションの追加 // PersistentFlags に追加すると、追加されたcobra.Commandに追加したサブコマンドでも使えるオプションになる // そうしたくない場合は Flags に追加すれば良い // --tokenオプション、文字列を受け付ける。デフォルト値は ""、最後の引数は --helpで出力されるそのオプションの説明 rootCmd.PersistentFlags().String( "token" , "" , "DocBase API Token" ) // こっちは--teamオプション。 rootCmd.PersistentFlags().String( "team" , "" , "Team name" ) // viperで値を管理 // "token" に、`token`という名前のオプションの値をバインドする viper.BindPFlag( "token" , rootCmd.PersistentFlags().Lookup( "token" )) // "token" に、`DOCBASE_TOKEN` という環境変数の値をバインドする viper.BindEnv( "token" , "DOCBASE_TOKEN" ) viper.BindPFlag( "team" , rootCmd.PersistentFlags().Lookup( "team" )) viper.BindEnv( "team" , "DOCBASE_TEAM_NAME" ) // uploadサブコマンドを追加する。 rootCmd.AddCommand(uplaodCmd) } オプションが指定されればその値を、されなければ環境変数を使う。というような感じで viper が値をよしなに決定してくれます。これにより、以下のような使い方で、APIトークンとチーム名を切り替えられるようになりました。 # オプションなし。環境変数を読みに行く $ docbase # --tokenに値を渡す # APIトークンは `hoge` として処理がすすむ。チーム名は環境変数の値のまま $ docbase --token=hoge # --teamも渡してみる # APIトークンは `hoge`、チーム名は `fuga` として処理される $ docbase --token=hoge --team=fuga cobra , viper は非常に強力で他にもまだまだできることがあるので、是非READMEなどを読んでみてほしいです。 まとめ 今回は「ターミナルから動かずにDocBaseにファイルをアップロードしたい!」という願いを叶えるため、Goを使って、DocBaseにファイルをアップロードするCLIツールを作りました。フレームワークとしては cobra と viper を選択し、この後の機能拡張も考えて「アップロード処理を行う upload サブコマンド」として実装しました。これをきっかけに社内でGoが大流行すればいいなあと思っています。 また、本当にターミナルから一歩も離れないことを目指すのであれば、メモの更新・作成を行うサブコマンドを実装する必要がありますが、投稿する前にプレビューなども見たいので、そこは手動にすることとしました。他のAPIは今後ほしいという要望が(私から)出れば実装すると思います。 明日の記事は id:nesh さんです!楽しみですね!
アバター