RAKSUL TechBlog

ラクスルグループのエンジニアが技術トピックを発信するブログです

今日からはじめるポジティブ思考

はじめに

RAKSUL Advent Calendar 2021」8日目は、PdM(プロダクトマネージャー)の齋藤が担当します。現在、私は新聞折込やポスティングなど集客支援を行うプロダクトを担当しており、10名を超える開発メンバーとともにモノづくりに取り組んでいます。

今日は、ポジティブマインドの重要性について書きます。

 

ポジティブマインドの重要性

昨今、IT業界は、どこを見渡しても、人手不足、リソース不足は深刻です。

DXやIT投資がより鮮明に進み、社内ではいくつものプロジェクトが並行して立ち上がり、少ない人数でプロダクト開発を進めなければならない状況です。会社組織としても、より高い生産性、チームのアウトプットが求められるようになっています。

PdM(プロダクトマネージャー)としても、新規開発やサービス運営などやるべきことは数多くあり、調整事も日々増えていきます。そんな中、PdMは、複数のメンバーをプロダクト開発視点で連携、マネジメントしていくことになりますが、思考としてマインドセットは重要な要素の一つだと考えています。

私がポジティブマインドの思考について興味を持ち始めたのは、この記事に出会ってからでした。

幸せな社員は創造性3倍、労働生産性1.3倍働き方改革における幸福度の重要性を説く(2017年11月) https://logmi.jp/business/articles/243410

 

マインドには、ざっくり分けるとポジティブとネガティブの2つがあります。もし、一緒に働く人を選べるなら、基本ポジティブな人がいいですよね。楽しく仕事できる環境が私は好きです。

ある研究成果*によると、ポジティブマインドでいると、生産性(パフォーマンス)は130%になるという発表があるそうです。私はこの記事を最初に読んだとき、みんながニコニコ、ハッピーでいるだけ、生産性(パフォーマンス)は130%になるのかと思いましたが、実は短絡的に帰結するものではなく、個人の考え方や行動が正のループを繰り返すことによって、結果、生産性が上がるということでした。

つまり、PdM含めて働く人がポジティブマインドを心がけることによって、正のループがチームに広がり、組織全体のパフォーマンスが130%になるのです。そのほかにも、ポジティブマインドの発想により、良いアイディアや創造性が膨らむ。仕事にも「やってみなはれ文化」が増える。結果として、楽しく働ける環境が作られ、従業員満足度も上がるかもしれません。ポジティブマインドを持つことは、良い結果に繋がりやすいと言えるでしょう。

逆説的にネガティブマインドの人が増えれば、否定的な意見が増え、過剰な品質確保が要求され、新たなチャレンジが阻害されて、負のループを繰り返し、結果、生産性(パフォーマンス)が70%になるということも言えます。

マインドセットの持ち方一つで、チームの価値観や生産性は劇的に変わります。 ポジティブマインドは重要なパーソナルスキルの一つとも言えます。

*参考情報 幸福学研究の分野では、エド・ディナー氏らの論文が有名。 世界の幸福学研究の父と称される米イリノイ大学心理学部名誉教授、エド・ディーナー氏らの論文によると、主観的幸福度の高い社員の生産性は平均で31%、売上は37%、創造性は3倍高いという傾向がある

 

ネガティブマインドを作らないために

仕事を通じて、人はストレスを溜め込み、不平不満が形成されていきます。 そのモヤモヤは、日々蓄積され大きくなり、やがて見える形でネガティブマインドになります。

ネガティブマインドを防ぐには、気軽にコミュニケーションできる環境が重要です。つまり雑談です。よく世間話の切り出しとして「ちょっと聞いてくださいよー!」「最近どうですか?」がありますが、きっかけは何でもいいのです。相手に解決策を求めているわけでもなく、オチが用意されているわけでもないことを前提に、会話の入り口があればいいのです。

まず、人と会話する(傾聴する)ことで思考が吐き出されます。対話により自分の考えが整理され、違う視点に変わっていきます(内省を促す)。そのときにポジティブマインドがベースにあれば、良い方向に進む可能性が高くなります。

そのためには、職場内での挨拶も重要で、会話しやすい雰囲気を作り出してくれます。雑談、コミュニケーション量が増えれば、ミーティングや調整事項も進めやすくなるでしょう。(関係の質が改善していきます)

ちなみに、ネガティブ思考とはちょっと異なりますが、不平不満は人を動かす原動力でもあり、ビジネスのアイディアになることもあります。また、物事に対する慎重さ、保守といったバランスも大切です。ネガティブが絶対悪というわけではありません。

 

