Zeals TECH BLOG

チャットボットでネットにおもてなし革命を起こす、チャットコマース『Zeals』を開発する株式会社Zealsの技術やエンジニア文化について発信します。

第3回、Zeals開発合宿に行ってきました!@湯河原

f:id:zeals_kody:20191121213718j:plain

どうも!味わい深いエンジニア kody です!Zealsでは第3回となる開発合宿に行ってきました!

Zealsでは、過去に2度の開発合宿を行っております。

tech.zeals.co.jp

tech.zeals.co.jp

3度目の実施となった今回の合宿はこれまでと異なり、Zealsの社員メンバーだけでなく、普段お世話になっている業務委託や社外の方なども巻き込んで(任意参加)参加してもらう、新しいスタイルで合宿を企画してみました。

このような企画にした意図として、普段リモートのみで稼働してる方や、Slackではよく見かけるけど(出社の頻度が少なく)実際に話したことがない方も混ぜて行うことで、より有意義なコミュニケーションが生まれ結果チーム力が上がるとのではないか、という考えがありました。

それでは、当日の模様をお伝えして参ります。

  • 合宿のしおり(スケジュール)
    • 合宿の会場
  • 開幕!
    • テーマ一覧
  • 開発中の様子
    • 1日目
    • 夜ご飯
    • 2日目
    • 最終プレゼンテーション
  • 反省
    • よかったこと
    • 改善したいこと
  • さいごに

合宿のしおり(スケジュール)

実際の合宿は以下のようなスケジュールで進みました。特に変わったことをするわけではなく、ひたすらに集中して黙々と作業する時間が続きます。笑

f:id:zeals_kody:20191121203418p:plain

合宿の会場

今回お世話になったのは、開発合宿の宿といえば...でおなじみ「おんやど恵」さん。 開発合宿用にプランが用意されており、設備も開発用に整えられているので特に準備せずに気軽に合宿を始められるのでおすすめです!

以下のリンクから予約できるので、開発合宿の開催を検討中の方はぜひ。

www.onyadomegumi.co.jp

(※人数次第では早めに予約しないと取れない可能性があるのでご注意ください)

開幕!

f:id:zeals_kody:20191121212206j:plain

今回の合宿では、各自で自由にテーマを決めて開発を進めていく形を取りました。

参加者の開発テーマ一覧はこちら!機械学習からインフラまで様々なテーマに取り組みました!

テーマ一覧

- Quattro Luncher(”Quattro Lunch”という、社内のシャッフルランチをいい感じに仕分けるアプリケーション)
- PWAを使ったwebサービスの開発
- TwitterAPIとJavaScript(Node.jsやReac)を用いたアプリケーション開発
- Golangの勉強(とりあえずTour of Go)
- CLI化ツールの習得&コードスコア自動スクレイピングのCLIツール化
- KPI管理シート自動更新スクリプト開発
- CI/CDを修正してのDXの向上
- k8sを使ったイベント駆動マイクロサービス作成
- automlでCV予測
- 自然言語処理を用いたbot開発
- React Native with Expo で スマホアプリ作成
- Face detection
- Golang × LINE bot
- Prophetを用いたデータ量増加予測
- Pythonコードに Annotation を追加

開発中の様子

1日目

スタート直後の様子です。諸事情あり、遅れて来る方もいたので、開始時点では10名程度のメンバーが集まっていました。 f:id:zeals_kody:20191121212421j:plain

開発の息抜きに、近くに公園があるので体も動かせます。(これで運動不足も解消👍)

f:id:zeals_kody:20191121212709j:plain

なんと、地方にいるインターン生も遅れて参加してくれました!!(写真左) f:id:zeals_kody:20191121212825j:plain

15時を過ぎ、部屋のチェックイン可能になったので各々好きなところで自由に開発します。

部屋でゴロゴロする人やリラクゼーションスペースを活用する人も・・・ f:id:zeals_kody:20191121222857j:plain

開発できる場所が会議室だけではないので、気分転換できて良いですね。 ほぼ全員集合できたのが、 18:00 を過ぎたあたりでした。

夜ご飯

f:id:zeals_kody:20191121212937j:plain

さて、お楽しみの夜ご飯(お酒)の時間です。笑

夜ご飯(お酒)をほどほどに楽しんだ後、各々自由に過ごします。

f:id:zeals_kody:20191121213025j:plain

夜ご飯の後も黙々と開発する人が予想より多くいて感激しました。 メンバーによっては、かなり夜遅くまで開発をしていた人もいましたね。 f:id:zeals_kody:20191121213222j:plain

2日目

あっという間に、2日目の朝です。

旅館の朝ごはん、量が多くてもなぜだか食べれちゃいますよね(?)。

f:id:zeals_kody:20191121213301j:plain

僕はコーヒー片手に、一人寂しく朝のお散歩を。 自然が隣にある環境は控えめにいって最高でした。

f:id:zeals_kody:20191121222454j:plain

最終プレゼンテーション

15時からは最終プレゼンテーションでした。 各々がこれまで開発した内容を、まとめて発表してもらいました!

f:id:zeals_kody:20191121213559j:plain

各自が熱の入ったプレゼンテーションを実施してくれたので、盛り上がりました!

また、普段業務で使わない技術を使うメンバーも多かったので、知らない・使ったことのない技術について知る良いきっかけとなりました。

反省

よかったこと

  • 普段関わりの少ないメンバーを巻き込むことで、チームの一体感が増した
  • 技術力の高い業務委託のメンバーからの学びがあった
  • 様々な技術領域での発表があったので、ノウハウ共有の機会となった

改善したいこと

  • 人数が増えてきたので、最終発表の時間が伸びてしまう
    • チーム開発にする等の工夫をしていきたい
  • 場所やメンバーによっては、wi-fi の速度が遅くなることがあった

さいごに

開発合宿も3回目の開催となりましたが、改めて技術への投資としてサポートをしてくれるエンジニア以外のメンバーに感謝ですね。今回の合宿で得た経験をプロダクト開発に活かして、さらに事業を加速させていきたいと思いました。

1泊2日で普段とは違った場所で開発に集中できることで良い成長に機会になったのと、普段業務だと関わらないメンバーと顔合わせて時間を過ごすことで結束力も高まったと思います。

ということで、Zealsでは文字通り老若男女様々な個性のあるメンバーが集まり、チーム一丸となって開発していることがご理解いただけたかと思います(伝わってなければすいません)。一緒に日本をぶち上げたい、熱い思いを持っているエンジニアの方お待ちしておりマウス

hrmos.co

続きを読む

Redash 分析環境のGKE移設&ver.3から7へのアップデート手順を公開

