開発

クロスブラウザに対応したE2Eテスト環境の技術選定

ハコベルの中村です。

みなさんのチームではE2Eテストをやってますか?このブログでも紹介した通り、印刷サービスの開発チームでもE2Eの記事が出たばかりですが、ハコベルコネクトの開発チームでも同時期にE2Eテストを構築しました。(事業部も違えば、プロダクトのフェーズも違うので、力をいれたいポイントが異なっていて、読み比べするのも面白いと思いますので是非両方読んでいただけると!)

ハコベルコネクトの関心としては主にこれらの点でした。

  • 物流業界の幅広いお客様に使って頂くために、IE11などのブラウザでも使えるようにチェックする必要があるが、時間がかかってしまう
  • 機能追加・変更の影響範囲チェック漏れでバグが出にくくしたい
  • フロントエンドのテストとしてブラックボックステストをしたい

この課題を軽減するためにE2Eテスト環境の構築をスタートしました。TestCafe + Browserstack という組み合わせを選択しました。今回の記事ではどのような基準で選定したのか、組み合わせて使用する際に必要な設定を紹介してみます。

 

続きを読む

既存プロダクトに最小構成でTypeScriptを導入する

こんにちは。

印刷のラクスルでフロントエンドを担当している菅野です。

現在、稼動中のとあるプロダクトへのTypeScript導入を進めています。今回は、既存プロダクトへの影響を最小限に留めつつTypeScriptを導入する手順をご紹介します。

 

TypeScriptとは

TypeScript – JavaScript that scales.

TypeScriptは、Microsoftが開発したオープンソースのプログラミング言語です。

詳細な説明は省きますが、以下のような特徴があります。

  • JavaScriptの厳密なスーパーセット(≒上位互換)
  • 省略可能な型システム
  • クラスベースのオブジェクト指向

 

TypeScript導入にあたって

今回TypeScriptを導入することで、以下のようなメリットがあります。

  • 型システムの恩恵が得られる
  • エディタの入力補完を受けられる
  • コード=ドキュメントという状況を作りやすい

 

型システムの恩恵が得られる

TypeScriptを導入する最大の理由です。

APIレスポンスの定義をDBに近づけることで、実行時エラーの危険性を大幅に減らすことが出来ます。また、ReduxやVuexで複雑になりがちな状態管理にも型を導入するメリットは大きいです。

コンパイルエラーを無くす=ランタイムエラーを無くすということを最大の目標としています。

 

エディタの入力補完を受けられる

こちらも大きなメリットとなります。

定義した変数はもちろん、Objectのプロパティ等も型込みでエディタの補完を受けられるようになるため、開発効率がアップします。

 

コード=ドキュメントという状況を作りやすい

コメント含め、コードに関するドキュメントは、書くのもメンテナンスをするのも大変です。

TypeScriptで型をしっかり書いていけば、引数の型や戻り値の型に関するドキュメントは必要なくなります。「コードが仕様を体現する」という状況を作りやすくなるわけです。

 

本記事のゴール

  • 既存プロダクトにおいて、TypeScriptで記述できるようにすること
    • .tsファイル及び.vueファイルでの<script lang="ts">を有効にする
  • 既存ロジックには影響をあたえないこと
  • 型検査は出来るようにすること

 

TypeScript導入の下準備

本手順はmizchiさんのGistを参考にさせていただいています。

導入対象プロダクトのフロントエンド構成について

JavaScript周りは以下の構成となっています。

  • Vue.js 2.5.17
  • npm 6.4.1
  • webpack 4.35.2

 

typescript, ts-loaderのinstall

webpackでバンドルするため、typescript本体に加えてts-loaderをinstallします。

npm install typescript ts-loader

 

導入時点でのバージョンは以下の通りです。

  • typescript 3.5.3
  • ts-loader 6.0.4

※ --save-devではない理由としては、フロントエンドモジュールのインストール及び本番ビルドをデプロイサーバー上で行っているためです。

 

webpack.config.jsにts-loaderの設定を追加

const config = {
  ...,
  module: {
    rules: [
      ...,
      {
        test: /\.tsx?$/,
        exclude: /node_modules/,
        use: [
          {
            loader: 'ts-loader',
            options: {
              transpileOnly: true, /* コンパイルのみで型検査を行わない */
              appendTsSuffixTo: [/\.vue$/] /* .vueファイルをTSとして読み込むようにする */
            }
          }
        ]
      },
    ]
  }
}

 

今回のポイントはtranspileOnly: trueです。このオプションを指定することにより、ts-loaderは型検査を行わなくなります

 

tsconfig.jsonの追加

プロジェクトのrootにtsconfig.jsonを追加します。

このファイルはtypescript compiler(tsc)に関する設定を記述するファイルです。

tsconfig.json · TypeScript

Compiler Options · TypeScript

基本構成はVue.js公式ドキュメントを参考にしています。

推奨構成 — Vue.js

{
  "compilerOptions": {
    "target": "es5", 
    "module": "es2015",
    "sourceMap": true,
    "importHelpers": true,
    "esModuleInterop": true,
    "moduleResolution": "node", 
    "allowSyntheticDefaultImports": true,/* import Vue from 'vue';のような形式を有効にする */
    "noImplicitThis": true, /* thisの型推論を有効にする */
    "baseUrl": "./",
    "paths": {
      "@/*": [
        "src/javascripts/*"/* '@/account/register'のようにimportできるようにする */
      ]
    }
  },
  "include": [
    "./src/javascripts/**/*.ts",
    "./src/javascripts/**/*.tsx",
    "./src/javascripts/**/*.vue"
  ],
  "exclude": [
    "node_modules"
  ]
}

この段階では "strict": trueにはせず、"noImplicitThis": trueのみに留めています。導入段階から厳しいルールを設定してしまうと、既存コードの移行が厳しくなってしまうという理由です。

 

型検査用のscriptを定義する

前述したように、ts-loaderでは型検査を行わないため、package.jsonに手動型検査用のscriptを定義します。

{
  ...,
  "scripts": {
    ...,
    "type-check": "npx tsc -p . --noEmit",
  }
}

npxでtypescript compilerを呼び出して型検査を行います。この際、--noEmitオプションを付与することで、ファイルのoutputを行わずに型検査のみを実行しています。

試しに既存の.jsファイルを.tsに書き換えて型検査を実行してみます。

 $ npm run type-check

> project@ type-check /path/to/project/frontend
> npx tsc -p . --noEmit