身近な例えとして、ポジティブな人は、飲み会に誘いやすい・誘われやすいと思いませんか? 周りにいる優秀なマネージャやリーダーには、聞き上手な人が多いと思いませんか?

すべての基本は、会話、コミュニケーションです。

 

ポジティブマインドを続けるために

ずっとポジティブマインドを維持することは簡単ではありません。ストレスが高い環境化で高負荷な毎日を過ごしていると、気が滅入ってしまうこともあるため、たまには息抜きも重要です。

ポジティブマインドを維持するためには、定期的に気持ちをリセットしたり、インセンティブを与える必要があります。

例えば

  • 週末に楽しいイベントを用意する
    • 金曜日は飲みに行く
    • 展示会に行く
    • 週末旅行に行く
    • Netflixをイッキ見する、ゲームをやり込む(没頭系)
  • セルフケア
    • ジムに行く、運動する
  • 啓発系、情報収集
    • 書籍を読む(自己啓発、自伝書、サクセスストーリーなど)
    • テレビを見る(情熱大陸、カンブリア宮殿など違う世界線の活躍を知る)
  • 仕事術
    • タスクリストを作成し、頭の中を整理する、リセットする
    • 1on1で雑談する(傾聴してもらう)
  • 有給休暇で完全に休む

 

人は、ゲーミフィケーションのように、短期的な目標を達成することが好きなように設計されていると思います。仕事とプライベートの切り替えを上手く使うことによって、ポジティブマインドを長く維持できるかもしれません。

 

勘違いなポジティブマインドに気を付ける

ポジティブマインドでも、良くないパターンがあります。

  • 無計画なポジティブ
    • 無計画で先が見通せないままノープランで進むこと。楽観的に行動しすぎるのは信頼を失います。
    • また、計画があっても筋が悪い場合は、信頼を失うことがあります。
  • 空気を読まないポジティブ
    • トラブル対応中にメンバーへの配慮がない発言をしたり、関係者とのミーティングで明らかに間違った意見を急に言ってみたり、シーンに応じた配慮がないとマイナスに作用します。

もちろん、誰にでも間違いはあります。考えがあるポジティブマインドは基本OKですが、無計画な楽観、成り行き任せの楽観は要注意です。

こういうシーンを見つけても、同じメンバーからは、なかなか指摘しにくいものです。そこはマネージャー/リーダーの出番です。1on1でのフィードバックやコーチングを通じて、本人の性格を考慮しながら育成していくことになります。

 

さいごに

すべての人が最初からポジティブなわけではありません。仕事に疲れてネガティブになっている人もいると思います。人の性格はなかなか変えられないかもしれませんが、まずは自分から始めることが大切です。ポジティブマインドは他人に伝播していきます。(逆にネガティブマインドも他人に伝播していきます。)

PdMとして、ポジティブマインドを持ちながら、良いプロダクト開発と良いふりかえりを行うことで、より高い成果につながっていきます。正のループを作るのは、はじめの1人からです。

 

今日からポジティブ思考をはじめてみませんか。 be_positive_mind

 

ラクスルではPdMを募集しています!

ラクスルのアドベントカレンダー全編はこちらから

サブドメインのサービス間を Kafka を使ってデータの同期を行った

この記事は、RAKSUL Advent Calendar 2021 6日目の記事です。

まえがき

はじめまして、今年の4月にラクスルへ新卒のエンジニアとして入社しました喜屋武です。

入社以降、ラクスル事業本部のエンタープライズ事業部で法人向けのサービスをサーバサイドエンジニアとして開発しています。

私が所属しているエンタープライズ事業部が提供するサービスは、企業内のアカウントや注文の管理を行うことができる法人利用をラクにするラクスルの印刷サービスです。このサービスが提供している機能の1つとして、最近リリースしたデザイン管理という機能があります。

この機能の開発でデータ同期の手段として Kakfa を使ったため、このことについて書いていきたいと思います。

デザイン管理とは

ラクスルには、オンラインデザイン というサービスが存在します。このサービスはラクスルで印刷したい物のデザインを web 上で作ることが出来るサービスです。このサービス上で作ったデザインを他の人へ共有する場合は、共有設定をデザインごとにおこない、生成されたURLの受け渡しを行うことでデザインの共有ができます。ですが、このサービスを企業で利用しようとするとURLの管理を行わなければならず煩雑になってしまいます。

デザイン管理はこの課題を解決し、企業内でよりラクにデザインを共有できるようにした機能です。詳しい機能は、https://enterprise.raksul.com/features のデザイン管理の項目をご覧ください。

[caption id="attachment_5212" align="aligncenter" width="1024"]ラクスルのオンラインデザイン ラクスルのオンラインデザイン[/caption]