f:id:zeals-engineer:20191114231808p:plain

こんにちは、分析基盤を担当している鍵本です。
本日は Redash サーバを GKE に移設して序にバージョンアップまでしちゃいました というお話をしようと思います。

  • 背景
  • 移設時のポイント
    • GCP リソース作成のコード化
    • バージョン管理をしやすくするための GKE 化
    • SSL化
  • 移設作業の詳細
    • GCP リソース作成
    • GKE 環境への Redash のデプロイ
      • マニフェストファイルの作成
      • デプロイ
    • データのリストア
      • 現行サーバのデータ取得
      • ローカルの Docker 環境での起動
      • Docker 環境へのリストア
      • バージョン 4 へのアップグレード
      • バージョン 5 へのアップグレード
      • バージョン 7 へのアップグレード
      • 本番環境へのデータ移行
  • 移行後に発覚した問題
    • 日本語での検索
    • クエリの進捗状況
  • まとめ
  • 最後に

背景

Zealsでは、ユーザーの行動ログなどに紐付いた日々のKPIを確認するために Redash を、BizDevのメンバー中心に利用しております。
弊社サービス『Zeals』 は元々Microsoft Azure 上で運用していたため、Redashも当然ながらAzure上に仮想マシンを立てて構築していました。導入は1年半ほど前のことで、バージョンはなんと 3 でした。とても古いですね。

tech.zeals.co.jp

その後ZealsをGCPに移行したことから、「Redash も移行しよう」「序でだからバージョンアップもしよう」という話になりました。
実際の移行作業は2019年10月11日に完了しております。このとき安定版最新バージョンが 7 だったので、一気に 4 ランクアップさせました。

github.com

移設時のポイント

今回の移設作業で注目すべき点は以下の通りです。

  • GCP リソース作成のコード化
  • バージョン管理をしやすくするための GKE 化
  • SSL化

GCP リソース作成のコード化

VPCネットワーク、静的外部IPアドレス、GKEクラスタといった GCP のリソースは一度作った後に触る機会はさほど多くありませんが、冪等性を担保するためにそれらをコード管理するようにしました。

qiita.com

バージョン管理をしやすくするための GKE 化

Redash バージョン4 まではオンプレのサーバーか仮想マシンにインストールすることを想定したスクリプトが用意されていましたが、その後は Docker 上に構築することを前提としたスクリプトに置き換わりましたので、GKE に構築するのが適切ではないかと考えました。

SSL化

Zealsでは、これまでHTTP通信で接続しておりました。KPIに利用するデータの内容はマスキングされたものを利用しているとはいえ、この状態は非常によくありません。そのため、今回のバージョンアップから HTTPS のみのアクセスに変更しております。

移設作業の詳細

GCP リソース作成

以下のリソースを構成管理ツール Pulumi を使って行いました。

  • VPCネットワーク
  • サブネット
  • 静的外部IPアドレス
  • GKEクラスタ
  • DNS (Redash 用の A レコード登録のみ)
  • Cloud Armor

Pulumi のコードについては別の機会にご紹介しようと思います。

www.pulumi.com

GKE 環境への Redash のデプロイ

Redash バージョン 7 の環境を以下の手順に従って GKE に構築します。

マニフェストファイルの作成

スクラッチからマニフェストファイルを作成するのは大変なので、 kompose というツールを使って docker-compose.yml から作成しました。

github.com

バージョン 7 の場合には setup/docker-compose.yml がありますので、まずはこれを用いてローカルのDocker 環境で起動できるように修正しておきます。次にこの docker-conmpose.yml を作業ディレクトリにコピーし、

kompose convert

を実行すると、必要なマニフェストファイルが生成されます。

これをもとにして必要な修正を加えることで、マニフェストファイルの作成コストを最小限にしています。主な修正箇所は以下の通りです。

  • 環境変数を ConfigMap に移動
  • 必要なリソースの定義
  • nginx 用のマニフェストの削除
  • Ingress の追加
  • ManagedCertificate の追加
  • cloud_sql_proxy をサイドカーとして起動する設定を追加
  • cloud_sql_proxy 用 Secret の追加

たとえば IngressManagedCertificate のマニフェストを紹介すると以下のようになります。

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: ingress
  namespace: analytics
  annotations:
    kubernetes.io/ingress.global-static-ip-name: [LB用静的外部IP名]
    networking.gke.io/managed-certificates: [ManagedCertificate名]
spec:
  rules:
  - host: [Redash用URL]
    http:
      paths:
      - backend:
          serviceName: [Redashサービス名]
          servicePort: 5000
apiVersion: networking.gke.io/v1beta1
kind: ManagedCertificate
metadata:
  name: [ManagedCertificate名]
spec:
  domains:
  - [Redash用URL]

tech.zeals.co.jp

デプロイ

cloud_sql_proxy 用 Secret ファイルの data には Cloud KMS を利用して暗号化された credential 情報が入っています。このままでは Secret として登録できないので、デプロイ前に復号化しておきます。

kubesec decrypt -i redash/secrets/cloudsql-client-service-account.yaml

あとはマニフェストを適用するだけとなります。

kustomize build redash/overlays/prd | kubectl apply -f -

ここで redash/overlays/prd には production 環境用の kustomization.yaml が配置されており、その中の resources に必要なマニフェストファイルが読み込まれるよう定義されております。

データのリストア

Redash ではバージョンアップをした時にデータを整合的にするためのツールとして manage コマンドを用意してくれています。しかし残念ながらバージョン 3 から 7 へは一気に変換することができませんでした。冷静に考えたら無茶苦茶な話ですよね。そこで以下のように順を追ってやっていくことになりました。

現行サーバのデータ取得

pg_dump コマンドを使って Redash データベースのダンプを取ります。

sudo -u postgres pg_dump redash | gzip -c > redash.dump.gz

ローカルの Docker 環境での起動

ローカルの Docker 環境にバージョン3の環境を作ります。

mkdir -p ~/workspace
cd workspace
git clone -b v3.0.0 https://github.com/getredash/redash.git
cd redash
vi docker-compose.production.yml
diff --git a/docker-compose.production.yml b/docker-compose.production.yml
index f0b9812d..f1b746cd 100644
--- a/docker-compose.production.yml
+++ b/docker-compose.production.yml
@@ -7,7 +7,7 @@
 version: '2'
 services:
   server:
-    image: redash/redash:latest
+    image: redash/redash:3.0.0.b3147
     command: server
     depends_on:
       - postgres
@@ -23,7 +23,7 @@ services:
       REDASH_WEB_WORKERS: 4
     restart: always
   worker:
