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

ハコベルの中村です。

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

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

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

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

 

続きを読む

オンラインデザインのPHPからRailsへのリプレイス

こんにちは。サーバサイドエンジニアを担当している新卒のカグラです。

今回はオンラインデザインのRailsへのシステムリプレイスについて紹介します。

オンラインデザインとPHP

ラクスルでは、入稿データを無料でデザインできるサービス「オンラインデザイン」を提供しており、たくさんのお客さんに使ってもらっています。

オンラインデザインは、数年前にPHPで作られたアプリケーションです。そのため当時のレガシーな設計や、今後のスケールを考えた時に複数人でパフォーマンス高く運用するのが難しそうな設計が多くあったため、現在PHPの完全廃止とRailsへのリプレイスを進めています。

スムーズに開発を進めるために

オンラインデザインの多くのプロセスは、ajaxを使った非同期処理で作られており、現在50個ぐらいのAPIがあります。呼び出し元のフロントもjQueryを使った複雑な処理が多い状態でした。
使ってくれているお客さんがいる中で、サーバーサイドとフロントエンドとの複雑な処理を一緒に移行してしまうと、デザインのデータ内容の互換性などで思わぬ障害を招いてしまう危険性が高くなるため、一度に両方を修正するのではなく、まずサーバーサイドのロジック部分をRailsのAPIとして開発し、PHPから呼び出すようなプロセスにしました。

このように進めていますす。

① 元の状態

② PHPのAPIロジックがRailsのAPIに置きかわった状態

③ リプレイス完了状態

今RailsのAPIの開発はほとんど終わりました。開発当初ほぼ一人でPHPの立ち上げを行っていたこともあり、複数人での長期運用には向いていない設計となっていたため、今回開発中に作業コンフリクトなどいくつかの問題が起こりました。例えば、PHPを一部修正して、一般的なRailsのAPI呼び出し処理手順をまとめたAbstractクラスを作ってextendするようにして回避したりしました。
また、万一RailsのAPIレスポンスに問題があった時などに、元のPHPの処理にfallbackすることができるようにもしています。
以下のようになります。

abstract class CallApi  {
    abstract function internalApi($params, $endpoint);
    abstract function checkFallback($response);
}

class DesignService extends CallApi {
    function internalApi($params, $endpoint) {
        ...........
        return checkFallback($response);
    }
 
    function checkFallback($response){
        return $response.error
    }
}


$response = DesignService->internalApi($params);
if $response.error {
   fallBack();
}

function fallBack (){
   //fallbackした処理
    .........
}

 

英語でstand-up meetingは社内で唯一

オンラインデザインチームは国際化が進んでいて、日本人よりもアジア諸国からきたメンバーのほうが多いです。そしてベトナムのチームと一緒に開発しながら、英語でコミュニケーションを取ることを楽しんでいます。自分も外国人として、英語であろうと日本語であろうと母語ではないので、時々難しい感じがするんですが、これまで自信のなかった英語のレベルも時間が経つにつれて上がってきた上に、それぞれの文化に触れて積極的にコミュニケーションを取れることはとても楽しいです。

まとめ

Welcome to join our global team!

Rails Girls Saigon 1st 開催レポート


システムエンジニアの泉田史杏です。
ラクスルは7/20にベトナムホーチミンにて第一回Rails Girls Saigonのスポンサーとして参加しました。

またコンテンツの企画と当日のコーチも務めてきたので、今回はその様子をお伝えします。

ホーチミン初のRails Girls

Rails Girlsは日本含めて世界各国で行われている、女性がプログラミングに親しみ、アイデアを形にするための手助けを目的とするイベントです。ベトナムでは過去にハノイやダナンで行われていますが、ホーチミンでは初の開催となりました。

ラクスルはホーチミンにも開発チームがあるため、今回参加を決めました。

会場はメインオーガナイザーであり、ホーチミンのど真ん中、眺めも非常に素晴らしいTINYpulseさんのオフィス!このブログの写真も提供いただいています、ありがとうございます。

40人弱の参加者に対しコーチは19人。
ベトナム語が話せない、カナダやフランスの参加者も。
でも大丈夫。コンテンツは全て英語で用意しています!
コーディングは、ベトナム語と英語のグループに別れて実施しました。

準備