実現するための課題

この機能を実現するためにエンタープライズのサービスだけではなく、別サービスとしてラクスルが提供しているオンラインデザインのサービスと連携しなければなりませんでした。具体的に言うと、エンタープライズが持っている企業とユーザの関係性のマッピングのデータをオンラインデザイン側に提供する必要がありました。

また、オンラインデザイン上で企業が管理できるデザイン数を企業ごとにエンタープライズ側から制御したいという要望があり、エンタープライズで設定された情報をオンラインデザイン側に大きな遅延を発生させず同期を行う必要がありました。

実装した方法について

上記の課題を実現するために、Kafka というミドルウェアを一部に用いて連携を行いました。

Kafka とは

Kafka とは、分散メッセージキューのシステム間のデータの受け渡しを仲介しデータを一時的に保持するミドルウェアです。データの送り側を Producer、受け取り側をConsumerと呼びます。

メッセージキューを介してシステム間の通信を行うことで、システム間の接続経路を簡略化し、システムを疎結合に保つことができるようになります。

どのように連携を行ったか

エンタープライズ側では企業の登録や企業情報の更新を行うタイミングで Kafka に Produce するようにしました。このように Produce することで、Consume しているサービス(今回はオンラインデザイン)が自身に関係のある変更であれば、適切なタイミングでデータの更新を行うことが出来るようになります。

また、同期する必要が無いデータやログインしているユーザが初めてアクセスした時などオンラインデザイン側がデータを持っていない場合に関しては、RPCを使ってデータの受け渡しを行うようにしました。ここでいうデータを同期する必要が無いものとは、オンラインデザイン上にエンタープライズ側の情報を保存していないデータです。

上記のフローを図に表すと以下のようになります。

全体像

なぜ Kafka を使ったのか

データを同期する手段としては、 Kafka 以外にも Webhook や API などが他の方法として挙げられると思います。

仮に Webhook や API を使うとするとエンタープライズはオンラインデザインの URL やオンラインデザインから返ってくる HTTP ステータスコードなどに依存してしまうことになります。今回のように Kafka を使うことでエンタープライズは、オンラインデザインのシステムに関心を持たずデータを提供するだけのシステムとして存在することができます。

また、 Kafka を使うことで今後オンラインデザイン以外のシステムがエンタープライズの企業情報を同期しつつデータを使いたいというケースが発生したとしても Kafka を見に行くだけで情報が取れるようになります。

また、メッセージキューのミドルウェアはいくつかありますがその中でも Kafka を選んだ理由としては、ラクスル内で Kafka を使った実績が他にもあり社内の知見を活かすことができたからです。

まとめ

今回はサブドメインのサービス間のデータの同期に Kafka を使った内容について書きました。

今回のケースでは、Kafka を使うことでシステム間を疎結合にすることができました。このように関心があるサービスをできるだけ少なくすることで保守性もあがり今後の拡張も容易になると思うので、今後も有効なケースがあれば積極的に使っていきたいと思います。

 

ラクスルではエンジニアを募集しています!

 

ラクスルのアドベントカレンダー全編はこちらから

 

Vue.js v2 Composition API + Vue Apollo v4 で得られた知見

はじめに

はじめまして、2021 年に入社した新卒の宮﨑です。現在はラクスル事業本部フロントエンド開発部に所属しています。RAKSUL Advent Calendar 2021 5日目はフロントエンドに関する話題です!

弊社ではいくつかのサービスで GraphQL を使用しており、クライアントでは Vue Apollo v3 を使用しています。この Vue Apollo というライブラリには Vue.js v3 で導入された Composition API をサポートする v4 が開発されています。今回は Vue Apollo v4 を半年ほど使用してみて得られた知見、苦労した点を共有したいと思います。

技術構成

主な技術構成は以下の通りです。Nuxt.js v2 に @nuxtjs/composition-api というライブラリを利用して Composition API を使用可能にしています。

  • Nuxt.js v2.14
  • Vue.js v2.7
  • @nuxtjs/composition-api v0.30
  • @nuxtjs/apollo v4.0.1-rc.5
  • @vue/apollo-composable v4.0.0-alpha.12

Vue Apollo v4 について

弊社で以前から使用している Vue Apollo v3 は Vue.js の this コンテキストがある前提でのライブラリのため、 Composition API の setup 内でクエリを呼び出すには適していませんでした。そこで登場したのが Vue Apollo v4 (@vue/apollo-composable) です。 useQueryuseMutation などの Apollo Client (@apollo/client) API が setup や Composition 関数内で使用できるようになります。今回は Vue Apollo v4 に合わせて Nuxt.js での SSR 対応などを行う @nuxtjs/apollo も使用しています。

