Zeals TECH BLOG

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

Design Systems & Style Code Refactoring

f:id:ssabcire:20200709101900p:plain

By Vish Nambiar & Kim Hyun-sung

Contents

  • Contents
  • Introduction
  • Our Current Problems
    • 1. DRY - Don't Repeat Yourself
    • 2. Locating implementations of CSS
    • 3. We spend too much time on styling
  • Previous Refactoring Strategies
  • The Connection Between Style and Design
  • Our Solution
    • Design Systems
    • Organising Our Codebase
    • Using Storybook
  • Conclusion
続きを読む

Python の log を JSON フォーマットで出力することでログが複数行分割される問題を防ぐ

こんにちは!Zeals で Python をメインに書いている新卒 1 年目のソフトウェアエンジニアの玉城です。

Python で Cloud Logging(旧: Stackdriver Logging)Datadog にログを出力するとき、複数行に分割されてしまい、元のログイベントとの関連付けが難しくなり困ったことはないでしょうか。

例えば、以下のようにログを書いたときに

logger.info("test\n I want to eat sushi")

Cloud Logging にはこのように表示されてしまうといった感じです。
f:id:ssabcire:20200703103623p:plain

これを JSON フォーマットにすることで解決してみましょう。


続きを読む

Trying to Build a Redux Implementation with Web Assembly in Rust

f:id:ssabcire:20200626105439p:plain

Introduction

Hi, I'm Aaron, a frontend engineer at Zeals. At Zeals, we use React for the frontend of our main application, with Redux as our primary method of managing state. While this works just fine for most of what we need our frontend to do, I like to explore other options and how they compare to established solutions.
So today, I thought it would be interesting trying to try to port Redux to web-assembly, and see how this compares to its Javascript counterpart. And when I say "compare", I mean this in the loosest sense.
I'm not familiar on all the latest web-assembly news and technology, so this is simply an opportunity to explore it and share what it was like.

This article somewhat assumes the reader is somewhat familiar with React. I will try not to spend too much time on it as it could be an entire article on its own, but not knowing react should not be a hurdle to understanding the larger implications.
I will spend some time talking about Redux/Rust/Wasm, as it will guide the reader into understanding how I tried to use Wasm, and perhaps why the trade-offs work the way they do.


  • Introduction
    • Web-assembly
    • Rust/Wasm
    • Goal
    • On Redux
  • The code
    • Introduction
    • Generating the Rust/Wasm Library
    • The React-JS project
    • Using the library in the new JS Project
    • Creating the Redux Store
    • The Redux "Store" methods
    • Using the redust "library" in our JS code
  • Result
    • Speed test
    • Why?
  • Conclusion
続きを読む

新卒から見たZealsエンジニア組織について

f:id:dainiizawa:20200316104115p:plain


はじめまして!現在Zealsで内定者インターンをしている玉城です!
私は大学の在学期間中、GoとGCPを使ったWebアプリケーションの作成や時間短縮ツールなどを独学で作成したり、自然言語処理と機械学習を用いた研究を行っていました。Zealsには、利用している技術や提供しているサービス、熱量の高さから、自分がやりたいことを達成できる環境だと思い入社することを決めました。
現在、内定者インターンという形でジョインしてからおよそ3ヶ月ほど経ちましたが、とても凄い環境で圧倒されています。
そこで今回は、新卒から見たZealsの開発組織と環境について書いていきます。
特に、

  • 私がZealsでどのように働いているのか
  • エンジニアが働きやすい環境か
  • 新卒でも主体的に動ける環境があるか

こういった問いを持つ方に、少しでも参考になれば幸いです。

続きを読む

GKE環境のRedashでPostgreSQLサーバのディスクが溢れたので、Persistent Volumeをいじって何とかした

f:id:lead-zep0324teruhisa:20200202211533p:plain

こんにちは、分析基盤を担当している鍵本です。

本日は昨年の10月に GKE に移行した Redash の PostgreSQL サーバでディスクがいっぱいになってサービス停止してしまった 時のお話をします。

GKE に移行した話については2019年11月15日に公開したテックブログ Redash 分析環境のGKE移設&ver.3から7へのアップデート手順を公開 を御覧ください。

tech.zeals.co.jp

続きを読む

fast_jsonapi gemを1.2から1.5へアップデートしたら躓いた点と対応策

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

こんにちは、分析基盤を担当しつつ Rails エンジニアのサポートもやっている鍵本です。

本日は Fast JSON API のバージョンアップをした時に突然テストが落ちて困った話をします。

github.com

  • 背景
  • テストが落ちる状態の再現
  • エラーの原因
  • 対応方法
  • まとめ
  • 最後に