-    image: redash/redash:latest
+    image: redash/redash:3.0.0.b3147
     command: scheduler
     environment:
       PYTHONUNBUFFERED: 0
@@ -38,8 +38,8 @@ services:
     restart: always
   postgres:
     image: postgres:9.5.6-alpine
-    # volumes:
-    #   - /opt/postgres-data:/var/lib/postgresql/data
+    volumes:
+      - /home/kagimoto/workspace/postgres-data:/var/lib/postgresql/data
     restart: always
   nginx:
     image: redash/nginx:latest
mkdir -p /home/kagimoto/workspace/postgres-data
docker-compose -f docker-compose.production.yml run --rm server create_db
docker-compose -f docker-compose.production.yml up -d

http://localhost/ にアクセスして、Redashのログイン画面が出ることを確認します。

Docker 環境へのリストア

既存のスキーマを一旦削除します。

docker exec -i redash_postgres_1 psql -U postgres -c 'drop schema public cascade; create schema public' 

次に Redash データベースを作成します。

docker exec -it redash_postgres_1 bash
su - postgres
createuser redash --no-superuser --no-createdb --no-createrole
createdb redash --owner=redash
exit
exit

先程取得したデータをリストアします。

zcat ~/tmp/redash.dump.gz | docker exec -i redash_postgres_1 psql -U postgres redash

docker-compose.yml を編集して、現行に合った環境変数を設定し再起動します。

docker stop $(docker ps -q)
docker-compose -f docker-compose.production.yml up -d

RedashのURL を /etc/hosts に 127.0.0.1 として登録し、http://[RedashのURL]/ にアクセスして現行と同じものが表示されることを確認します。

バージョン 4 へのアップグレード

まずローカルリポジトリをアップグレードします。

docker stop $(docker ps -q)
git checkout -b v4.0.0 refs/tags/v4.0.0
vi docker-compose.production.yml

イメージのバージョンを4に変更します(差分はオリジナルからのもので、上記からの追加差分ではありませんのでご注意ください)。

diff --git a/docker-compose.production.yml b/docker-compose.production.yml
index f0b9812d..8358b5f4 100644
--- a/docker-compose.production.yml
+++ b/docker-compose.production.yml
@@ -7,7 +7,7 @@
 version: '2'
 services:
   server:
-    image: redash/redash:latest
+    image: redash/redash:4.0.1.b4038
     command: server
     depends_on:
       - postgres

manage コマンドでデータベースの修正をしてコンテナを起動します。

docker-compose -f docker-compose.production.yml run --rm server manage db upgrade
docker-compose -f docker-compose.production.yml up -d

http://[RedashのURL]/ にアクセスして画面下部に Redash 4.0.1+b4038 と表示され、バージョンが上がっていることを確認します。

バージョン 5 へのアップグレード

同様にローカルリポジトリをアップグレードします。

docker stop $(docker ps -q)
git checkout -b v5.0.0 refs/tags/v5.0.0
vi docker-compose.production.yml

イメージのバージョンを 5 に変更します(差分はオリジナルからのもので、上記からの追加差分ではありませんのでご注意ください)。

diff --git a/docker-compose.production.yml b/docker-compose.production.yml
index f0b9812d..8358b5f4 100644
--- a/docker-compose.production.yml
+++ b/docker-compose.production.yml
@@ -7,7 +7,7 @@
 version: '2'
 services:
   server:
-    image: redash/redash:latest
+    image: redash/redash:5.0.0.b4754
     command: server
     depends_on:
       - postgres

manage コマンドでデータベースの修正をしてコンテナを起動します。

docker-compose -f docker-compose.production.yml run --rm server manage db upgrade
docker-compose -f docker-compose.production.yml up -d

http://[RedashのURL]/ にアクセスして画面下部に Redash 5.0.0+b4754 と表示されていることを確認します。

バージョン 7 へのアップグレード

docker-compose.production.yml を退避して、ローカルレポジトリをアップグレードします。

cp docker-compose.production.yml ../
git checkout docker-compose.production.yml
docker stop $(docker ps -q)
git checkout -b master remotes/origin/master
vi setup/docker-compose.yml

※ 現在は master がバージョン 8 になっているので、v7.0.0 のタグを checkout する必要があります。

diff --git a/setup/docker-compose.yml b/setup/docker-compose.yml
index aea6369b..832b5e45 100644
--- a/setup/docker-compose.yml
+++ b/setup/docker-compose.yml
@@ -4,7 +4,7 @@ x-redash-service: &redash-service
   depends_on:
     - postgres
     - redis
-  env_file: /opt/redash/env
+  env_file: /home/kagimoto/workspace/redash/env
   restart: always
 services:
   server:
@@ -36,10 +36,10 @@ services:
     image: redis:5.0-alpine
     restart: always
   postgres:
-    image: postgres:9.5-alpine
-    env_file: /opt/redash/env
+    image: postgres:9.5.6-alpine
+    env_file: /home/kagimoto/workspace/redash/env
     volumes:
-      - /opt/redash/postgres-data:/var/lib/postgresql/data
+      - /home/kagimoto/workspace/postgres-data:/var/lib/postgresql/data
     restart: always
   nginx:
     image: redash/nginx:latest

env ファイルに必要な環境変数を全て定義しておきます。その後 manage コマンドでデータベースの修正をしてコンテナを起動します。

docker-compose -f setup/docker-compose.yml run --rm server manage db upgrade
docker-compose -f setup/docker-compose.yml up -d

http://[RedashのURL]/ にアクセスして右上のアカウント名をクリックして、メニューの一番下に Version: 7.0.0+b18042 と表示されていることを確認します。

本番環境へのデータ移行

ローカル環境の PostgreSQL サーバから Redash データベースのダンプを取得し、一部内容を修正します。

docker exec -i setup_postgres_1 pg_dump -U postgres redash > redash.dump
sed -e s/redash$/postgres/ -e 's/redash;$/postgres;/' < redash.dump > redash_v7.dump

これは Docker 環境を前提とするバージョン 5 からは、postgres データベースに Redash 用のテーブルを作成し、それに対して postgres ユーザでアクセスするように変更されたからです。上記はダンプデータに含まれているテーブルの所有者情報等を sed で書き換えているというわけです。

本番にリストアします。

kubectl exec -it --namespace=analytics $(basename $(kubectl get pod -o name -n analytics -l 'io.kompose.service==postgres')) -- psql -U postgres -c 'drop schema public cascade; create schema public'
cat redash_v7.dump | kubectl exec -it --namespace=analytics $(basename $(kubectl get pod -o name -n analytics -l 'io.kompose.service==postgres')) -- psql -U postgres postgres