Composition API で Vue Apollo v4 を使うための設定

ライブラリをインストールするだけでは @nuxtjs/apollo と Vue Apollo v4 は上手く動作しません。公式ドキュメントには記載されていませんが、 こちらのissue をもとに解決できました。 @nuxtjs/apollo は Apollo Client に関する複雑な設定をしてくれるライブラリなのですが、 @nuxtjs/apollo で作成した Apollo Client を Vue Apollo v4 のクライアントとして登録する必要があります。

// <projectRoot>/plugins/apollo-client.ts

import { Context } from '@nuxt/types'
import {
  provide,
  onGlobalSetup,
  defineNuxtPlugin,
} from '@nuxtjs/composition-api'
import { DefaultApolloClient } from '@vue/apollo-composable/dist'

export default defineNuxtPlugin(({ app }: Context): void => {
  onGlobalSetup(() => {
    provide(DefaultApolloClient, app.apolloProvider?.defaultClient)
  })
})

Composition API との組み合わせ

Composition API と Vue Apollo v4 の組み合わせ方についてはネット上にもほとんど知見がなく手探りな状態でした。開発初期の頃は以下のような使い方をしていました。

買い物カートの情報を取得するクエリを例に考えてみましょう。クエリの引数として「あとで買う」オプションを取っています。

// <projectRoot>/apollo/queries/masterData.ts

import gql from 'graphql-tag'

export const getCart = gql`
  query getCart($buyLater: boolean) {
    cart(buyLater: $buyLater) {
      name
      updatedAt
      ...
    }
  }
`

続いて、このクエリを扱う Composition 関数を用意します。単純な要件の画面では 1 クエリ・1 Composition 関数の対応で実装しています。useQuery の返り値からレスポンス待ちかどうかを表す loading という値がありますが、これは ref でラップされた値なのでリアクティブに変化します。このように外部の TypeScript ファイル内でリアクティブな値を扱えるようになったのも Composition API のおかげですね。useQuery にクエリの引数を渡す手段はいくつかありますが、公式ドキュメントで詳細に説明されています。

// <projectRoot>/composables/useCart.ts

import { useQuery, useResult, Reactive } from '@vue/apollo-composable/dist'

interface Payload {
  buyLater: boolean
}

export default function useCart(payload: Reactive<Payload>) {
  const {
    loading,
    refetch,
    onResult,
  } = useQuery<CartResponse>(  // レスポンスの型は自動生成されたものを指定する
    cartQuery,  // GraphQL のクエリ文字列
    payload,    // `reactive` でラップされたオブジェクトをそのまま渡すことができる
  )

  // `ComputedRef` というリアクティブな値として結果を取り出せる
  const cart = useResult(result) 

  return { loading, refetch, onResult, cart }
}

setup 内でこの Composition 関数を実行します。

// <projectRoot>/pages/index.vue

import { defineComponent, reactive } from '@nuxtjs/composition-api'
import useCart from '<projectRoot>/composables/useCart'

export default defineComponent({
  setup() {
    const payload = reactive({
      buyLater: false,
    })

    const {
      loading,
      refetch,
      onResult,
      cart,
    } = useCart(payload)

    onResult(() => {
      // cartQuery のレスポンスが返ってきた際に実行する処理をここに書く
    })

    return { loading, refetch, cart }
  }
})

これで setup の中でクエリを呼び出すことができました。

useQuery と useLazyQuery の使い方

Vue Apollo v4 ではクエリを呼び出す関数として useQueryuseLazyQuery が用意されています。useQuerysetup が実行されるタイミングでクエリが呼び出されます ( setup の実行タイミングについては Vue.js v3 のドキュメントを参照してください) 。一方、useLazyQuery はクエリの遅延呼び出しを行うために用意されています。しかし、useLazyQuery は Vue Apollo の公式ドキュメントには記載がされていないので注意が必要です (記事執筆時点 2021/12/2)。使い方は Apollo Client のドキュメントに記載されており、API は基本的に同じです。

直列呼び出しで発生した問題と解決策

GraphQL は 1 度のリクエストでその画面に必要な情報を取得できるのが魅力です。しかし、1 度のリクエストで完結させようとしてレスポンスタイムが大幅に伸びてしまう、早く取れる情報から画面上に表示できた方が体験として良いなど、どうしても複数の GraphQL クエリを直列で呼びたくなる場面が多々あります。この要求に対して開発初期段階では以下のように対処していました。