src/javascripts/account/register.ts:23:13 - error TS2339: Property 'name' does not exist on type 'unknown'.

23     if (elm.name == 'authenticity_token') {
               ~~~~

...


Found 39 errors.

上記のように、コンパイルエラーを検査することが出来ます。

前述したように、ts-loaderでは型検査を行っていませんので、この状態でもビルドは成功します

 

sfc.d.tsを作成する

Visual Studio Code等のエディター・IDEで .vueファイルのimportを解決するために、プロジェクト内に以下のようなファイルを追加します。(sfcはSingle File Componentの略です)

declare module '*.vue' {
  import Vue from 'vue'
  export default Vue
}

 

以上の修正は既にmasterブランチへマージされており、無事TypeScriptで開発できるようになりました。

既存ロジックの変更は行っていないため、障害等も特にありませんでした。

 

まとめ

いかがでしたでしょうか。

0→1でTypeScriptを導入することは簡単ですが、既存プロダクトに途中からTypeScriptを導入するのは心理的ハードルが高いように思えます。

本記事が、「稼動中のプロダクトにTypeScriptを導入したいけど、障害怖いしキツそうだなぁ…」といった方々の助けになれれば幸いです。

ラクスルでは、Vue.jsで新たなUIスタンダードを創るフロントエンドエンジニアを募集しています。

 

参考資料

エンドツーエンドテスト(E2Eテスト)をTestCafeとAWS CodeBuildでもっともっと手軽にする

ラクスルの@nnm_techです。

ラクスルの印刷サービスではこれまでにもE2Eテストがありましたが、この度より良いシステムを目指し再構築することになりました。 本記事ではTestCafeとAWS CodeBuildを用いたE2Eテスト刷新について紹介したいと思います。

ラクスルで運用している印刷サービスは裏側で複数のサービスが協調動作しています。 入稿サービス、発注サービス、決済サービス…外部サービスとの連携も含めるとかなりの数があります。
このような中、弊社では各サービスの変更時におけるユースケースの動作担保・リグレッション検知を目的にE2Eテストを実施しています。

続きを読む

GoのおすすめDIツール

はじめに

これまでラクスルではRubyを開発言語として採用することが多かったのですが、最近はコマンドラインツールやバッチ処理などでGoによる開発も増えています。
私が最近取り組んでいる印刷発注基盤の刷新プロジェクトでもGoを使ってWEBアプリケーションを開発しており、社内では先例がないこともあって色々と苦労しながらも楽しく開発を進めています。
GoによるWEBアプリケーション開発では、RubyにおけるRailsのようなデファクトスタンダードは存在しないため考慮すべき点がたくさんあります。
例えば、「パッケージ構成をどうするか」「WEBアプリケーションフレームワークを使うべきか」などですが今回はちょっと軽めのテーマとしてDIツールについて紹介します。

DIとは

DIとはDependency Injectionの略で「依存性の注入」と訳されます。
Goではインタフェース型の値をコンスタラクタの引数などで渡すことでDIを実現することが多いと思います。
具体的なコードを見てみましょう。

type DogUsecase struct {
	repo DogRepository
}

func NewDogUsecase(dogRepo DogRepository) DogUsecase {
	return DogUsecase{
		repo: dogRepo,
	}
}

func (u DogUsecase) All() []Dog {
	return u.repo.All()
}

type DogRepository interface {
	All() []Dog
}

type dogRepositoryImpl struct{}

func NewDogRepository() DogRepository {
	return dogRepositoryImpl{}
}

func (r dogRepositoryImpl) All() []Dog {
	// DBからDogを取得
}

func main() {
	// インターフェース型の値を生成
	repo := NewDogRepository()
	// コンストラクタの引数として値を渡す
	usecase := NewDogUsecase(repo)
	usecase.All()
}

上記ではDogUsecaseのAll()メソッド内でDogRepositoryを使っています。
そして、コンストラクタであるNewDogUsecaseの引数としてDogRepositoryを渡しています。
ポイントは下記の2点です。

  • DogUsecaseの内部でDogRepositoryを生成しているのではなく外から渡している
  • DogRepositoryをインターフェースにしている

DogUsecaseDogRepositoryというインターフェースにのみ依存しており、その具象型(dogRepositoryImpl)には依存していません。
そのため、DogUsecaseの単体テストを書く時にDogRepositoryの具象型をモックに変更することが可能です。
また、DogRepositoryの具象型の実装に変更(データの取得元をDBではなく外部APIに変えるなど)があった場合でも、インターフェースが変わらない限りはDogUsecaseのコードを修正する必要がありません。
このように、DIを使うことによってDBや外部APIといった実装の詳細を利用者側から隠蔽し、モジュール間を疎結合にすることで保守性やテスタビリティを高めることができます。

DIツール(dig)の紹介

上述したコードではmain関数の中でDIを行なっていました。
今回のようなシンプルなコードの場合はこれでも問題ないのですが、アプリケーションが大きくなっていくとDIのためのコードが肥大化しメンテナンスしづらくなります
そこで、これらのコードを自動化するためのDIツールが役に立ちます。
GoのDIツールはいくつかありますが、現在私たちのプロジェクトではuber-go/digを使っています。
digの特徴は何といってもシンプルで学習コストが低い点です。また、少し複雑なDIもやろうと思えばできる柔軟性も備えています。
先ほどのコードの中でDIしていた箇所をdigを使って書き直すとこのようになります。

func main() {
  container := dig.New()
  container.Provide(NewDogRepository)
  container.Provide(NewDogUsecase)
  container.Invoke(func(usecase DogUsecase) {
    usecase.All()
  })
}

dig.New()でDIコンテナを作成し、Provideでコンテナに型を登録します。
Provideの引数は、「返り値として登録したい型を返す関数」です。
例えば、NewDogRepositoryは返り値としてDogRepository型を返す関数なので、Provideの引数として渡すことによってDogRepository型をコンテナに登録することができます。
登録した型の値を取り出したい場合には、Invokeを使用します。Invokeの引数は、「利用したい型を引数にした関数」です。
ここでは、DogUsecase型を使ってAll()メソッドを実行したいので、DogUsecase型を引数とする関数がInvokeの引数になっています。
注目していただきたいのは、container.Provide(NewDogUsecase)という行です。
NewDogUsecaseは引数としてDogRepositoryを必要としますが、この行ではDogRepositoryを渡していません。
一つ前の行のcontainer.Provide(NewDogRepository)によってDogRepository型をコンテナに登録しているため、Invokeの実行時には自動でNewDogRepositoryの引数であるDogRepository型の値をコンテナから取得することができます。

一見するとDIツールを使わなかったコードの方が短くてシンプルに思えるかもしれませんが、アプリケーションが成長してモジュールが増え依存関係も複雑になっていくと、手動でDIをする必要がなくコンストラクタの引数によって自動的にDIできることのメリットは大きくなります。

digの応用的な使い方

digを使った場合、基本的には型をベースにしてDIが実行されますが、時には同じ型で別の値を使いたいというケースもあると思います。
例として、MasterとSlaveのDBを同じRepository内で使いたいというケースを考えてみます。

func NewMasterConnection() (*sql.DB, error)
func NewSlaveConnection() (*sql.DB, error)

type DBConnection struct {
  dig.In

  MasterConn *sql.DB `name:"master"`
  SlaveConn  *sql.DB `name:"slave"`
}

func NewDogRepository(c DBConnection) DogRepository {
  return dogRepositoryImpl{
    master: c.MasterConn,
    slave:  c.SlaveConn,
  }
}

func main() {
  container := dig.New()
  container.Provide(NewMasterConnection, dig.Name("master"))
  container.Provide(NewSlaveConnection, dig.Name("slave"))
  container.Provide(NewDogRepository)
}

上記では、NewMasterConnectionNewSlaveConnectionProvideの引数として渡す際にdig.Nameを使って名前付けしています。
そして、NewDogRepositoryではDBConnectionという構造体のフィールドの中で`name:”master”``name:”slave”`といったタグを付ける事で同じ型(*sql.DB)の中でdig.Nameに対応する値を取得することができます。
このように、digは少し複雑なDIをする場合も柔軟に対応できます。
今回紹介した機能の他にもオプショナルな型であったり、同じ型の異なる値をグループ化したりといったこともできますので、詳しくは公式ドキュメントをご参照ください。

まとめ

  • ラクスルでもGoで開発している!
  • DIはモジュール間の結合度を下げて保守性やテスタビリティを高める
  • 手動DIはアプリケーションが大きくなるとつらいのでDIツールを使うのがオススメ
  • uber-go/digというDIツールが便利

社内ハッカソンの意義 〜Raksul Hack Week #2 開催から見えてきたこと〜

こんにちは。ハコベル(物流サービス)チームのサーバサイドエンジニア 加藤です。

このブログでもお伝えしていた第2回社内ハッカソン Raksul Hack Week #2。6月第2週に開催しました。今回は開催のレポートをしつつ、そこから見えてきたものについて書いてみたいと思います。

開催概要と今回のテーマ

2回目となる今回も、Raksul Hack Week #1から基本的な開催概要は変えずに企画しました。

  • 参加者はエンジニア、プロダクトマネージャー、デザイナーとする
  • ハッカソンの期間は1週間とし、期間中はハッカソン100%コミット、普段の開発業務はやらない(※ ただし緊急対応系は最優先で)
  • チーム制とする
  • 最終日に各チームは成果発表する
  • ラクスルの事業、ステークホルダーに関わることであれば何に取り組んでも良い

結果として、4日間の作業期間+1日の発表会&表彰式という日程で、2〜5名の16チームが参加する大イベントとなりました。運営は各事業部から集まったエンジニア・PM・デザイナーの4人で行いました。

そして今回のテーマは、

 

みんなで創る次の世界

〜好きなことから始まる挑戦〜

課題 ✖️チーム✖️技術

 

第一回の開催の後、様々な背景を持ったメンバーが増えて続けているプロダクトチーム。普段は業務に集中していて、視野を広げたり、チームを超えてお互いを知り合う時間が十分にとれないのが現状です。今回は、普段の仕事の範囲を超えて、お客様や社内の課題を共有し、チームと技術の力を使って、次の世界を実現するきっかけにしようという気持ちを込めました。

開催までの取り組み

今年は、以下のように開催2ヶ月前から次ごとにテーマを区切り準備を進めてきました。

  • 4月:アイデア月間
  • 5月:チーミング月間
  • 6月:Raksul Hack Week #2

この期間に、各事業部のビジネスメンバーからお客様の課題、自分たちの業務の課題やこんなのがあれば嬉しいといった声を聞くランチイベント Hack Week Lunch や、チームビルディングイベント Hack Week Pre-event を開催しました。

詳しくは、今年も Raksul Hack Week を開催します! や いよいよRaksul Hack Weekが始まります!をご覧ください。

いよいよ本番

まずは、初日のキックオフ。毎週の全社朝会の後、スケジュールや注意事項の説明を聞き、くじで発表順を決めていざ開始!

キックオフの様子

初日は、それぞれのチームで、4日間をどんな風に進めるか、課題を整理したりプランニングしたりする様子が見受けられました。

ホワイトボードを前にチームでディスカッション

進め方を決めたあとはそれぞれで作業を進めたり、ラクスルでもすっかり定番になった、ペアプロやモブプログラミングを取り入れて進めるチームやユーザ調査に出かけるチームもありました。

モブプロで作業を進めるチーム

連日、いつもより遅くまで作業を続ける姿が見られました。すごいコミットメントですね。

バイキングデスクで仲良く作業する2人

ベトナムからは今回は2チームが参加。

作戦を練り直すベトナムチームのメンバー

発表会

発表会は、参加メンバーはもちろん、弁理士の先生や、ボードメンバー、ビジネスメンバーも参加して行われました。持ち時間は、各チーム7分。お昼を挟んでの2部構成で行いました。

ネットワークがつながりにくいというトラブルがある中、それぞれ、デモまで行って聞き応えのある発表となりました。1週間の熱量が発表にもこもります。

チームみんなで発表

技術的にも、AI、IoT、モバイルアプリやフロント技術を使ったものや、組み合わせ方に工夫を凝らしたものなど、様々な取り組みが発表されました。どのチームも課題設定が素晴らしく、ボードメンバーからも賞賛の声がありました。

発表を聞き入るCEO 松本とCTO 泉

投票&表彰式

前回は、シールをボードに貼る形式で行われましたが、今回は集計のことも考えて Google Form を利用。一人2チームを選ぶ形式で投票を行いました。また、どこが素晴らしいと思って投票したのか、投票コメントも記入してもらうようにし、翌週に全チームにフィードバックしました。

受賞したチームや受賞は逃したものの評価が高かったチームを3チームほどご紹介します。

技術負債の可視化ツールに取り組んだのは、以前からこのブログにも度々投稿している、エンジニアマネージャーの二串とチームメンバー。ラクスルは以前から技術負債解消に果敢に取り組んでいますが、その取り組みをダイエットに見立てて、楽しく可視化しようとしたアイデアがみんなの心を掴みました。

技術負債可視化チーム

そして今回一位は、古参の岡田と新卒からラクスルにジョインしている3人のメンバーのチームでした。

印刷マッチングプラットフォームであるラクスル。その重要なテーマの1つである、発注周りの改善提案に取り組みました。

ビジネスメンバーが思いつかなかった課題にテクノロジーを使って切り込み、見事な改善提案で、みんなをあっと言わせました。

リーダーシップをとったのは、3年目の岸野。「以前から個人的にやるべきだと思っていた課題。Hack Week という機会に取り組むことができました。」とコメント。ジョブローテーションをしながら、確実に成長している3人の今後がますます楽しみになりました。

CEO松本と1位のチーム

ちなみに私のチームは、CEO賞を受賞しました。ハコベル(物流サービス)の技術スタックを横展開し、他事業で数年前からの課題を解決するWebサービスとアプリを開発しました。運営しつつも、今回のテーマ「みんなで創る次の世界」を体現したいと思い、CTOの泉、アプリエンジニアのライアン、デザイナーの武末とともに、トライアル運用を目指して取り組みました。

未来を見つめるCEO賞受賞チーム

ここで紹介しきれませんが、本当にどのチームの成果も素晴らしく、今後どのようにするのかは継続議論がされることになりました。

懇親会で締めくくり

みんな全力投球したHack Weekを終えての懇親会は、それぞれ取り組んだテーマの話で盛り上がりました。また、期間中のエピソードもたくさん聞くことができました。

懇親会で挨拶するCTO

Raksul Hack Week #2 開催から見えてきたもの

Hack Week後のアンケートを通じて、参加メンバーからたくさんの声をいただきました。

「Hack Week Lunchもラクスル自体の理解度解像度が上がり良かった」

「普段あまり関わりの無いメンバーと開発でき、充実していた」

「ゼロからのプロダクトに少人数で考えて向かうという、実は普段あまりチャンスが無いようなことができたことにとても価値があったと思う」

「新しい技術にトライできて勉強になったし楽しかった」

「自分自身の課題も見えたり、メンバーとの交流できて、良い経験が得られたなと思った」

 

これらの声や運営している中での気づきから、ラクスルにおける社内ハッカソンの意義を私なりにまとめてみました。

  1. 学びの機会
    – 技術だけでなく、ビジネス課題に視野を広げる
    – 課題設定、プランニング、ゼロイチの開発を自分たちで行う力を養う(広義の開発力の向上)
  2. コミュニケーションの機会
    – 成長しているエンジニア組織でのチームを超えたコミュニケーションの促進
    – お互いを応援・賞賛しあう文化づくり
  3. 挑戦の機会
    – 新しい技術への挑戦
    – 結果が見えないテーマに挑戦(失敗できる機会)
  4. ビジョンの実現の加速の機会
    – 動くものを見せることで未着手のテーマに本格的に取組むきっかけにする

Hack Week はさながら冒険の旅のようでした。出発前の戸惑いあったり、チームで課題を乗り切る場面があったり。翌週出会ったみんなは、心なしか一回り力強くなったように感じました。

表彰式後の集合写真。何のポーズかな??

1週間業務を止めて行う社内ハッカソンの開催は、それなりの投資。決定するのにも勇気が要りますし、運営チームや参加者を含めて準備もあります。でも、上記にまとめたように、今回それ以上に意義のあるイベントだと思いました。

エンジニア組織の活性化をお考えの方は、社内ハッカソンに取り組んでみてはいかがでしょうか。

ラクスルでは事業をつくっていきたいエンジニアを絶賛募集しています!

自分の書くコードで、印刷・広告・物流といった大きな産業の課題を解決するようなインパクトを作り出してみませんか。技術・業界・業務など多角的に理解を深めながら、自らアイデアを出し、主体的に事業をつくっていく。そんな働き方をしたい方をお待ちしています!

ハコベルチームはHack Week後から五反田の新オフィスで勤務。新オフィスにもぜひ遊びに来てください!

gRPC Client Interceptor入門 with Ruby

こんにちは。サーバサイドエンジニアの三瓶です。印刷ECサイトの ラクスル の開発保守を担当するチームに所属しています。

ラクスルでは Raksul Platform Project (RPP) と称して技術負債の返済活動を継続的に行っているのですが、その流れの一環として印刷ECチームでは巨大化したアプリケーションから商品仕様に関わる部分を別サービスとして切り出す、という作業を最近行いました。

この切り出したサービスでは通信方式として gRPC を、言語としてはRubyを採用しています。

実際に導入してみて、gRPCはまだドキュメントも多くはないと感じたので、本記事ではgRPCが備える機能の1つである Interceptor についてチュートリアル形式でご紹介したいと思います。言語はもちろんRubyを使います。

※ gRPC導入に至った背景や経緯について興味のある方は弊社エンジニアの二串のスライドをご参照ください

Interceptorとは何か

Interceptorとは、RPCコールの前後に任意の処理を差し込める機能・レイヤーのことです。Railsアプリケーションに馴染みがある方は Rack Middleware のようなものを想像してもらうと理解しやすいかと思います。

Interceptorを使えば、認証やロギング、データのフィルタリングなど各通信で共通の処理をひとまとめにできます。
実際に、我々のプロジェクトではリクエストを一意に特定するためのIDをmetadataに付与するInterceptorやリクエストしたメソッド・パラメータ・処理時間をログに残すInterceptor、例外をCatchしてSentryに通知するInterceptor等を自作して、本番環境で稼働しています。

以下はClient → Server間の通信に2つのInterceptorが介在したときのイメージ図です。

作成するClient Interceptorの仕様を決める

ClientのInterceptorと一口に言っても、以下のパターンが考えられます。

  • リクエスト処理の「前」に差し込む。リクエストデータに何か手を入れたい場合など
  • リクエスト処理の「後」に差し込む。レスポンスデータに何か手を入れたい場合など
  • リクエスト処理の「前後」に差し込む。両方に手を入れたい場合や処理全体に何かをしたい場合など

このうち、今回はリクエストの「前」に差し込んでリクエストデータを大文字化する UpcaseInterceptor と、リクエストの「前後」に差し込んで処理時間を計測する PerformanceLoggingInterceptor の2つを作ってみることにします。

以下、それぞれのインターセプターの簡単な仕様です。

  • UpcaseInterceptor
    • リクエストの前に差し込む
    • リクエストオブジェクトに name というキー名のデータあればその値を大文字化する
  • PerformanceLoggingInterceptor
    • リクエストの前後に差し込む
    • リクエスト全体にかかった時間を計測し、標準出力へ出力する
    • 時間は小数点第3位以下を四捨五入する

また、動作の流れは次の図のようになります。赤い線の箇所でインターセプターの処理を挟みこむイメージです。

Client Interceptorを実装する

※ 注意:gRPCのRuby実装では、IntercptorのAPIインターフェースは EXPERIMENTAL という扱いのようです。そのため、今後のバージョンアップで今回紹介するものとはAPIインターフェースが変わっている、ということがありえますのでお気をつけください。

Interceptorを実装するのはとても簡単で、今回の仕様であれば数行で作れてしまいます。

UpcaseInterceptor

UpcaseInterceptorの実装は次のようになりました。

# upcase_interceptor.rb
class UpcaseInterceptor < GRPC::ClientInterceptor
  def request_response(request: nil, call: nil, method: nil, metadata: nil)
    request.name = request.name.upcase
    yield
  end
end

いくつかポイントとなる部分を説明します。

(1) GRPC::ClientInterceptor を継承する

インターセプタークラスは GRPC::ClientInterceptor クラスを継承する必要があります。
継承しない場合、GRPC::InterceptorRegistry::DescendantError 例外が発生します。

(2) 通信方式に合わせてメソッドをオーバーライドする

メソッドは利用する通信方式に合わせてオーバーライドします。gRPCには以下4つの通信方式があり、それぞれに対応するメソッドがあります。

  • Unary RPC : #request_response
  • Server streaming RPC : #server_streamer
  • Client streaming RPC : #client_streamer
  • Bidirectional streaming RPC : #bidi_streamer

今回は、1リクエストに対して1レスポンスが返ってくる一番シンプルな通信方式であるUnary RPCを使うため、request_response メソッドをオーバーライドしています。

※ 各通信方式の詳細が気になる方は https://grpc.io/docs/guides/concepts/ をご参照ください

(3) yieldで次の処理を呼び出す

次にメソッドの中ですが、yield を呼び出すことで次のインターセプターまたはリクエスト処理の本体を呼び出すことになります。
UpcaseInterceptorはリクエスト実行の前に呼び出したいので、yield をコールする前に変換処理をしています。

PerformanceLoggingInterceptor

同様に、処理時間を計測して出力するPerformanceLoggingInterceptorは次のようになりました。

# performance_logging_interceptor.rb
class PerformanceLoggingInterceptor < GRPC::ClientInterceptor
  def request_response(request: nil, call: nil, method: nil, metadata: nil)
    start_time = Time.now.to_f
    resp = yield
    request_time = Time.now.to_f - start_time

    puts "duration: #{request_time.round(3)} sec"
    resp
  end
end

PerformanceLoggingInterceptorはリクエスト処理の全体を計測したいため、リクエストの前後に処理を差し込みます。
前述のとおり、リクエスト処理は yield を呼び出すことで伝播されていくため、その前後を囲う形で時間を取れば良いということになります。

返り値は yield で受け取ったレスポンスオブジェクトをそのまま返します。

Interceptorを使う

それでは、作成した2つのInterceptorを実際に使ってみましょう。
gRPC公式のチュートリアル Ruby Quick Start にあるHelloWorldアプリケーションに、今回作成したインターセプターを組み込んで動かしてみます。

Client Interceptorは、Stub と呼ばれるクライアントオブジェクトを初期化するときに引数として渡します。

# greeter_client.rb
require 'grpc'
require 'helloworld_services_pb'

# ★ 作成したインターセプターをrequireする
require_relative './performance_logging_interceptor'
require_relative './upcase_interceptor'

def main
  stub = Helloworld::Greeter::Stub.new(
    'localhost:50051',
    :this_channel_is_insecure,
    interceptors: [UpcaseInterceptor.new, PerformanceLoggingInterceptor.new] # ★ ここでインターセプターを設定する
  )

  # 省略..
end

main

interceptors 引数に渡す順序でインターセプターの実行順序が決まります。
配列の末尾から先頭へと順に実行されるため、上の書き方の場合、PerformanceLoggingInterceptorUpcaseInterceptor の順で実行されることになります。当初のイメージ図の通りの順序ですね。

それでは実行してみましょう。
まず、比較対象としてインターセプターを使わなかった場合の結果を見てみます。

$ bundle exec ruby greeter_client.rb
"Greeting: Hello world"

チュートリアルの通りであれば、上のように “Greeting: Hello world” という結果が出力されると思います。

次にインターセプターを有効化した状態で実行してみます。

$ bundle exec ruby greeter_client.rb
duration: 0.002 sec
"Greeting: Hello WORLD"

UpcaseInterceptor によって “world” が大文字化されて、PerfomanceLoggingInterceptor によって実行にかかった時間が出力されました!

まとめ

以上、簡単ではありますが、RubyでのgRPC Client Interceptorの仕組みや使い方についてのご紹介でした。

アプリケーションを本格的に運用するにつれて様々な要求が発生してくるかと思いますが、Interceptorが役に立つ・解決手段となるケースもあるのではないかと思います。そのようなときにこの記事が参考になれば幸いです。

ラクスルテレビCMオンラインストアの初期開発の進め方

こんにちは。フロントエンドを担当している古谷です。

今回は、ラクスルテレビCMオンラインストアを開発開始からリリースするまでの進め方についてご紹介します。

こちらの記事は、以前行われた 【ラクスル×KCF】FrontendNight というイベントにて 本編には惜しくも入れられなかった部分を中心に構成しています。

イベントで発表したこちらの資料もあわせてご覧ください。

Storybookを使って安心しながら開発を進める ラクスルテレビCMオンラインストア開発

こちらの資料では、「進める際のリスクがなにかをしっかりと想定すること、そのための対応手段の選択肢を増やすために技術力をあげていくことが大事。」とまとめています。

この発表では主にツール、技術の選定にフォーカスして発表していました。
この記事では、進め方について、実際のPRの画面を交えながら紹介していきます。

速度を保ちつつ開発する進め方

こちらも、上記資料と同じくどんなことが起きたら開発がうまく回らないかを考えていました。

以下の不安があり、それらに対して対策を打っていました。

  • 技術選定はうまくいっても速度が出ないのではないか
  • 仕様や実装の変更による手戻りが多いのではないか

速度が出ない問題にはどう対処したか

基本的なことではありますが、PRの単位を小さく保つこと、そして丁寧にすることがあげられます。

こちらが実際のPRです。

(※ 小さくとかいてあるわりに、Files changed が15なのは、Snapshotで多くのコンポーネントに影響が出ているからです。)

PRのテンプレートを作った

おそらく多くのプロジェクトで行っていると思いますが、PULL_REQUEST_TEMPLATE.md の形式を整えます。 チケット番号、実装したものの概要は書くとして、それ以外になぜその実装にしたのかという理由を書くことが大切です。

また、(実装者が想定している)影響範囲を書いておくことで、全体把握をしているという信頼関係の構築がしやすくなると思っています。 リリース前だからこそできることですね。

## 概要

簡潔に何ができるようになるかを書きます。
また、その背景や対応するチケットがあれば貼ってください。

その実装にした理由をコミットメッセージに加えてこちらに書いてください。
その他、レビューを厚めにしてもらいたい点があれば書いてください。

## 影響範囲

Storybookのスクリーンショットなどわかりやすいものがあれば貼ってください。
Propsの変更など大きな変更があれば書いてください。

## TODO

* [ ] storybook
* [ ] このPRをmergeするために必要な残タスクがあれば列挙します

## このPRでやらないこと

このPRでは対象外にすることを書きます。
チケット、ISSUE、FIXMEを使って管理してください。

スクリーンショットを乗せるようにした

フロントエンドの開発では、スクリーンショットを乗せることでPRを中心にコミュニケーションが回ります。 スクリーンショットを乗せることで、仕様に沿った動きをしているかどうか、挙動の認識があっているかを一目でわかることが大切です。

レビューイはスクリーンショットを撮るために、当然手元で実装物を再現させる必要があります。

動作確認してからPRを出すのは当然ですが、複数パターン考えられるものを撮ったりするうちに、考慮漏れに気づいたり、モバイルサイズにした時に思わぬ挙動を見つけたりするのではないかと思います。私もこの段階で考慮漏れに気づくことがありました。

手元での動作確認は基本の所作ですが、 スクリーンショットを撮るということをフローに入れるだけで、うっかりは少なくなるはずです。

レビュアーも手元で再現させるのは当然ですが、PRで見るべき箇所がわかりやすいのでレビューしやすくなります。

やらないことを明記した

開発期に関しては、PRをためてしまい脳内バッファを消費することがないように、多少中途半端であってもmergeしてしまうことが大切と考えます。

また、完成度の高い物を作っても直前に変わる可能性もあるため、過度な作り込みは厳禁です。

とはいえ、中途半端なものがリリースされてしまっては問題なので、PRに明記することとチケットやコード内 FIXME などで残すようにしています。

ここで、想定している「ここでは積み残している事」を共有する事で、レビュー時に実装者の考慮漏れを見つけることができますし、積み残しを個人のタスクからチームのタスクに開放することが出来ます。

手戻りを発生させないようにどうしたか

手戻りの原因は沢山あると思いますが、一つはエンジニア以外とのコミュニケーション不足で起こるものがあると思っています。

これに関しては、PRにスクリーンショットを貼ることである程度対応ができました。

PRに貼るスクリーンショットをアニメーションGifにしておくと、連携したチャットツール(Slack)にPRに貼られた画像が流れます。Slackに動く画面が流れれば、エンジニア以外も実装している内容に関して興味が持てるようになります。

このPRの動くスクリーンショットと、Slackの絵文字コミュニケーションとあわされば、実装中のものに対して誰でもリアクションが出来る状態が保たれます。


※ 価格データなどはランダムなものを使っています
※ 実際のSlackの画像を編集しています

これによって成果物をベースとしたチーム全体の認識共有に繋がったと思います。 対面での進捗報告や、確認環境へのデプロイも大切ですが、もっと周囲がフォローしやすい形に持っていく事で、実装の認識の齟齬を発生しにくくすることができました。

まとめ

ラクスルテレビCMオンラインストアの初期開発では、PRをこまめに出すことと動くスクリーンショットを貼ることを心がけました。

まわりの関心を引きつけつつ、抜けもれなく開発を進めていました。

フェーズやチーム構成によって最適な進め方は違うと思いますが、参考になると幸いです。

ラクスルでは、新サービス開発をリードするフロントエンドエンジニアを募集しています。

 

ハコベルコネクトをリリースしました!

サーバサイドエンジニアの加藤です。
昨年8月に印刷から運送マッチングサービスのハコベルのチームに異動し、ハコベルコネクトの開発を進めてきました。

ハコベルコネクトとは

現在の物流業界における最大の課題はドライバー不足。ハコベルチームでは、現場の課題を調査し、その原因を探ってきました。見えてきたのは、複数の運送会社によって仕事が行われていることで、システム化が進まず紙・電話・FAXなどアナログで生産性を上げにくい現場環境でした。

ハコベルコネクトは、この情報断絶を解決すべく、大手物流荷主、一般貨物事業者など運送会社を自由度高くつなぐ物流プラットフォームを目指しています。

先月 1月24日に記者発表をし、日経新聞やTechCrunchにも取り上げられました。弊社CTOの泉がスマホのGoogle Chromeの新規タブで出てきたとのことで、社内でもちょっとした話題になりました。

今までも提供してきたサービスは、ハコベルカーゴ※に名称変更。これからもラストワンマイル向けのマッチングサービスを提供していきます。

仕事をする人に心地よい体験を

ハコベルコネクトは、PMやデザイナー・エンジニアが現場に足を運び、MVPを開発、実証実験を重ねて、徐々にその姿を明らかにしてできてきたプロダクトです。いくつもの運送会社の事業所に出向き、たくさんの方々にインタビューさせていただいたり、トラックに同乗させていただいたり。仕事をする人がどんな課題と向き合っているのか。様々なプロセスを経て、方向性が定まってきました。

ドライバーアプリイメージ

 

ハコベルコネクトという名前もその中で生まれてきました。SNSのように、会社と会社がつながりスムーズに情報のやりとりができることで、快適に仕事が出来るようにという想いが込められています。

ハコベルコネクトイメージ

技術負債の芽を最低限に ー今しかないでしょ!ー

さて、プロダクトとしてのリリースに向け、エンジニアとしてはどんなチャレンジがあったでしょうか。

  • 実証実験向けに急いで作られた側面があったため、既に技術負債気味なところがある
  • 業界独自の業務をモデリングしているため、キャッチアップしにくい
  • よくある機能でも複雑度が高く、要件の不整合が起きて開発が進みにくい

ハコベルチームには、私を含めラクスルの開発に関わってきたメンバーもいます。

「そうだ、ラクスルを作り直そう!」という投稿や「生まれ変わらNight -技術的負債からの一発逆転-」というイベントでご紹介させて頂いた通り、ラクスルは技術負債の課題に向き合ってきました。

(ちなみにまだまだ先はありますが、経営陣の理解もあり、ラクスルの技術負債の解消は着々と進んでいます。)

その教訓を活かして、ハコベルコネクトでは、リリース前に負債の芽の解消にも時間を使ってきました。リリース前ならば、ユーザー影響を気にせずにあらかじめアーキテクチャの整備を進めることが出来ます。「今しかないでしょ!」を合言葉に、気なっていたところはどんどん書き直しました。

ただし、特定業界向けのSaaSというサービスの成長特性を考えて、最初からドメイン分割やマイクロサービスを採用することはしていません。ラクスルで蓄積してきているベストプラクティスを取り入れ、まずはRails アプリケーションとしての Rail にきれいに乗るというところに集中しました。

フロントは、ラクスルの他のプロダクトでも採用しているVue.jsを使い、実証実験後にUIをフルリニューアル。Web APIが必要だったため、コントローラやサービスは約80%を書き換えました。

Web APIの開発中には、GraphQLが話題になったり、gRPC-Webが正式リリースになったりしたタイミングで、チーム内でGraphQLのPOCも行なったりしていたのですが、開発の状況を考慮して今回はオーソドックスにRESTful APIで開発していくことにしました。

フロントメンバーとのコミュニケーションやAPI エンドポイントの可視化のため、API仕様はSwaggerで記述することにしています。

チームでプロダクトを開発する ーペア&モブプロ、デザインスタジオー

先ほども書いた通り、ハコベルコネクトは、業界独自の業務をモデリングしているためキャッチアップしにくくチームの生産性がなかなか上がりにくい状態でした。

6ヶ月間でチームも倍増。新メンバーのキャッチアップスピードをあげたり、モチベーションを保ちつつ機能開発スピードを上げるのが重要課題になっていました。

そのため、複数人でコーディングするモブプログラミングやペアプログラミングを取り入れて、難しいドメイン知識を共有しつつ開発を進めたり、モチベーションが上がりにくい機能開発をわいわいと議論しながら進めました。

モブプログラミング用のスペースも導入され、今ではハコベルだけでなく他のチームで取り入れています。

モブプログラミングの様子

また、ラクスルの他のチームで行なっていた、コラボレーティブデザインの手法である「デザインスタジオ」も取り入れました。機能に対して色々なアイデアが出るのはもちろん、デザイナーも含めてこれから作る機能のデータ構造を共有したり、機能の背景を理解するのに役に立っています。

ハコベルチームには、運送業界出身のメンバーもいます。要件がわからない時はすぐに話を聞けるのも、チームで開発を進める上ですごく助けになりました。

新メンバー募集中です

さて、ハコベルコネクトはリリースしましたが、私たちの毎日を支えてくれている物流の現場には課題がたくさんあります。課題を解決すべく、ハコベルコネクトも成長させていきたいと思っています。

「仕組みを変えれば、世界はもっとよくなる」

エンジニアリングの力で世界を変えたいフロントエンドエンジニア、サーバサイドエンジニアを絶賛募集中です。興味を持たれた方は是非一度オフィスに遊びにきてください!

※ 2019/3/4 ハコベルマッチングからハコベルカーゴに再名称変更したためブランド画像更新しました。

MeetUpシールがとことん簡単に作れるプロダクトの話

突然ですがシールの作り方、わからなくないですか!?

ラクスルなら誰でも簡単にデザインできるサービス担当の武政です。

弊社でもミートアップや勉強会がよく行われていますが
企画にステッカーがあると一体感がでてテンションも上がりますよね。

でも忙しい中でデザイナーさんにお願いするのは申し訳ない…外注だと予算もかかる…
そんなお悩みの声に応えて、パパッとステッカーを制作できるプロダクトをリリースしました!

オンラインデザインのシール・ステッカー・ラベルです。

  • 円形、正方形、長方形が合計9サイズ
  • シール・ステッカーのテンプレートも約300点公開(2018/11現在)

MeetUp用のシールを作ろう!

私もオンラインデザインで、リリース記念のシールを作ってみました

MeetUp用シールの無料デザインテンプレートも公開!

ミートアップですぐ使えるテンプレートも用意しました!
ぜひロゴやテキスト入れて遊んでみてください。
※画像をクリックするとオンラインデザインに遷移します

①ミートアップ:おしゃれ  (円形 直径50mm)
円形はPCに貼るには定番ですね

テンプレート編集はこちらから>>

②ミートアップ:シンプル (正方形 50*50mm)
ロゴを置いて、背景色をロゴ内の色に変えるだけでそれらしくなります

テンプレート編集はこちらから>>

シールでこんな遊び方も!?

少年時代に熱中したキラキラ風シールも作れちゃいます。
③キラキラ風シール:※ホログラム加工ではありません (正方形 50*50mm)

テンプレート編集はこちらから>>

シールになった弊社CTOもニッコリ。

シール制作プロセスの何をシステムで解決したのか?

シールのデータ制作はコンテンツとしては驚くほど簡単ですが
制作データの構造が難しいという不思議な商品です。

  • 具体的に難しいポイントは「カットパス」データの作成
  • カットパスとはシールの形に切り抜くためのパスデータのこと
  • カットパスと聞いた瞬間に印刷業界以外の方は「んんん?」となりますよね

デザイナーさん以外にはこの「カットパスの理解と制作」が難しい。
これがシール作成における「できる/できない」を決定づける障壁です。


システムが解決したシール制作者の課題は「カットパスをつくる必要がない」という部分です。
これには制作が簡単になることに加え、制作作業の大幅な短縮効果があります。

このようにしてシールが簡単に作ることができるプロダクトができました。
ぜひお試しください!
https://raksul.com/online-design/sticker/

ラクスルではエンジニアを積極採用しています

ラクスルは画像処理や印刷技術、SVGに関わる開発ができるレアな環境です。
興味を持たれた方は是非一度オフィスに遊びにいらしてください!

1週間やる、楽しい社内ハッカソンの作り方。

RakSul Hack Week Logo

はじめに

こんにちは。ラクスルプラットフォームチームのエンジニアの二串です。

先日、ラクスルではRaksul Hack Week #1という1週間通しでやる全員参加型の社内ハッカソンを開催しました。その運営を担当しまして、本記事では1週間やる楽しい社内ハッカソンの作り方をご紹介します。

開催の背景

もともとラクスルのエンジニア向けの制度としてHack-It Dayという月に1日、自由なテーマで開発することができる日がありました。もちろん活用はされていたものの、1日では満足いくものが作れなかったり、日々のリリースを優先してしまったり…といった課題も見えてきていました。そこで今回「Raksul Hack Week」に名称を改め、皆で1週間集中して面白いプロダクト・サービスを作ったり、新しい技術チャレンジをしたりする機会をつくろうと考えました。

第1回目となる今回のテーマは「HACK THE SYSTEM, HACK THE WORLD. (仕組みをハックすれば、世界はもっとカッコよくなる)」です。1週間通しでハッカソンをやる、というのは個人的に(そして他のエンジニア達も)初めての体験でありましたが、結果として自分たちの設定したテーマに全力を注ぐことができてよかったと思います。取り組んだ内容は記事後半の方で紹介します。

イベント概要

どんな感じのイベントだったのか?

  • 参加者はエンジニア、プロダクトマネージャー、デザイナーとする
  • ハッカソンの期間は1週間とし、期間中はハッカソン100%コミット、普段の開発業務はやらない(※ ただし緊急対応系は最優先で)
  • チーム制とする
  • 最終日に各チームは成果発表する
  • ラクスルの事業、ステークホルダーに関わることであれば何に取り組んでも良い

開催週の月曜日が祝日のため実際には前3日間開発、4日目成果発表会となりました。開催場所は企画段階で社外のコワーキングスペース等を借りるという案も出ましたが、初回ということで慣れたオフィス内開催としました。

準備

参加者の自発性を重視する形で、チーム構成は自由、また取り組む内容もチーム毎に自由に決定可能としました。また、イベント開催1ヶ月以上前から、各自で取り組みたいことを考えてもらってアイデアを膨らませてねーとお願いし、少しつづ Hack Week を盛り上げることにしました。

今回やったチームを決めるまでの流れは次の通りです。

  1. まず、社内(非参加者方面)からネタ(お困り毎など)を募集する
  2. 1を参加者に展開してアイデアの種にしてもらう
  3. 参加者は各自やりたいことをシェアする(専用confluenceページに書いてもらう)
  4. 出たやりたいことに賛同した人たちでチームを組む、もしくは3の起票者がメンバをリクルーティングしたりして組んでもらう
  5. チームを決めかねてる人たちはシャッフルで決定。取り組み内容は3を参考に決めてもらう
  6. Google Formでチームエントリ

全チーム構成が決まったのが開催の1週間前でした。この時点で大体のチームがやることもざっくり決めれている状態でした。

普段の業務に近いメンバーと組んだチームもありましたが、結構普段組まないメンツでの構成のチームもあり、結果的には何が出来上がるかな〜と期待が膨らんでくるチーム編成となりました。

ステッカー作りました(もちろんラクスルで作りました)

ハッカソン当日

ハッカソンが始まってしまえばあとは各チームもくもくと作業するだけです。社内の様々な場所で開発が行われました。

ちなみに、私は運営ではありましたがチームの一員としても参加しており期間中はがっつりと開発してイベントを楽しむこともできました。事前の準備が大事! 普段はサーバサイドの開発がメインですがHack Weekでは違う技術をということでTypeScriptNuxt.jsでフロントエンド中心のプロジェクトをやりました。難しかったですが勉強になりました!

ちなみに、1日の終わりに各チーム進捗報告をSlack #hack_week チャネルにしてもらうようにしました。進捗サマリとともに開発風景やスクショ等を共有してもらうことで、チーム間でもコミュニケーションが生まれたり、ビジネスメンバ等も様子を垣間見れたりして良かったとおもいます。

CTO泉率いるチームの開発風景。なんか楽しそうです。

 

ベトナムの開発拠点もリモート参加

取り組み内容紹介

一部ではありますがアウトプットを紹介します!

Fax2Web

アナログとデジタルとのブリッジになるための技術開発、ということでFAXとwebをつなぐチャレンジ。FAXを受信するとシステム連携されwebの注文ステータスが変更されたり、文字認識により自動入力されたりする仕組みのPoCを取り組みました。

メンテゲーム

サイトのメンテ中に遊べるゲームの実装にトライ。システム構成は Vue CLI 3 を使った静的サイト。

発表会・打ち上げの様子

発表会は1チームづつ順に発表。ベトナムの開発拠点とも接続して中継しました。

発表会の様子

 

1週間のハッカソンを振り返って話題が尽きない打ち上げとなりました

その他細かい運営のこと

参加必須のため参加者が50人弱ということでそれなりに入念に準備しました。まず、各チームの開発繁忙期と被らないようにスケジュール調整し日程を決めました。その後は運営コアメンバを招集してのキックオフ(これが開催3ヶ月前)、あとは週1回定例会を開きまして、少しづつ準備進めていきました。

なお、企画にあたっては以下の記事を参考にさせていただきました。有益な情報ありがとうございました。

まとめ

Raksul Hack Week #1 という社内ハッカソンを開催しました。

初めての長期社内ハッカソンということで、開催前には本当に盛り上がるのだろうか、普段1週間開発を止めてまでやるほどの価値が出るだろうか、といった不安も運営的にはありましたが、結果的には開催後のアンケートや、また経営陣へのイベント後の振り返り報告でも好評で良いイベントになりました。

普段の業務では要件通りに仕事をするということは大切ではありますが、今回のようにエンジニアやデザイナーの自発的でクリエイティブな発想で仕事をするというのも価値があることだなと感じました。また、使ったことのない技術スタックを試す機会としても良かったとおもっていて、実際今回使った新しい技術を業務に取り入れることにしたチームもありました。

今後もラクスルのテックカルチャーの1つとして開催していければいいなとおもいます。