/etc/hosts に登録した RedashのURL を削除し、 http://[RedashのURL]/ にアクセスして、先程ローカル環境で確認した状態と同じものが見えることを確認します。

移行後に発覚した問題

日本語での検索

バージョン 7 に移行してみたところ、検索窓でクエリやダッシュボードを日本語で検索しても想定した検索結果が得られないことがわかりました。これはバージョン 8 で修正されたようです。この記事を執筆中に正式リリースされたようなので、すぐにアップデートしようと思います。

クエリの進捗状況

バージョン 3 の場合には、誰の実行した adhoc クエリが実行中で、誰のが待ち状態なのかが管理画面からわかったのですが、どのバージョンからかはわかりませんが、そのような情報が得られなくなったようで管理者としては不便な感じを受けております。もしかしたら何か手立てがあるのかもしれません。ご存じの方がいらっしゃったら教えて下さい...!!

まとめ

今回は Redash を GKE 環境に移行し、バージョンアップしたというお話を紹介しました。定期的にバージョンアップをしていかないと、あとで苦労する ことを思い知らされた感じがします。でも GKE に移行したので、今後は気軽にバージョンアップできるんじゃないかと(勝手に)期待しています。

続きを読む

gRPC(protobuf)をモノリシックなRailsアプリケーションに導入する

f:id:zeals-engineer:20191112233421p:plain

こんにちは!
Railsエンジニアをやっているtakakudaです。

今回のエントリーでは、Zeals(旧fanp)サービスへのgRPCを導入を紹介できればと思います。
目次は以下のようになります。

  • そもそもgRPCとは
  • 導入経緯
  • 導入
    • proto fileにデータ構造を定義する
    • protocコマンドを用いてRubyコードを生成する
    • 生成されたRubyコードをlib配下に置き、config/initializers配下でStubを作成する
    • 共通化したいメソッド呼び出し部分をprotobufで生成されたメソッドに書き換える
  • Railsへ導入時にハマったこと
  • gRPCを導入してみて
  • 最後に

そもそもgRPCとは

gRPCはRPCを実現するためにGoogleが開発したプロトコルの1つで、インターフェイス定義言語のもとになるメッセージ交換形式としてProtocol Buffersを利用できます。gRPC上のアプリケーションでは、別マシン上にあるアプリケーションのメソッドをローカルオブジェクトのように直接呼び出すことができ、分散アプリケーションおよびサービスの作成を簡単にできます。

f:id:takakudakei:20191108193526p:plain

https://www.grpc.io/docs/guides/

導入経緯

簡単にZealsの機能紹介をすると、サービスは大きく分けて以下の3つから構成されています。

[1] チャットボットの管理用Webアプリケーション

[2] チャットボットへのメッセージ送受信サービス

[3] 1と2から共通処理を切り出したマイクロサービス群

もっと詳しく知りたい方は、以前公開しました以下の記事をご覧ください。

tech.zeals.co.jp

今回は、上記の構成の中でも[3]にあたる、「チャットボット管理用のWebアプリケーション」と「チャットボットへのメッセージ送受信サービス」で共通して発生する処理をAPIとして切り出すために利用したgRPCをテーマとしています。

2つのサービスで共通する処理をサービスごとにRuby, Pythonと異なる開発言語で実装してるため、

  • 機能追加や修正の際に、それぞれのアプリに同じ実装を行わなければならない
  • 挙動に差異があった場合、その差異がどちらの実装のバグによるものかを特定するのに時間がかかる

といった問題が発生しており、サービス全体見た場合のメンテナンスコストが高くなってきたため、共通処理部分をGoのAPIとして切り出すことを決定しました。

導入

以下では、実際にZealsで使用している「メッセージの配信対象となるユーザーをフィルタリングする処理」を例に解説していきます。

またprotobufによって生成されたコードについてですが、今回はRailsに導入する部分について紹介したいのでサーバー側の実装については触れず、クライアント側のコードについて紹介させていただきます。
導入手順としては以下のようになります。

  1. proto fileにデータ構造を定義する
  2. protocコマンドを用いてRubyコードを生成する
  3. 生成されたRubyコードをlib配下に置き、config/initializers配下でStubを作成する
  4. 共通化したいメソッド呼び出し部分をprotobufで生成されたメソッドに書き換える

proto fileにデータ構造を定義する

まずは.protoという拡張子のファイルで、

  • Service:RPCサービスのインターフェイスを定義することができる 
  • Message:メッセージのフォーマットを定義することができる

をそれぞれ書いていきます。

/proto/cosmos.proto

syntax = "proto3";


import "entities/common.proto";
import "entities/entities.proto";
import "entities/message.proto";

// CosmosService is microservices composed of a suite of small and lightweight services.
service CosmosService {
  // Filter filters a list of end users related with a given chatbot ID by using a given filter ID.
  // End users is filtered by the following conditions.
  // 1. Whether the attribute ID of the end user matches filter's one.
  // 2. Whether inflow date and time of the end user matches filter's one.
  // 3. Whether the chatbot has permission to send a message to the end user.
  rpc Filter(FilterRequest) returns (stream e.EndUser) {}
}

// FilterRequest
message FilterRequest {
  // The ID of the chatbot that will be used to collect end users related with it. And then the set of end users will be filtered.
  int64 chatbot_id = 1;
  // The ID of the filter to be used to filter a set of end users.
  int64 filter_id = 2;
}

syntaxは2019年11月現時点で主流versionである3を指定します。

簡単に説明するとCosmosServiceはFilterRequestという型を受け取ると、FilterRequestの条件によってフィルタリングされたユーザーをresponseとして返すということを定義しています。

developers.google.com

protocコマンドを用いてRubyコードを生成する

gem install grpc-tools

Protocol Buffersの定義から自動でコードを生成するのに必要なツールをinstallします。

https://rubygems.org/gems/grpc-tools/versions/1.25.0

grpc_tools_ruby_protoc -I /proto \--ruby_out=lib --grpc_out=lib ../proto/cosmos.proto

コマンドを実行することでProtocol Buffersの定義をもとにRubyコードが生成されます。

https://grpc.io/docs/tutorials/basic/ruby/

生成されたRubyコードをlib配下に置き、config/initializers配下でStubを作成する

自動生成されたコードはlib配下に置きます。

Protocol Buffersから生成されたコードを実行するためにはStubを作成する必要があるので、 config/initializer にStubを作成する設定fileを作成します。

stub = CosmosService::Service.rpc_stub_class.new(localhost:38080, :this_channel_is_insecure)

共通化したいメソッド呼び出し部分をprotobufで生成されたメソッドに書き換える