Vue Apollo v4 ではクエリの引数としてリアクティブな値を渡すことができます。そして渡された値の変更を検知して、クエリを自動で呼び出すという機能があります。先ほど例として挙げた useCart において、payload.buyLater = true のように変更すると、getCart クエリが自動でリクエストされるイメージです。また、レスポンスは  useResult を利用することで ComputedRef というリアクティブな値として取り出すことができます。この仕組みを利用して、前のクエリ結果から次のクエリも自動で呼び出そうという狙いでした。

getCartクエリの結果として得られたリアクティブな値(ComputedRef)を次のクエリの引数として渡す

これはクエリ呼び出しが単純な画面で非常に便利です。しかし、以下のように複数の条件でクエリの直列呼び出しを変えるような要件では破綻することがわかりました。

  • あるモーダルを開く場合
    • getCart クエリ -> B クエリ
  • あるボタンを押した場合
    • getCart クエリ -> C クエリ
  • ページの初期表示の場合 ->
    • getCart クエリ -> D クエリ

getCartクエリの後に3つのクエリを直列で呼ぶ

この方法の問題点は、リアクティブな値を useQuery の引数として渡すと、その後に実行するクエリを選択したり、実行タイミングを制御するのが難しいということです。例えば、getCart  -> B クエリ だけを期待していたが、C クエリ も実行されてしまうようなケースです。そこで私たちは getCart の結果を B クエリ の useQuery へ渡す際に、リアクティブな変更を保留し、任意のタイミングでその変更を適用するような仕組みを自作するなどでなんとか対処していました。

しかし、これでは開発が進むにつれてカオスになり、どこかのタイミングで手をつけられなくなるのは明らかでした。そこで 1 週間ほど時間を確保して、改めて何が問題なのかを洗い出しました。

抱える問題点

  1. 直列で呼ばなければならないクエリが多い
  2. 得たいデータはユーザーアクションに依存しているが、クエリ呼び出しはユーザーアクションに関係なく直前で呼ばれるクエリに依存している

1 つ目の問題は開発メンバーの GraphQL 利用経験が浅かったこともあり、クエリ設計がどうしても REST のようになってしまったのが良くなかったと思います。GraphQL の思想に立ち返り、可能な限り 1 度のリクエストでデータを取れるように順次リゾルバーを修正し、現在も対応中です。

2 つ目の問題点は直列なクエリの呼び出しとユーザーアクションがうまく紐づいていないということです。そこで、多少コードが冗長になることを許容して、ユーザーアクションごとに useQuery や  useLazyQuery による直列呼び出しを書き、Composition 関数内に閉じ込めました。

クエリごとにComposition関数を使って閉じ込める

買い物カートのモーダルを開いた際に、商品と合計金額を 2 つのクエリで取得するサンプルを書いてみます。実装している中で気づいた細かな挙動などはコメントしています。

// <projectRoot>/composables/useCartModalOpen.ts
// import, interface などは省略

export default function useCartModalOpen(payload: Reactive<Payload>) {
  const cartQueryCalled = ref(false)
  const billingDetailsQueryCalled = ref(false)

  // load 関数や refetch 関数でクエリ引数を渡す場合、
  // useLazyQuery では undefined と指定できます。
  const {
    load: cartLoad,
    refetch: cartRefetch,
    result: cartResult,
    onResult: cartOnResult,
  } = useLazyQuery(getCart, undefined) 

  // 初回呼び出しは load 関数を、それ以降は refetch 関数を使用します。
  // load 関数がネットワークエラーになどにより失敗しても、
  // 2 度目以降は refetch 関数を使わないとリクエストが
  // 飛ばないので注意が必要です。
  const fetchCart = () => {
    const variables = {
      buyLater: false,
    }

    // load や refetch を実行するタイミングでクエリの引数を渡しています。
    if (cartQueryCalled.value) {
      cartRefetch(variables)
    } else {
      // getCart は GraphQL クエリです。
      cartLoad(getCart, variables)
    }
    cartQueryCalled.value = true
  }

  const cart = useResult(cartResult)

  // getBillingDetails は GraphQL クエリです
  const {
    load,
    refetch,
    result,
    onResult,
  } = useLazyQuery(getBillingDetails, undefined)

  const fetchBillingDetails = () => {
    const variables = {
      cart: cart.value,
    }

    if (billingDetailsQueryCalled.value) {
      refetch(variables)
    } else {
      load(getCart, variables)
    }
    billingDetailsQueryCalled.value = true
  }

  const billingDetails = useResult(result)

  cartOnResult(() => {
    // getCart クエリ後に次の billingDetails クエリを呼びます
    fetchBillingDetails()
  })

  return {
    cartModalOpenFetch: fetchCart,
    onResult,
    cart,
    billingDetails,
  }
}