企画開始は約3ヶ月前(私は2ヶ月前頃から参加)。
Girls達がアプリケーションを楽しいと思える場にしたいという想いと、過去のRails Girlsのフィードバックから、以下二つを特に時間をかけて練りました。

  • ワクワクするアプリケーション
  • 環境構築の時間短縮

ワクワクするアプリケーション

Girls達が、作って楽しい、身近に感じられるウェブサイトってなんだろう?
というところから議論をはじめ、最終的にECサイトに決めました。
これは自分たちでコンテンツを全て準備する必要があるものの、それでもやりたいという多くのコーチの想いの元レビューやリハーサルを重ねながら独自に作っていきました。

仕事終わりにオフィスに集まったリハーサルの風景

開催2週間前のリハーサル。仕事終わりのオフィスにて

環境構築の時間短縮

Windowsにノウハウがあるスタッフがほぼおらず、Railsの開発に集中できなかったということが過去にはあったようです。
そこで今回は思い切って開催日を1日とし、環境はrails serverとrails consoleが、それぞれコマンド一発で立ち上がるDockerを事前にコーチ陣で準備をすることにしました。

結果、一部環境構築できなかったメンバーには、開始前にコーチがセットアップを手伝うことで大きな問題なく、アプリケーション開発に集中しながら進めることができました。

当日コーチ陣によるDockerセットアップサポート

当日コーチ陣によるDockerセットアップサポート

いざ開始!

ワークショップのメインコンテンツは3つ。

  • HTML + CSSを書いてみよう
  • 技術スタックの詰め合わせ”BentoBox”
  • RailsでECサイトの開発

 

HTML + CSSを書いてみよう

まずはWebページ作成の練習!

真っ白のindex.htmlとstyle.cssの二つのファイルから静的なページ作ります。時間は50分。

一緒にコードを見て解説やデバッグしながら、画像サイズが整ったり、バックグラウンドカラーが表示されたり、マウスオーバーで文字の色が変わったりと、だんだんできあがっていくことを楽しみました。

技術スタックの詰め合わせ”Bentobox”

弁当箱のように、技術スタックをフロントエンド、バックエンド、インフラとエリアを分けてカテゴライズしながら、アプリケーションの仕組みを学ぶゲーム。

空の弁当箱に技術スタックを書き込む

空の弁当箱の用紙に技術スタックを書き込んでいきます

私のチームでは、ラクスルの技術スタックを紹介。まず詳細は説明せず、Googleなどで5分で調べてもらいカテゴライズしてもらいました。少し難しいかなという思うものも、必ず誰かは正しくカテゴライズできていました。ラクスルでの具体的な使用例などを交えながら、紹介しました。

この後、ランチを食べていよいよRailsの開発です。

ベトナム料理のオードブル。弁当ではない。

ベトナム料理のオードブル。弁当ではない。

RailsでECサイト開発

いよいよECサイトの開発!3時間の長い時間になります。

解説をしながらも、「erb と html は何が違う?」、「json とは?」、「Gemfileって何?」、「サイトの商品の画像変えたいんだけどどうしたらいい?」など質問は盛り沢山。

独自に自分達で作ったコンテンツだからこそ、「ああ、たしかにこの説明だけだとわかんないよな…」みたいな瞬間もいくつかありました。

ペースはみんな違うのですが、グループメンバー同士でも活発に話し合いながら開発は進みました。最後はHerokuにデプロイをして完了です。

ECサイト完成イメージ

最終完成イメージ

発表〜懇親会

有志で2名から作ったアプリケーションを発表してもらった後、懇親会です。

参加者(左)に記念品を渡すオーガナイザーのNgocさん(右) Cảm ơn nhiều!

参加者(左)に記念品を渡すメインオーガナイザーのNgocさん(右) Cảm ơn nhiều!

様々なバックグラウンドの参加者と触れ合うことができました。例えば、こんな方達。

  • ITを活かせる方法を探している英語の教師
  • フランスからインターンシップと留学で来ていて、スタートアップに興味がある学生
  • 非エンジニアからフルスタックエンジニアを目指してジョブチェンジしようとしている新卒社員
  • カナダからやってきて、ベトナム現地印刷関連会社での営業

また、スポンサーとしてラクスルはサービスの紹介もさせていただき、多くの方に知ってもらうことができました。

不便なことをシステムで解決したいよね、という想いを持っている方は多かったです。

その他GitHubさん、Stack Overflowさんを始め、海外からも多くの会社にサポートしていただけ、素晴らしいイベントとなりました。