生成されたコードを、Rubyで実装しているチャットボット管理画面のコードへ組み込みます。

# requestを用意して
req = FilterRequest.new(chatbot_id: chatbot_id, filter_id: filter_id)
# Stubを使ってサーバー側のメソッドを呼び出します
end_users = stub.filter(req)
# ストリーミングで返ってくるので1つずつ取り出し、end_userのidの配列として返します
end_users.map(&:id)

Railsへ導入時にハマったこと

Rails側での実装を終え、いざステージング環境にて動作確認します。 動作確認の際に、gRPCのdependencyであるgoogle-protobufをビルド済み共有ライブラリとしてインストールすると、正常に動作しないという問題にぶつかりました。

その際の環境は、以下の通りです。

  • ruby:2.5.5-alpine
  • grpc (1.23.0)

ここが僕が大きくハマったポイントです…。
Zealsではkubernetesを本番環境で運用しており、その際に利用するDockerのimageとしてAlpine Linuxを使用していました。

この問題を調査すると、どうやらalpineのimageでprotobufを利用しようとすると発生している問題らしく、本家のprotocolbuffers/protobufでもissueが上がっていました。Alpine LinuxだとRubyライブラリに不足があり、ld-linux-x86-64.soがないというエラーです。

github.com

対応としては上記のissueで議論されている内容を参考にしました。

具体的には、gRPCのdependencyであるgoogle-protobuf を BUNDLE_FORCE_RUBY_PLATFORM=1 でソースからビルドするworkaroundを取るように、以下のようにDockerfileを編集しました。

mv Gemfile Gemfile.orig && \
sed -e /grpc/d Gemfile.orig > Gemfile && \
bundle install && \
cp Gemfile.orig Gemfile && \
BUNDLE_FORCE_RUBY_PLATFORM=1 bundle install

やっていることは、最初のbundle installではgRPCを省いた状態で実行し、2回目のbundle install時にgRPCのみで実行できるようにしています。 一見二度手間のようなことをやっていますが、目的はbundle installでかかる時間を少しでも短くするためです。

gRPCを導入してみて

  • 機能追加や修正の際に、それぞれのアプリに同じ実装を行わなければならない
  • 挙動に差異があった場合、その差異がどちらの実装のバグによるものかを特定するのに時間がかかる

この問題が解決し、今では修正もデバックも容易になりました。

現在は上記のイチ機能しかまだ切り出せていないのですが、これを機に巨大なモノリシックになりつつあるRailsアプリを切り出していき、メンテナンス性や拡張性をマネジメントしやすいプロダクトに成長させていきます。

続きを読む

Kubernetes Meetup Tokyo#24で「ZealsでのCI/CDパイプライン構築事例」について登壇しました!

https://connpass-tokyo.s3.amazonaws.com/thumbs/16/f1/16f13ae16413921089f49e9bb5087352.png

こんにちは!
普段はGolangでMicroservicesを開発しているぱんでぃーです!

今回のエントリーでは『Kubernetes Meetup Tokyo #24』でLT枠として登壇してきたレポートをお届けします!

k8sjp.connpass.com

続きを読む

Rails環境でセキュリティ向上のため、Brakeman gemを導入&脆弱性対策を実施しました

Brakemanのロゴ

こんにちは、ZealsでバックエンドエンジニアとしてRailsを使って開発をしている鈴木です。
Zealsでは、セキュリティ対策の一環としてBrakemanを使用しております。

今回はなぜ、Brakemanを導入することに至ったかの理由とBrakemanの導入、実際に活用してうまくいったノウハウを紹介させていただきます。
Railsのセキュリティ対策について調べている方や、Brakemanをどのように活用していくか調べている方の参考になれば幸いです。

  • そもそもBrakemanとは
  • Brakemanを導入した経緯
  • Brakeman活用までの流れ
    • 1.Brakemanの実行結果をエクスポートし、脆弱性の調査および相談
    • 2.脆弱性についての調査
    • 3.脆弱性の対応についてチームで方針を決定
    • 4 話した優先度や対応方針に従って修正
  • 今後やっていきたいこと
  • さいごに

そもそもBrakemanとは

Brakemanとはソースコードに、SQLインジェクションなどの脆弱性がないかを解析してくれるライブラリです。 SQLインジェクションなどの有名な脆弱性だけでなく、解析してくれる項目が20種類以上もあります。

github.com

これだけ解析をしてくれるため、URLを設定する際に危険なサイトのURLを登録できてしまう脆弱性に気づくことができました。現在のサービスの運営上では問題になっていませんが、将来的に大きなインシデントとなる可能性があります。

もちろん、 コードレビューだけでは気づけない箇所も指摘してくれますので、セキュリティに強いアプリケーションを開発していく上では必須のツールです。

実行についても、以下のようにコマンド一発で解析してくれるため非常に簡単です。

brakeman

実行すると、以下のように解析した結果を表示してくれます。

もちろん、Brakemanは他のGemと同様に簡単にインストールできます。
グローバルにGemをインストールする場合は、以下のコマンドを実行してください。

gem install brakeman

Gemfile にを用いる場合は以下のように記述してください。

group :development do
  gem 'brakeman', :require => false
end

Brakemanを導入した経緯

Zealsはクライアントである企業様の大切な情報を扱うアプリケーションです。 そのため、セキュリティ対策を万全にする必要があります。

今後より多くの機能を追加し事業を成長させる上で、セキュリティ強化は重要なテーマとなります。 そうなった場合に、数万行のコードをエンジニアがすべて解析することは、現実的ではありません。

そういった脆弱性の診断を効率的にするために、今回Brakemanを導入しました。

Brakeman活用までの流れ

1.Brakemanの実行結果をエクスポートし、脆弱性の調査および相談

最初のフローとして、Brakemanを用いた脆弱性の調査、相談をしていきます。

実行結果を見るために、その都度コマンドラインで実行するしていくのは非効率的です。
Brakemanには解析した結果を、HTMLなどにエクスポートしてくれる便利機能があります。

解析結果をエクスポートするためには、以下のコマンドを実行するだけOKです。

brakeman -o brakeman.html

Zealsでは、エクスポートされたHTMLを資料として、脆弱性の調査や相談を社内外のエンジニアと共同で行いました。

脆弱性を調査した箇所の一例としては、冒頭に説明した「URLを設定するところで危険なサイトのURLを登録できてしまう」という脆弱性です。こちらに関しては、コードレビューの時点では気づくことができませんでした。

詳細な内容としては、「アプリケーションの公開範囲が将来toC向けに広がった場合、不正なデータを登録される可能性がある」というものです。