これによって GraphQL クエリの直列呼び出しという複雑なコードが Composition 関数の中に閉じ込められ、この Composition 関数を使う側は内部でいくつのクエリが呼び出されているかを意識する必要がなくなります。また、クエリの設計を改善する際はこの Composition 関数内での直列呼び出しを無くせるようにリゾルバーを修正していけば良いということです。

今後の改善ポイント

今後はクエリ設計を見直して直列な呼び出しを減らしていくとともに、キャッシュの設定もしていきたいです。Apollo Client のキャッシュ機構に関しては私自身まだ理解が浅い部分なので、高速化していけるよう知見を貯めていきたいと思います。

最後に

以上が Vue Apollo v4 を使って得られた知見と苦労した話でした。

良かった点は Composition API との併用により、

  • 再利用可能なクエリ呼び出しを書ける
  • 煩雑になりやすい GraphQL クエリの呼び出し部分をラップできる
  • リアクティブな値をうまく使えば状態変化に合わせた自動クエリ呼び出しができる

気を付けたい点としては、

  • セットアップ方法はネット上に知見があまり無いので注意
  • GraphQL クエリを直列に呼ぶ必要がないように可能な限り設計段階で考慮しよう

特に GraphQL の設計は画面仕様への依存が強く、開発初期段階で完璧な設計を考えるのを困難だと感じています。最後に述べたような Composition 関数を利用して GraphQL クエリの直列呼び出しをラップし、徐々にクエリ設計を改善していく方法は現実的なのかもしれないと思いました。

ラクスルではエンジニアを募集中です!

ラクスルのアドベントカレンダー全編はこちらから

テレワーク 相手を知らず 照れわーく

はじめに

はじめまして。サーバーサイドエンジニアの藤本です。

ネット印刷サービスであるラクスルを開発・運用しているPBU(Printing Business Unit)チームに所属しています。

本日はRAKSUL Advent Calendar 2021 4日目です。

ラクスルにjoinして、早いもので3ヶ月が経過しました。ラクスルは2013年より開発、保守されているシステムで非常に巨大なシステムになっています。キャッチアップが大変だったのですが、チームメンバーの手厚い協力もあり、ようやく機能追加を行える程度には理解が進みました。

そこで、先日リリースした折りパンフレット・ポスターのお届け予定日指定機能の開発リードをしました。

その中での学びや気づきを記事にしたいと思います。

お届け予定日指定機能とは?

ラクスルではチラシやポスターなど、様々な商品を扱っています。その中でチラシの注文時にお届け予定日を指定出来る機能を約1年前にリリースしました。

チラシのお届け予定日リリースでの反響を受け、今回はポスターと折りパンフレットでもお届け予定日を指定出来るように開発しました。

各商品で指定可能なお届け予定日のリストは私たちが開発・運用しているECシステム側では管理しておらず、別チームが開発している委託先管理・発注システムにAPI経由で問い合わせています。

ラクスルはユーザーが注文しようとしている商品カテゴリ(チラシ、ポスターなど)や商品仕様(用紙サイズや紙の種類など)をリクエストパラメータに含め、委託先管理・発注システムにお届け予定日の問い合わせをしています。

開発をしていく中での気づき

フルリモート下での他チームとのコミュニケーションの難しさ

私は2021年9月にラクスルにjoinしましたが、ラクスルでは当時フルリモート勤務になっていました(チームビルディングの観点より11月より週1出社を開始しています)。

開発を進めていく上で見えてきた課題感

印刷ECのシステムと委託先管理・発注システムが別れている

ラクスルでは開発手法にスクラムを用いており、スクラムチーム単位での開発を行っています。各スクラムの活動内容は共有されるものの、システムの詳細な仕様は把握できていません

お届け予定日のリストは委託先管理・発注システムから取得しますので、他チームとコミュニケーションをとって開発を進めていく必要がありました。

他チームとのやり取り

入社して日が浅く、リモートでは自チームメンバーとのコミュニケーションが中心になることもあり、他チームのメンバーの人となりをよく知らない中で、コミュニケーションをとるのに心理的な障壁がありました。

そのような状況の中、認識があっているか何度もヒアリングをする必要がありました。

ドメイン知識の不足

ラクスルでは多くの商品を販売しており、それらの属性がソースコード上に定義(ex. 仕上がりサイズのA4の1/3仕上がりは "A4_ONE_THIRD")されています。サイズや色、パンフレットのおり方などなど。ソースコードに書かれているものがどの商品定義にあたるのかをパッと頭の中で紐づけるのが難しいです。