背景

Zealsは、LINEおよびFacebook Messenger上で動作するチャットボットサービスで、WebのForm体験をチャット上で完結させることができます。この Zeals のアーキテクチャは同じくテックブログの記事『進化し続ける「Zeals」サービスを支えるアーキテクチャについて』でご紹介しているとおり、バックエンドを Ruby on Rails と React + Redux (& TypeScript) で実装しています。

tech.zeals.co.jp

Rails は DB から必要なデータを準備し JSON 形式に変換したものをフロント側の React に渡しています。その JSON 形式に変換する部分に Fast JSON API を用いています。アップデート前はバージョン 1.2 を用いておりテストコードも問題なく通っていたのですが、1.5 に gem のアップデートをしたところ突然テストが通らなくなりました。

テストが落ちる状態の再現

サンプルプログラムを使ってテストが落ちた状態を再現してみます。

属性として size, color を持つ Shirt モデルを作成します。

class Shirt < ApplicationRecord
  enum size: { 'XS': 0, 'S': 1, 'M': 2, 'L': 3, 'XL': 4 }
end

Shirt モデルのためのシリアライザを以下のように作成します。

class ShirtSerializer < ActiveModel::Serializer
  include FastJsonapi::ObjectSerializer

  attributes :size, :color
end

Shirt モデルをシリアライズして JSON を返すコントローラを作成します。

class ShirtController < ApplicationController
  def show
    @shirt = Shirt.find(params[:id])

    shirt_json = ShirtSerializer.new(@shirt).serializable_hash[:data][:attributes]

    render json: shirt_json
  end
end

ShirtController#show は {size: サイズ, color: 色} を返すので、例えば size: 'M' となる Shirt を生成して、その値が正しく得られることをテストするコードを用意します。

require 'rails_helper'

RSpec.describe ShirtController, type: :controller do
  describe '#show' do
    let(:shirt) { create(:shirt, size: 'M', color: 'black') }

    subject { JSON.parse(response.body, symbolize_names: true) }

    example 'response の size が M である' do
      get :show, params: { id: shirt.id }

      expect(subject[:size]).to eq shirt.size
    end
  end
end

fast_jsonapi のバージョンを 1.2 としてテストすると問題なく成功します。

$ bundle exec rspec spec/controllers/shirt_controller_spec.rb
.

Finished in 0.02659 seconds (files took 1.39 seconds to load)
1 example, 0 failures

ここで fast_jsonapi のバージョンを 1.5 にアップデートすると以下のような結果を得ます。

$ bundle exec rspec spec/controllers/shirt_controller_spec.rb
F

Failures:

  1) ShirtController#show response の size が M である
     Failure/Error: shirt_json = ShirtSerializer.new(@shirt).serializable_hash[:data][:attributes]

     NoMethodError:
       undefined method `each' for #<Shirt:0x00007f9478a3f318>
     # ./app/controllers/shirt_controller.rb:5:in `show'
     # ./spec/controllers/shirt_controller_spec.rb:10:in `block (3 levels) in <top (required)>'

Finished in 0.01942 seconds (files took 1.43 seconds to load)
1 example, 1 failure

Failed examples:

rspec ./spec/controllers/shirt_controller_spec.rb:9 # ShirtController#show response の size が M である

エラーの原因

fast_jsonapi の serializable_hash メソッドは ActiveRecord::Relation オブジェクト か否かで処理を分けているのですが、その判定方法が 1.2 以前とそれ以降で変わってしまったことが原因でした。具体的には 1.3 以降では以下のような条件で判定しています。

      resource.respond_to?(:size) && !resource.respond_to?(:each_pair)

我々の Shirt モデルには enum で size という属性を持っているため size メソッドが存在します。そのため ActiveRecord::Relation オブジェクトであると間違って判定され、

@shirt.each do |s|
  ...
end

のような処理が実行され、each メソッドが存在しない というエラーが出たということでした。因みに 1.2 以前では以下のような判定だったので問題なかったというわけです。

      resource.respond_to?(:each) && !resource.respond_to?(:each_pair)

対応方法

テーブルスキーマを変更して列名を size から別のものに変更すればいいのですが、影響範囲が大きいためシリアライズする際にオプションを渡すことで対応することとしました。すなわち、

class ShirtController < ApplicationController
  def show
    @shirt = Shirt.find(params[:id])
    options = { is_collection: false }

    shirt_json = ShirtSerializer.new(@shirt, options).serializable_hash[:data][:attributes]

    render json: shirt_json
  end
end

@shirt が ActiveRecord::Relation オブジェクトでないことがわかっているから採用できる方法です。これで先のテストコードが成功することがわかります。

続きを読む