エクスポートしたHTMLをブラウザで開くと、このような画面が表示されます。

ここでの指摘内容の例として、以下のようにBrakemanが指摘してくれています。

  • RemoteFollowControllerのcreateメソッドで、ユーザーが入力した値をもとにリダイレクトする可能性がある
  • そのため、オープンリダイレクトにつながってしまう可能性がある

tooljp.com

2.脆弱性についての調査

Brakemanは、現在は脆弱性でなくとも、将来的に脆弱性となりえる箇所まで指摘してくれます。

脆弱性と指摘されたコードについても、何かの理由があってそのような実装になっている場合もありますので、その背景も調査してドキュメントに残しておくと、後から振り返ることができ非常に便利です。

ドキュメントがあることで第三者への脆弱性報告の資料として使えたり、実装方針について相談する資料にもなり、とても役に立ちました。

3.脆弱性の対応についてチームで方針を決定

調査した脆弱性ごとに対応方法を選択し、修正していく方針となりました。

しかし、すぐに修正可能な方法を採用すると、処理に時間がかかりサービスが使いづらくなる脆弱性対策が存在します。 そのため、「脆弱性対策として処理時間をどれぐらいかけて良いか」をもとに対応方法を考え、チーム内で相談と調査をしていきました。

先に調査を行ったことで、脆弱性対策によって処理が遅くなることが事前にわかり、実装の手戻りを防いだり、工数の見積を正確にした上で対策を進めることができたという点が非常に良かったですね。

4 話した優先度や対応方針に従って修正

チーム内で決定した優先度や対応方針に従って、プロダクトコードに修正を加えます。

Brakemanは、アプリケーション内でのユーザー入力の操作内容をもとに脆弱性を判断しているため、問題ない仕様でも指摘される場合があります。
そのような場合は、Brakemanの指摘を無視するコマンドを実行します。

以下のコマンドを実行すると、警告を無視する設定が可能です。

brakeman -I 

上記のコマンドを実行すると、以下の実行画面が表示されます。

最初に、警告を無視するignore設定をするファイル名を何にするか質問されます。

特にこだわりがなければ、デフォルトのままで問題ないのでEnterを押します。
次に、どの対話形式で進めていくかを確認されますが、最初は全件対応で問題ないので1 を入力してください。

さらに、挙げられた脆弱性ごとに、内容を無視するかを確認されます。
主に実行するコマンドは以下のコマンドです。

  • n :ignoreをする際、ノートにignoreする理由を書くことが可能です
    • iでもignore可能ですが、あとから見返すとignoreした理由が不明になるため、nの使用を推奨します
  • s:何もせずにスキップします
  • u:ignoreを取り消します

今後やっていきたいこと

CIを使ってBrakemanを実行していきます(Zealsでは、CIにCircle CIを利用しています)。

当然ですが、一度Brakemanの対応が終わった後にも、新しい脆弱性が生まれる可能性があります。

新しい脆弱性が生まれないようにするため、CIツールでBrakemanを実行し継続的に脆弱性を確認したほうが良いでしょう。
Zealsでは今後、初動の脆弱性対応の完了後、 CircleCIを用いたBrakeman運用をしていく予定です。

circleci.com

続きを読む

Gotanda.js#13をfreeeさんのオフィスで開催&登壇しました!

f:id:zeals-engineer:20191006001711j:plain

みなさんこんにちは!

10月4日に五反田の地域JavaScriptコミュニティGotanda.js』の第13回イベントが行われたのですが、Zealsエンジニアのぱんでぃーさんこと濱田が登壇したので、そちらの模様をお届けいたします。

Gotanda.jsはテックブログで今までに何度かイベントレポートを書いておりまして、前回はZealsオフィスで開催させていただきました。

tech.zeals.co.jp

今回もとても面白い会になりましたので、当日の模様をお届けできればと思います。

  • イベント全体の雰囲気
    • LT一覧
  • ぱんでぃーさんの登壇
  • さいごに

イベント全体の雰囲気

f:id:zeals-engineer:20191006000741j:plain

※奥に見える卓球台がいつも気になってます

今回はfreeeさんのオフィスにあるイベントスペースをお借りして、イベントを開催しました!めちゃくちゃキレイな会場で、音響やスクリーンなどの設備が整っていたのが印象的でした(羨ましい...)。

当日は、後ほどお話するぱんでぃーさんを含め計7名の方にLTをしていただいたのですが、「実践的なテクニックやツールについての話」と「少しマニアックだけど面白いフレームワークやツールの話」とが半分ずつくらいでした。

学びもあり、笑いもありという、他にはない珍しい雰囲気の中で当日のイベントが進んでいきました。

f:id:zeals-engineer:20191006131213j:plain

LT一覧

・Code Generatorを作ってコンポーネントを自動生成しよう
・やっと Sprockets やめる話
・JavaScript で広げる xR の世界
・Web Audioで作る!快感エンターキー
・Gatsby.jsとCloud Functionsで毎週自動でコンテンツが更新され続けるフレームワーク比較サイトを作った話
・変化(へんげ)の術

音声を扱う『p5.js』といWeb Audio APIの話や、JSでxRを実装する話についての話は、普段のWeb開発ではなかなか触れることがなかったので、「そんなフレームワークまで存在するんだ」と、JSの対応範囲の広さやトレンドの移り変わりの速さを感じました。

一方で、Gatsby.jsなどのSSRのLTやsprockets(Rails)を含んだ話、あるいはコンポーネントについてのノウハウなど、とても実践的な話も多く、JSを触りたい欲求が強くなってしまいました。(以下一部資料)

speakerdeck.com

speakerdeck.com

 

ぱんでぃーさんの登壇

さて、Zealsエンジニアぱんでぃーさんの登壇です。

続きを読む

React+Redux環境に『Jest』と『 Enzyme』を導入し、フロントエンドでのテスト環境を構築しました

f:id:zeals-engineer:20190930124820p:plain みなさんこんにちは!
Zealsでフロントエンドエンジニアとしてインターンをしている栂瀬といいます。

この度Zealsで、テスティングフレームワークのJestを導入しました!
背景や導入にあたっての取り組みなど、何を行なったかを詳しくご紹介していきたいと思います!

  • Jest & Enzyme とは
  • 背景
  • 実際に動かしてみた
    • 【準備】React Componentをテストしよう
    • 実際のテストコードをご紹介
      • タグの有無をテスト
      • イベントのテスト
    • Reduxのテスト
      • reducerのテスト
    • テストコード勉強会
      • 勉強会のコンテンツ:shallow is faster than mount ??
        • 検証結果
      • 勉強会の成果
  • テスト導入のメリット
  • 今後の展望
  • まとめ