ex.size=A4_ONE_THIRD&color=BOTH_SIDE_COLOR&paper=WOODFREE&weight=135&amount=100&business_day=2&folding=HALF&crack_prevention=CRACK_PREVENTION_OFF

また、ECサイトと発注システムは異なるシステムですので、ソースコード上での定義が異なるケースがあります。ECサイト側では、A41/3仕上がりは "A4_ONE_THIRD"ですが、発注システムでは、 "ONE_THIRD_OF_A4" と定義されています。このあたりのパラメータの定義の変換を行う必要がありました。

課題解決のためのアプローチ

上記の課題感への対応として、下記を意識して行いました。

他チームを含めたオンラインMTGを設定し、パラメータの認識合わせを実施する

現状を整理し、自チームの認識している内容をドキュメント化することで、他チームとの認識内容に齟齬がないかを確認しました。

また、他チームのメンバーにコードレビューをしてもらうことで、認識にズレがないことを確認しました。

ぐいぐい聞く 🍶

上述したように人柄を把握していない人とコミュニケーションをとっていくのは大変ですが、他チームのメンバーもそれは同じだと思いますので、まず自分からここがわからない、こう考えていますが合っていますか、とコミュニケーションをとるようにしました。

とにかく自分の考えや意思を表に出すことを意識していました。

ラクスルにはCo-Operationの文化があり、チーム外からの質問や相談にも積極的に対応してくれます。

コミュニケーションを取る際には、Slackでのコミュニケーションが活発なので、さらっと聞いてみたり、最近ではhuddle(Slack上の音声会議)も利用してサクッと相談することにしています。がっつり仕様を確認したい際にはGoogle Meetで顔を合わせながらやるようにしていました。

ローカル開発の複雑さ

前述の通り、お届け予定日のリストを取得するには委託先管理・発注システムとの連携が不可欠なのですが、そこでも課題が見えてきました。

開発を進めていく上で見えてきた課題感

APIのリクエストに成功すると、APIのレスポンスを正しく処理できているかの動作確認が必要となります。APIのリクエストに成功しているかどうかの確認が簡単に行えない状況でした。

課題解決のためのアプローチ

ここまで記載した通り、他チームの開発しているアプリケーションに依存する機能となっていますので、動作確認をするためには、下記2点のどちらかの方法をとる必要がありました。

  1. ローカルに他チームアプリケーションの開発環境を整備する
  2. 他チームのアプリケーションがデプロイされているテスト環境が存在するので、そこに自チームのシステムもデプロイする

1. の方法は、ラクスルは商品ごとのマスターデータが非常に多く、他チームのアプリケーションを動作させるのにどのデータを投入するかを判断することが難しかったです。Docker環境が現在整備中(詳細後述)なこともあり、環境構築にかかるコストが高くなりそうでした。

2. の方法は、テスト環境では本番に近いデータが用意されており、またラクスルはデプロイが高速で、早ければ5分もかからない環境になっています。認識合わせを事前に行った上でコードを作成すれば、それほど多くのデプロイをする必要もないと想定しました。

そのため、今回は2の手法を選択しました。

将来的にはDockerなどでコンテナ化されており、ローカル環境で開発可能であることが理想ではあると思いますので、コンテナを利用した開発環境の準備を進めています。

💡 ラクスルは2013年に作成されたシステムを、PHPからRailsへの移行作業を行いながら開発・運用しています。PHPとRailsのハイブリッド構成でnginxでルーティングされている箇所も多いため、Docker環境の構築が難易度の高いものとなっています。ローカル環境の代わりとして、1人1台の個人開発用EC2が提供されていますが、今回はデータ投入の手間などを考えテスト環境を使用しました。

まとめ

印刷ECは結構ニッチな領域で、raksul.comは長年保守されたシステムということもあり、聞かなければわからないこともたくさんあるので素直に聞くことが大切だと思います。

ラクスルは企業のカルチャーとしてCo-Operationが浸透しており、コミュニケーションの取りやすい環境にあると感じました。ただ、やはり人となりがわかっていない人とリモートでコミュニケーションをとり、仕様を詰めていくのはエネルギーのいることでした。

今後は出社日を有効活用し、リアルで熱く議論してチームの交流を推進していきたいです 🔥

ラクスルには出社日にチーム間の交流を促進するための取り組みとして、ドリンクやケータリングが提供されています。

写真はいの一番にケータリングに群がるPBUのメンバーたち。

ラクスルではエンジニアを募集しています!

https://recruit.raksul.com/career/engineer/

ラクスルのアドベントカレンダー全編はこちらから

https://qiita.com/advent-calendar/2021/raksul

スピードチェック入稿にシールを対応させた話