ちなみにこのうちわは、ラクスルのノベルティで作成したものでベトナムの開発チームメインで開発したアプリケーションです。

こういった活動も通して、一緒に働く仲間を増やしたり、コミュニティを広げていきたい方、ラクスルで是非一緒に盛り上げていきませんか?

既存プロダクトに最小構成で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+Rails+SQS+S3で社内のオペレーターチェック入稿システムを刷新した話

こんにちは。サーバーサイドエンジニアの荒井です。
ラクスルでデータ入稿〜印刷用データ作成に関わる開発チームに所属しています。

以前、GoのおすすめDIツールについての話がありましたが、今回は、Goを使ってラクスルのオペレーターチェック入稿システムを刷新した話について書きたいと思います。

新システム構成図

新システム構成図

続きを読む

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後から五反田の新オフィスで勤務。新オフィスにもぜひ遊びに来てください!

いよいよRaksul Hack Weekが始まります!

こんにちは。エンジニアマネージャーの石川です。普段は、印刷ECサイトで提供している集客支援(ダイレクトメール、新聞折込、ポスティング等)の開発に携わってます。

前回、ハコベルの加藤よりHack Weekの紹介をさせていただきましたが、いよいよHack Week #2の開催が来週となりました。

Hack Weekは、エンジニア、デザイナ、PM、普段ラクスルの事業を支えているTECHチームが参加するハッカソンイベントです。自分たちでアイディアを出し合い、1週間かけて開発を行います。

本日は、前回の記事以降に行われたチームビルディングイベントの様子をご紹介したいと思います!

チームビルディングイベントを開催しました

Hack Week開催まで3週間を切った5/22、Hack Week pre-eventと称して、チームビルディングを目的としたイベントを開催しました。

前回の記事では、 Hack Week Lunch を実施の様子をご紹介しましたが、今回はチームビルディングイベントということもあり、美味しい食事とお酒を楽しみながら、自由な雰囲気でコミュニケーションを取りやすいイベントを目指しました。

まずはIceBreak

Hack Weekでは、普段は違うチームで開発をしているメンバーと一緒に開発ができる良いチャンスでもあります。

今回のイベントでは、普段のチーム以外のメンバーともぜひ交流を深めてほしい。

そこで、冒頭の時間をIceBreakタイムとして簡単なゲームを実施しました。

まずは隣にいる人と話すきっかけを持ってもらうこと、声を出して話して見ることで、緊張をほぐしてもらうことが目的です。

2人ペアを組んでもらい、簡単なゲームを通して15分ほど会話を楽しんでもらいました。

会場の雰囲気も和んできたところで…ラクスルビールで乾杯をしてイベントスタートです!

チームビルディングタイム

「こういう面白いことやろうと思ってるんだけど、フロントやってくれませんか?」

と、熱いプレゼンをするメンバー

「まだ何をやるか決めてないんですが、何やるか決めました?」

と、すこし不安な思いを抱えながらの参加となったメンバー

それぞれの想いが入り混じりながら、チームビルディングタイムがスタートしました。

それぞれの想いを胸に、イベントスタートです!

最初はみんな手探りでしたが、それぞれが持ち寄ったアイディアを語り合いながら食事とお酒を楽しみ、それぞれのテーブルで様々な議論が交わされました。

身振り手振りを交えて熱い議論を交わします。

チームビルディングイベントの結果は?

イベント開催をきっかけに、16のチームが結成されました!

チームビルディングイベント中に結成されたチームも合ったようですが、イベントの翌日以降に改めて話して結成したチームも多く見られたようです。

チームビルディングイベントに参加してみて

私自身も、ラクスルに入社して4ヶ月。

社内にまだまだ知り合いが少ないので少し不安な気持ちでの参加でしたが、自己紹介から始まり、お互いの事業の紹介をしたり、温めていたアイディアの話をしたり、Hack Weekに対する不安が解消され「いよいよ始まるんだな!」と期待が膨らむイベントになりました。

いよいよRaksul Hack Weekが始まります!

いよいよ来週からRaksul Hack Weekが始まります。

それぞれの16チームのテーマも出揃いました。

テーマは、普段関わっている事業に関するもの、最新の技術を使ったもの、様々です。

次回は、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が役に立つ・解決手段となるケースもあるのではないかと思います。そのようなときにこの記事が参考になれば幸いです。