Jest & Enzyme とは

JestとはFacebookが開発しており、細かい設定なしで簡単にフロントエンドのユニットテストを行うことができるテスティングフレームワークです。

jestjs.io

EnzymeとはAirbnbが開発しており、Reactのテストコード記述を簡単にしてくれるテストユーティリティツールです。

Enzymeには、shallowレンダリングフルDOMレンダリングの機能が備わっています。
shallowレンダリングを行うことで、インスタンス化またはレンダーされていない子コンポーネントの振る舞いを心配することなくユニットテストを行うことができます。

airbnb.io

背景

フロントエンドにテストを導入した背景としては、以下の課題を解決することにありました。

  1. 手動でデバッグを行うのが大変で、本番にデプロイ後に発覚するバグが多かった
  2. デバッグの難しさゆえに、継続的・定期的にリファクタリングを行うことが難しかった

これまでは、エディタでコードを書き終わった後にブラウザでアプリを立ち上げ、実装の影響が及ぶ箇所を1つずつ手動でテストしていました。
リファクタリングに関しては特に難しく、リファクタリング前と後で挙動が全く同じになっていることを目視で確認しなければいけなかったため、抜け漏れが発生してエラーに繋がることもありました。

実際に動かしてみた

【準備】React Componentをテストしよう

ZealsではフロントエンドのフレームワークにReact.jsを使っており、1ファイル1Componentというルールでファイルシステムを構築しています。
そのため、テスト対象のComponentを決めたらそのファイルと同名の.test.js(ts)ファイルを作成し、そこにテストコードを書いています。

import React, { Component } from 'react';

class Sample extends Component {
  render() {
    return (
      <div>
        <h1> {title} </h1>
      </div>
    )
  }

}

export default Sample;
import React from 'react';
import Adapter from 'enzyme-adapter-react-16';
import Enzyme, { shallow } from 'enzyme';
import Sample from './Sample';

Enzyme.configure({ adapter: new Adapter() });

describe('Sample.jsx', () => {
  const defaultValues = {
    title: 'hoge',
  };

  const makeComponent = (args = {}) => (<Sample {...Object.assign({}, defaultValues, args)} />);

  test('show h1', () => {
    const component = shallow(makeComponent());
    expect(component.find('h1').text()).toEqual('hoge');
  })

})

実際のテストコードをご紹介

それでは基本的なテストコードを紹介していきます。

タグの有無をテスト

以下はフラグ(ここでは isOpenModal )の true / false によって、タグの表示が切り替わっているかをテストするコードの例です。

import React from 'react';
import Adapter from 'enzyme-adapter-react-16';
import Enzyme, { shallow } from 'enzyme';
import Sample from './Sample';

const defaultValues = {
  isOpenModal: false,
}

const makeComponent = (args = {}) => (<Sample {...Object.assign({}, defaultValues, args)} />); // 値の指定がなければdefaultValuesを設定

describe('When isOpenModal is true', () => {
  test('Modal should be displayed', () => {
    const wrappper = shallow(makeComponent({ isOpenModal: true })); // shallowを使用するためshallow wrapperを作成
       expect(wrappper.exists(Modal)).toBeTruthy();
  })
})

describe('When isOpenModal is false', () => {
  test('Modal should not be displayed', () => {
    const wrappper = shallow(makeComponent());
       expect(wrappper.exists(Modal)).toBeFalsy();
  })
})

イベントのテスト

eventのテストではMock Functionを使用します。
フロントエンドチームの方針で、イベントが正しくcallされているかどうかもテスト対象に含めています。

import React from 'react';
import Adapter from 'enzyme-adapter-react-16';
import Enzyme, { shallow } from 'enzyme';
import Sample from './Sample';

const makeComponent = (args = {}) => (<Sample {...Object.assign({}, defaultValues, args)} />);

describe('execute event', () => {

  // onChangeイベントが呼ばれたかどうかをテストする
  test('execute onChange event', () => {
    const onChangeMock = jest.fn()
    const wrappper = shallow(makeComponent({ ValueIsChange: onChangeMock })); // テスト対象のComponentの関数にMock Functionを代入
     wrapper.find('input').simulate('change');
     expect(onChangeMock).toHaveBeenCalled();
  })

  // onClickイベントが呼ばれたかどうかをテストする
  test('execute onClick event', () => {
    const onClickMock = jest.fn()
    const wrapper = shallow(makeComponent({ toggleSomething: onClickMock }));
     wrapper.find('button').simulate('click');
     expect(onClickMock).toHaveBeenCalled();
  })

  // onClickイベントに `e.stopPropagation()` が効いているかのテスト
  test('execute onClick event', () => {
    const onClickMock = jest.fn()
    const wrapper = shallow(makeComponent());
     wrapper.find('button').simulate('click', { stopPropagation: () => { onClickMock() } });
     expect(onClickMock).toHaveBeenCalled();
  })

})

Reduxのテスト

Zealsではステート管理にReduxを使用しており、そちらのテストも行なっています。

reducerのテスト

reducerのテストコードが書かれたファイルに、initialStateとactionCreatorをimportをしているのがポイントです。

initialStateをimportしていることによりコードベースの変更による影響を防いでいます。
また、actionCreatorの責務は主にactionをreturnするというシンプルなものなので、reducerのテストに移譲して効率よくテストできるようにしています。

import { cancel, expand, setData } from './actions/sample' // action
import { initialState } from './reducers/sample'
import { sample as reducer } from './reducers'

// importしたinitialState
// export const initialState = {
//   isCanceled: false,
//   isExpanded: false,
//   data: {},
// };