はじめに

はじめまして、 ラクスルのフロントエンドエンジニアの原口です。 2020年4月に新卒第5期として入社して、入社後から今現在まで DTP チームに所属しています。

今回は、 DTP チームで開発しているデータチェック基盤の紹介と、新たにシールに対応できるように機能の追加を行った話をしたいと思います。

スピードチェック入稿とは

ラクスルでは、ファイルのアップロードと同時に自動でチェックを行い、そのファイルを印刷用に最適化する「スピードチェック入稿」というサービスを提供しています。具体的には、フォントに問題がないか?画像が壊れていないか?などのチェックと、可能であればその自動修正を行います。

スピードチェック入稿による恩恵として以下が挙げられます。

  • より早くユーザにチェック結果を届けることができる
  • 基本的なデータチェックを自動で行うことで、オペレータが高度なデータチェックに注力できる

オペレータはより多くの注文に対してデータチェックを行うことが出来るため、データチェックの平均的な待ち時間の短縮やデータチェックの品質の安定化に、欠かせない機能となっています。

シールという商材について

既にラクスルではチラシや冊子、名刺といった注文数の多い商品はスピードチェック入稿に対応していました。

今回紹介するシールは、チラシや冊子などのメジャーな商品に劣らないデータチェック件数を誇りながら、オペレータによる手動チェックにおいて次のような課題がありました。

  • 注文の縦横のサイズと印刷データの縦横のサイズが一致していないことが多い
  • データチェックを一度で通過できる割合が 20~30% と低い
  • 修正が必要な印刷データの場合、ユーザはオペレータとメールでコミュニケーションする必要がある

印刷データに関する問題をユーザに自己解決してもらうことでこれらの課題を解決するシールスピードチェック入稿を開発しました。

シールスピードチェック入稿について

ファイルアップロード

まずはじめに、手持ちのファイルを選択します。

注文の縦横のサイズが対応している場合はラクスルの提供しているオンラインデザインも利用することが出来ます。

アップロード画面

データチェック

アップロードされたファイルは自動的にトリミングされて、印刷の仕上がりプレビューが表示されます。問題がなければこのまま入稿確定まで進むことが出来ます。

アップロードと同時にデータチェックが自動で開始します

印刷の仕上がりプレビューが表示されます

調整が必要なファイル

シールはチラシや冊子などの定形の商品と異なり、様々な注文の縦横のサイズに対して様々な種類のファイルがアップロードされます。たとえば、

  • ロゴデザインのような余白が大きい印刷データ
  • 注文サイズの縦横比と印刷データの縦横比が異なる印刷データ

といったケースが考えられます。

このようなファイルをアップロードした場合、下記のような仕上がりプレビューが表示されます。

印刷したいロゴが見切れてしまっている状態

この問題を解消するため、エディタ機能を実装しました。 プレビューに問題があった場合「仕上がりを調整する」ボタンでエディタを開き、手動でトリミングの位置を調整することが出来ます。

エディタの機能

「縦幅に合わせる」「横幅に合わせる」を選択することで、簡単に印刷データの大きさを調整できます。

位置や大きさが期待通りでない場合はさらに調整できます。

大きさの自動調整

調整後は仕上がりプレビューにその調整が反映され、ユーザは再度仕上がりを確認できます。

大きさと位置の手動調整

フロントでこだわったこと

フロント側の実装にあたり、データチェック業務に携わっているオペレータの方にユーザの印刷データの特徴のヒアリングを行い、機能のあるべき形について議論しました。

例えば、当初は UI を極力シンプルにし、ユーザには画像の位置と大きさの細かい調整してもらうことを想定していたため、画像の右下に一つだけツマミを配置し、拡縮を調整する設計となっていました。 ところがユーザのファイルを見ていくと、A4サイズのデータの端にロゴのみがあるような印刷データが一定割合存在したので、画面を大きく使って操作できるような UI が必要であることが分かりました。 そこで、四隅に拡縮を調整するアイコンを置き、どんな印刷データが来ても WEB 上で調整できるようにしました。

また、リリースに向けてフロントエンド開発に携わっていないメンバーを呼んでのレビューを定期的に行いました。 普段操作し慣れている自分が気づかない問題点をあげてもらい、 UI の調整を行いました。

最後に

シールスピードチェック入稿は10月にリリースしたばかりですが、より使いやすいプロダクトを目指して機能開発を行っていきます。

ラクスルでは「仕組みを変えれば、世界はもっと良くなる」のビジョンのもと、産業構造の変革にチャレンジしています。

エンジニアも絶賛募集中ですので、ご興味をお持ちいただけましたらぜひお声がけください!