describe('reducer', () => {

  // initialStateがそのまま返ってくるか
  test('initialState should be returned', () => {
    expect(reducer(undefined, {})).toEqual(...initialState);
  });

  test('handle CANCEL', () => { // CANCELはcancel reducer自身が呼ばれたことを検知するための識別子
    const action = cancel()
    expect(reducer(initialState, action)).toEqual({
        ...initialState,
       isCanceled: !action.isCanceled, // cancelが呼ばれたらisCancelがtrueになる想定
    });
  });

  test('handle EXPAND', () => {
    const action = modalExpand(true) // action内では引数の値をisExpandedに代入している
    expect(reducer(initialState, action)).toEqual({
        ...initialState,
        isExpanded: !action.isExpanded,
    });
  });

  test('handle SET_DATA', () => {
    const value = 'testValue'
    const action = setData(value)
    expect(reducer(initialState, action)).toEqual(Object.assign({}, initialState, { data: Object.assign({}, initialState.data, { sampleData: action.value }) }));
  });

テストコード勉強会

Zealsではフロントエンド全体のレベルを底上げするため、毎週末にフロントエンドチームで集まって勉強会を開催しています。

今回のテスト導入にあたって、最近はテストに焦点を当てた勉強会を開催していました。
テスト導入時に丁寧にメンバーにノウハウを共有しながら進むことで、属人化の防止やテスト規約に対する共通認識をチーム内で得ることが可能です。

進め方としては、フロントエンドの各メンバーは、毎週コードベース内の小さなComponentや小さなロジックのテストを書いてプルリクエストを出します。
そして毎週末に、全員でプルリクエストをディスプレイに映しながら、お気に入りのテストや学んだことを説明し、議論します。

GitHub上のPRに対してレビュー

勉強会のコンテンツ:shallow is faster than mount ??

勉強会ではレビューのほか、ペアプログラミングを実施したり、より大きなComponentのテストに取り組むなど様々なことを行なっています。
例えば最近では、Enzymeのドキュメントには shallowmount より速いと書かれていましたが、それがどのくらい速いのかを検証してみました。

Difference between Shallow, Mount and render of Enzyme · GitHub

以下で実際のコードと共に詳しく紹介していきます。

▼Demo.test.js▼

import React from 'react';
import Adapter from 'enzyme-adapter-react-16';
import Enzyme, { shallow, mount } from 'enzyme';
import Parent from './Parent'

Enzyme.configure({ adapter: new Adapter() });

const makeComponent = () => <Parent/>

describe('shallow', () => {
  test('test Parent by shallow', () => {
    const wrapper = shallow(makeComponent())
    for (let i=0, sum=10000; i<sum; i++) {
      expect(wrapper.find('h1').text()).toEqual('Parent');
    }
  });
});

describe('mount', () => {
  test('test Parent by mount', () => {
    const wrapper = mount(makeComponent())
    for (let i=0, sum=10000; i<sum; i++) {
      expect(wrapper.find('h1').text()).toEqual('Parent');
    }
  });
});

▼Parent.jsx▼

import React, { Component } from 'react';
import Child from './Child'

class Parent extends Component {
  render() {
    return (
      <div>
        <h1>Parent</h1>
        <Child></Child>
      </div>
    )
  }
}

export default Parent;

▼Child.jsx▼

import React, { Component } from 'react';

class Child extends Component {
  render() {
    return (
      <div>
        <h2>Child</h2>
      </div>
    )
  }
}

export default Child;

テスト結果1

mount の方が実行速度が早くなりました。

なので実験として、 shallowmount の実行速度の速さは実はComponentの構造に依存するのではないか という仮説のもと、稼働中のサービスで使われている複雑なComponent(複数のstyled Component・importしたComponent・関数・state・propsが含まれる)に対して shallowmount で同じテストを行って実行速度を比べてみました。

ソースコードは載せることはできないのですが、以下のようなテストを実行しました。

▼Component.test.js▼

import React from 'react';
import Adapter from 'enzyme-adapter-react-16';
import Enzyme, { shallow, mount } from 'enzyme';
import Component from './Component';

Enzyme.configure({ adapter: new Adapter() });

const defaultValues = { title: '' };
const makeComponent = (args = {}) => (<Component {...Object.assign({}, defaultValues, args)} />);

describe('shallow', () => {
  test('shallow is faster', () => {
    const component = shallow(makeComponent({ title: 'my test' }));
    component.setState({ isOpen: true }); // isOpenがtrueの時だけtitleが表示される

    expect(component.find('.title').text()).toEqual('my test');
  })
})

describe('mount', () => {
  test('mount is slower', () => {
    const component = mount(makeComponent({ title: 'my test' }));
    component.setState({ isOpen: true });

    expect(component.find('.title').text()).toEqual('my test');
  })
})

すると以下のように shallow の方が速いという結果になりました。

テスト結果2

検証結果

単純なComponentのテストでは、速度だけで shallowmount を比べると(僅差ではありますが)なんと mount の方が速い結果が得られてしまいました。

しかし、アプリケーションにおいてはComponentが1番目の検証コード(Child.jsx)のような単純な構造ではないという前提のもと、ドキュメントでは shallow の方が速くなると書かれているのかもしれませんね。

勉強会の成果

勉強会を行なった結果として、エンジニアメンバーが新しいテスト文化に早く慣れることができました。

また、古いComponentと比較すると、ユニットテストを導入した新しいComponentでは事前に問題が可視化されてエラーが発生しにくいなどのテストの効果が目に見えて生まれていて、「テストコードなしでこれまでどうやってアプリケーション作ったのだろう」と思うほど、テストの効果が高いことを実感できました。

テスト導入のメリット

当初の課題についてですが、以下のように解決することができました。

  1. テストが自動化され、デバッグに必要なエンジニアの手間とバグが少なくなった
  2. リファクタリングが正しく行えたのかをエンジニアがプログラマブルに判断しやすくなった

実装後に挙動を確かめるにはコマンドを入力してテストを実行するだけで良いため、手動でテストする工数が大幅に減りました。

それにより次のタスクに取り掛かるのも早くなり、全体的な実装スピードの向上に繋がりました。
2に関しては、特に正確性の向上が著しかったですね。

今後の展望

今後のフロントエンドテストに関してですが、いわゆるTDD(テスト駆動開発) での開発をフロントエンドで進めていきたいと考えています。

そのために、勉強会やPR上での議論を繰り返すことで、エンジニアのテストに対する免疫をなくし、新しく入ってきていただいたエンジニアの方への積極的なノウハウ共有を行っていきたいと思います。

まとめ

今回はテストコードに関する取り組みを紹介しましたが、他にも課題にフロントエンダー全員で挑戦したりと、Zealsならではの取り組みがたくさんあります。今後もテックブログで、そのような取り組みを紹介していければと思います。

続きを読む

進化し続ける「Zeals」サービスを支えるアーキテクチャについて

f:id:zeals-engineer:20190922211042p:plain

こんにちは、Zeals CTOの島田です。
本日は、進化するZeals(旧fanp)サービスのアーキテクチャを、 リリース当初と現在で比較して紹介 します。

以下のようなアジェンダで解説していければと思います。

  • はじめに
  • リリース当初のアーキテクチャ
    • 技術選定
    • 新しくプロダクトの課題
  • 現在
  • 今後の展望
  • さいごに

Go(gRPC, Protocol Buffers)を用いたmicrosevicesやkubernetes(GKE)を用いた新しいインフラ環境など、これからのZealsを支えるコア技術について解説できればと思います。

続きを読む