RAKSUL TechBlog

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

Timeless Leadership Principles for a New World

Thanks to technological advancements, our world is flattening and shrinking faster than ever before. As techies, we no longer need to travel or relocate to other parts of the globe in order to gain international and intercultural experiences, for they are literally served to our doorstep in the post-pandemic world. Thriving and leading in this new world of such rich diversity ironically calls for going back to the basics and practicing some time-tested principles. In this article, I have attempted to enumerate some of them, which I have witnessed and experienced both in real and virtual multinational settings.

Execution

Grand visions and impressive strategies mean absolutely nothing without focused, disciplined and persevering execution. Execution is not glamorous, fanciful, or a nice and friendly job. It is being detail-oriented, thorough and obsessed with meeting commitments - every single time. Gaining leadership credibility by delivering high quality results, consistently, is only achieved by relentlessly and meticulously tracking everything and tying the loose ends on a daily basis. This is not to undermine delegation, but to emphasize the culture of delegating with accountability.

Conscientious Accountability

The culture of accountability transcends regional, cultural and language boundaries and promotes clarity, trust and team spirit. When we consistently hold ourselves and others accountable for committed results it sends a strong signal that our collective success is not optional and every single team member needs to contribute to it. As simple as it might seem, even good leaders need practice at assigning accountabilities crisply and unambiguously, clarifying expectations in terms of deliverables and timelines, giving heads-up on possible hurdles and pointing to sources of help if needed.

Teamwork for the business

Every corporate organization has its own vision, missions, goals and projects. Individual and leadership brilliance, charm, charisma, eloquence, foresight, etc. are rather inconsequential without a spirited team executing towards a common goal. A true leader acquires the right talent, motivates them to work as a team and aligns the team with the business goals. I used to work with a leader who would begin important meetings with a slide that just said “we are a business”, and this had the uncanny ability to orient everyone present. It basically meant we need to contribute every ounce of our energy for the business that relies on us; it implicitly conveyed that we are not a club, not a social organization, not a university, which all are completely different contexts.

Braving VUCA

It is a VUCA world out there. VUCA stands for Volatility, Uncertainty, Complexity and Ambiguity! Our world today is dynamic, fast, ruthless and waits for none! Technologies are changing, industries are being reinvented, companies are morphing; processes, ideas and beliefs are getting outdated… It needs us to be curious and open-minded. We need to observe externally, learn, unlearn, adopt and adapt very swiftly, just so that we don’t get left behind. To quote Albert Einstein, “In the middle of difficulty lies opportunity”.

Agree and commit or disagree and commit

This one is easier in some cultures than others; nevertheless, can’t be ignored in a global setting, in the knowledge industry. Some of the best, winning ideas emerge from multiple, diverse voices contributing, conflicting and collaborating. A golden rule that I learned from one of my previous managers is: “we discuss, debate, diverge and we finally agree and commit or we disagree and commit”. By doing this, leadership decision-making is never undermined and it is understood to be separate from discussion and debates.

Finding Purpose and Resonance

Better Systems, Better World! What an amazing sense of purpose! When a leader connects the team and what they do to such a resonating purpose, it galvanizes the team. Finding a sense of purpose at work can be extremely powerful! A job all of a sudden becomes a cause. This can lead us into a virtuous cycle of discovering new challenges, going beyond boundaries of the past, learning new things and achieving unexpected targets which in turn ensure perpetual engagement, excitement and progression.

Sanjay Rajasekhar CTO, Josys; President, RAKSUL India

Organization Management with Job Description and Report line structure

I'm Shian Izumida, a software engineer and engineering manager. RAKSUL has scaled about 500 employees across multi regional offices, and only the printing industry unit - the biggest business in RAKSUL group - has more than 50 software engineers on staff in Japan and Vietnam. In this post, I will share the organization management methods that we have introduced since this year to address quality growth in the scaling organization.

Background

Until now, the talent management of RAKSUL’s software engineers has consisted of competence based Grade Standard, performance based OKR, and 360 degree feedback. And some of them were working on multiple projects managed by different managers. However, along with the company’s scaling, while some of them could work well with high autonomy, others faced some challenges by multiple report line managers such as working prioritization by different management's ownership. Therefore, to address this and strengthen the management structure, we introduced the reporting structure and job description for all positions.

Report line structure

Concept of Engineering organization structure:

We clarified the relationship between the evaluator and the evaluatee on the simple report line. We don’t normally allow multiple hat. Then, this structure makes segregation of duties clear, and be able to focus on our work with clear priorities without worrying about the multiple areas of responsibility required by several managers.

Job Description

For certain senior or higher grade members, the managers define the job descriptions of their direct reports. These job descriptions are public with their OKR/MBO in the company and it can guarantee the transparency for the range of impact their work.

OKR / MBO

We apply OKR as a goal of our team such as delivery schedule of epics, product quality KPI, business KPI and so on. The draft of each project is typically written by TechLead and finalized by engineering managers.

Here is a sample Job Description of a TechLead:

Summary

Having used the job description and report line structure for about six months, here are some GOOD and MORE from me.

Good

  • Can leverage job description for recruitment and evaluation of our staff in relation to the general job market
  • This helps me explain to my direct reports about how high-level expectation could be associated with their direct reports’ missions
  • Published job descriptions are great value when I consider missions for anyone

More

  • Quality of job description depends on the managers.
  • Strict “police” who remind all managers to submit the job description need until the operation is penetrated
  • Simpler competency-based personal development is still good enough for junior to intermidiate software engineers without job description

Of course, not all can follow the above rule perfectly for the moment. But this strongly supports organizational alignment, and encourages everybody to arrange the way toward our same goal. We will continue to optimize our product development performance with better management!

Making Josys data analytics platform more manageable

In this article, I would like to discuss how our data analytics team in Josys have evolved this year, and outline each steps towards a more manageable data analytics platform.

Originally a department within the Raksul Inc. company, Josys Inc. has now split off as an independent company, offering the SaaS “Josys” whose goal is to facilitate IT administrators’s day-to-day tasks. The product was launched in September of last year, and we as a company are still developing. Especially for the data analytics team, we have only been put together as a team in April of this year.

Prior to this, there was only one data engineer to manage all the data such as KPI measurements and product usage (not me), and all analyses were done on Redash with a little help of R scripts here and there. There were many manual tasks on the consumers of these data, i.e. the sales team, such as exporting CSV files from Redash and importing them to their own spreadsheets to identify which customers they need to call today.

While Redash is a versatile BI tool that we continue to use today, it is not very good at managing many queries over a long period of time. As many other data engineers/analysts in our company have commented, once there are enough number of queries, we can no longer identify which queries are actually used in the business reports, and which queries are safe to delete. Reviewing queries is not easy either, and each team has to come up with their own solution.

Set up a repository to store SQL

To address this, the first step we took at Josys data analytics team was to create a GitHub repository to store all our SQLs. At the moment, this is the current directory structure in our repository:

.
├── README.md
├── bigquery
│   ├── sql
│   │   ├── gcp_project_1
│   │   │   ├── mart_foo
│   │   │   │   ├── some_query.sql
│   │   ├── ...
│   ├── ...
├── redash
│   ├── dashboard
│   │   ├── feature_1
│   │   │   ├── some_measurement.sql
│   │   │   └── another_measurement.
│   │   ├── some_other_query.sql
│   │   ├── ...
└── ...

Once all queries were migrated, both reviewing and maintaining the queries became so much more easier. For reviewing, now all we needed to do was raise a pull request on GitHub and assign a reviewer. All the review comments and changes were now visible in a single page, which may seem obvious to an application developer, but was still a drastic change from when we were going back-to-back from Slack threads to the Redash SQL editor.

Query maintenance was also a much more efficient, since we now had the option to globally search queries for a specific column or table name. Before, when application development team had modified the schema, we needed to 1) identify all queries that referenced the column/table, and 2) navigate to the query editor for each page, and finally 3) make the necessary changes. Obviously, this process is prone to mistakes such as forgetting to update one or more queries, and also is time consuming. After the GitHub migration, searching queries and editing queries could be done much faster, and these changes could also be reviewed by another person.

Agree on a coding format

The next step after our migration to GitHub was to agree on an SQL coding format. After reviewing past discussions among other data analytics teams in the company, we decided to go with this format:

select
    students.name
    , students.code
    , campus.name
from students
    join campus on students.campus_id = campus.id
where students.enrolled
    and campus.name is not null

The key features of this format are:

  1. user lowercase for everything
  2. commas and operators should be placed at the beginning of the next line
  3. join operators should be indented w.r.t. from clauses

This article from cookpad (in Japanese) explains the intentions behind the rules extensively, but to summarize,

  1. syntax highlighting is enough to denote which words are reserved keywords (e.g. concat)
  2. leading commas and operators helps readers understand how many columns/conditions there are in the query
  3. from clause is a result of all the joined tables, and also join is not a clause so should be indented

While we have not yet installed a linter, SQLFluff is recommended to lint/format this style of SQL (you can try it online here).

Write a script to automate deployment to BigQuery

As part of establishing the Josys data analytics platform as a team, we have also set up BigQuery as our data warehouse (I have also written about this process here). All analyses on BigQuery are managed as "views", not project-level queries, and our repository stores the queries that define these views. On top of this, we also added a python script to automate updating these views with queries written in our local machine.

To illustrate this, let’s say we have

  • a dataset mart_customers, and
  • a view onboarding_status in the dataset, which belongs to
  • josys-analytics GCP project.

In this case, we would have a corresponding file in our repository bigquery/sql/josys-analytics/mart_customers/onboarding_status.sql.

Now, if we didn’t have the script and we made some changes to the SQL file, we would have to:

  • open BigQuery in our browser,
  • navigate to the view in question,
  • paste the SQL from the local file to the browser editor,
  • and finally, save the updated SQL.

This is quite the hassle, and would seem to be too much work just to fix a small typo. But with our script, we can simply run

python update_views.py

and handle updating the views in one command. Internally, this script uses the python BigQuery client and a pre-defined list of SQL files for each dataset. The script calls the [client.query method](https://cloud.google.com/python/docs/reference/bigquery/latest/google.cloud.bigquery.client.Client#google_cloud_bigquery_client_Client_query) to run create or replace view DDL statements. Needless to say, this script has made updating the BigQuery views very simple.

Final words

To summarize, Josys analytics team has

  • set up a repository to store our queries,
  • agreed on a coding style for our SQLs, and
  • wrote a script to automate deployment to our data warehouse.

These steps have vastly simplified our workflow, and helped increase the quality of the data we provide to stakeholders.

Lastly, I would like to acknowledge the data analytics team at Raksul department, whose repository we have heavily referenced for setting up our own.

Thanks for reading, and happy holidays!

プロダクト開発の不確実性を下げるために意識している3つのこと

はじめに

こんにちは!ラクスルのエンタープライズ事業部でプロダクトマネージャー(以下、PdM)をしている平光です。 ラクスルアドベントカレンダーの19日目を担当します。

簡単に自己紹介します。

ラクスルに新卒で入社して、現在6年目になります。学生時代に、LPO/CVR改善のSaaSにて営業・マーケティングのインターンを行った後、Edtechの会社でエンジニアとしてインターンをし、ラクスルに入社しています。ラクスルでは、ECやサプライチェーンマネジメント(以下、SCM)など、様々な領域でPdMを行ってきました。 今は、エンタープライズ事業部にて、ラクスルを大企業・中堅企業の方にも利用していただくことを目的に、事業開発およびプロダクト開発をしています。

エンタープライズ事業の紹介

現在、ラクスルでは大企業・中堅企業向けのサービスとして ラクスル エンタープライズ を提供しています。決算説明会資料にもある通り、以下の3つを今後の成長ドライバーと捉えており、本事業は3を担っています。

  1. EC化の進展による対象市場の拡大
  2. オーガニック・買収によるカテゴリー拡張
  3. 個人事業主/SME中心の事業展開から、大企業セグメントへの顧客基盤の拡張

https://ssl4.eir-parts.net/doc/4384/ir_material_for_fiscal_ym/128321/00.pdf

不確実性を下げるために意識していること

「リリースしたプロダクトを実際に利用してもらえるか」「そのサービスの利用が事業成長に繋がるのか」その答えはリリースしてみないとわからない状態だと思います。 PdMは、生みの苦しみに苛まれながらプロダクト開発に向き合うことと思います。

そのような中で、新しく生んだプロダクトが確実に事業の成長に繋がるように、不確実な状態をさげていくことが、PdMの大事な役割だと思っています。

本記事では、プロダクト開発の不確実性をさげるために、私が意識している3つのことをまとめられたらと思います。

1. 1Wayな意思決定を避ける

まず1つ目は、1Way(一方通行)な意思決定を避けることです。

その意思決定は「あと戻りできるのか?」「やり直しがきくのか?」を意識しています。

プロダクト開発においては、初期のアーキテクチャーや設計がその後のサービス展開の成否をわけます。 1機能の追加によるデータベースのスキーマ変更などが、今後のサービス展開を妨げる要因にならないか?その可能性がある場合、自分は意思決定に確信を持ててるのかを自問します。

初めから、その後のサービス展開をすべて見通せていれば、それに越したことはもちろんありません。 しかし、そのようなスーパーマンはいないでしょうし、当時は考えもしなかった展開を作っていけるのが、長期で事業価値を作っていくことのおもしろさだと思います。

したがって、1Wayな意思決定を避けることは非常に大切なことだと思います。

2. 具体と抽象を行き来する

プロダクト開発をしていると、よく耳にすると思いますが、非常に難しいですよね。

具体の定義をGoogleで調べてみたところ、Oxford Languagesの定義では、以下でした。プロダクト開発においては、実際の顧客の声を聞くこと・一次情報を取りに行くことなどが当てはまると思います。

(そのものが単に考えられるというだけでなく)形・姿を備えること。具象。

反対に、抽象の定義をGoogleで調べてみたところ、Oxford Languagesの定義では、以下でした。

多くの物や事柄や具体的な概念から、それらの範囲の全部に共通な属性を抜き出し、これを一般的な概念としてとらえること。

当たり前ですが、言葉の定義からもわかるように抽象は数多くの具体の事柄から生まれます。したがって、起点は、顧客の声を聞くこと・一次情報を取りに行くことです。

具体の事柄を集めたら、抽象化を行います。似ていること、共通していることを探して、事柄の関係性を見抜きます。

ラクスル エンタープライズのプロダクト開発において、抽象化するときに意識している観点は「ステークホルダー」「顧客のビジネスモデル」「印刷物の利用用途・シーン」「業務の流れ」です。たとえ業界・業種が異なる企業であっても、印刷物の利用用途や業務の流れが一緒であることは多々あります。外から見える情報だけで抽象化するのではなく、一歩踏み込んだ情報から抽象化していくことが大事なのかもしれないと感じています。

事柄の切り取り方、グルーピングの粒度など、具体をどれだけ抽象化してプロダクト開発に落とすのかはPdMの腕の見せ所ですね。

チームのエンジニアにアーキテクト思考という書籍をおすすめしてもらったので、貼っておきます。

3. リターンを複線化する

プロダクト開発の優先順位をみなさんはどのようにつけていますでしょうか?ROI(Returen on Investment)は多くの会社において、意思決定の基準の1つになっていることと思います。では、リターンをどのように定義していますでしょうか?

私は、1つのプロダクト開発によって、複数箇所からのリターンを設計できないかを検討します。

たとえば、とあるセグメントの顧客に向けて機能Aをリリースするとします。機能Aを利用する顧客は、利用しない顧客と比べてリピート率がN%改善すると見込みました。リピート率の改善からリターンを設計し、リリースしました。結果は想定していたより顧客の機能Aの利用率が低く、当初のリターンを下回りました。しかし、機能Aで作ったアルゴリズムを社内オペレーションシステムに組み込むことによって、社内業務の効率化をすることができると考えていました。結果、機能Aの開発によって生み出された資産を活用して、他のユースケースでリターンを得ることができました。

このように、メインでは顧客が機能Aを利用するユースケースからのリターンを設計しつつ、他のユースケースでも活用できるようにプロダクトを設計していくことで、リターンの確度をあげられると思っています。

ただし、そればかりを考えすぎて、本来向き合う課題・提供したい価値とずれてしまうのは本末転倒なので注意が必要です。

まとめ

ここまで、プロダクト開発の不確実性を下げるために意識している3つのことをまとめてきました。みなさん、いかがでしたでしょうか?

とはいえ、不確実性をゼロにすることはできないので、最終的にはリーダーやオーナーがどれだけ顧客に向き合い、プロダクトについて考えてきたかだと思っています。スタンスを取って意思決定をし、それを正解にすることが一番大事でしょう。

まだまだ日々試行錯誤しながら取り組んでおります。みなさんの意見も聞けると嬉しいです。

最後に

この記事を読んでラクスルに少しでも興味を持っていただいた方に向けて(勝手に)TO DOリストを用意しました。

▼すぐにラクスルに応募したい方 ・レジュメをアップデートする ・応募する

▼転職を検討するタイミングで応募したい方

  • RAKSUL Engineer Recruitment Bookをブックマークする
  • タレントプールに登録する(ご希望のポジションで募集を開始する際に採用担当からご連絡いたします)
  • レジュメをアップデートしておく

みなさまのアクションをお待ちしています!

OpenAPI Generator typescript-fetch を使ってみる

この記事は ラクスルの2022年アドベントカレンダー 18日目の記事です。

こんにちは!グループ会社のダンボールワンに出向中の 🐈 miyahkun です。現在はフロントエンドエンジニアとして働いています。今回は OpenAPI を利用したクライアントコードの自動生成について紹介したいと思います。

背景

OpenAPI のクライアント生成として OpenAPI Generator というプロジェクトが有名です。弊社でも多くのプロジェクトで使用されており、生成先の言語として TypeScript、HTTP クライアントに axios を用いる typescript-axios というオプションが最も使用されています。

最近 OpenAPI のクライアント生成を新たに行う機会があり、いつものように typescript-axios をインストール・実行しました。そして自動生成されたコードを確認してみると、既存のプロジェクトで使用しているバージョンとは異なる挙動となっていました。

ここでは typescript-axios で気になった挙動の変化と解決策について共有します。

typescript-axios はもうケース変換してくれない

私たちの開発チームでは JSON 形式の API レスポンスにおいて、プロパティを snake_case で提供しています。一方、その API を利用する JavaScript (TypeScript) で書かれたクライアント内では camelCase を使用したくなります。これは JSON API を扱うにあたって非常にありふれた話題かと思います。

せっかくクライアントコードを自動生成するからには、以下のような処理も自動でやってほしいですよね。

  • リクエストのパラメーターは camelCase -> snake_case へ変換
  • JSON 形式のレスポンスではプロパティを snake_case -> camelCase へ変換
  • それに対応する TypeScript の型もケース変換されたものを生成

私たちが既存のプロジェクトで使用しているバージョンの typescript-axios では、これらの要求の一部に応えてくれていました。

  • リクエストのパラメーターは生成されたコード内でケース変換
  • レスポンスは TypeScript の型としてケース変換したものを生成する。一方で、実体は自前でケース変換する必要がある。

しかし、このケース変換の要求に応えてくれていた生成時のオプション modelPropertyNaming が v5.3.0 で削除されていることを知りました。削除された理由はこちらの issue で述べられています。

modelPropertyNaming: original というスキーマ定義を尊重する設定にした場合でも、ドットなどのケース変換不可な文字を含むプロパティ名が意図せず変換されていました。例えば、 product.name というプロパティ名が product_name になるといった具合です。

代替案の検討

私のチームは代替案としては以下の3つを挙げました。

  1. modelPropertyNaming がサポートされている typescript-axios の古いバージョンを使う。
  2. axios-case-converter などでプロパティ名の変換を行い、TypeScript の型を変換する処理は自前で書く。
  3. modelPropertyNaming がサポートされている Fetch API を利用した typescript-fetch を使用する。

(1) は該当バージョンが 1 年ほど前のリリースということもあり、積極的にメンテナンスされることが期待できないため候補から外しました。

(2) は自動生成されるそれぞれの型に対して、ケース変換を一度通さないといけない点が煩わしいと感じて却下しました。

(3) で述べているように OpenAPI Generator には HTTP クライアントとして Fetch API を使用する方法も用意されています。こちらは typescript-axios で以前にサポートされていたものと同等の modelPropertyNaming が存在しており、今回はこちらの案を採用することにしました。

私のチームが開発しているサービスでは IE11 のサポートが廃止されたこともあり、Fetch API を気軽に使えるようになりました。これにより Fetch API のポリフィルや axios を使わないことでバンドルサイズを削減できます。また、Node.js v18 から experimental ではありますがフラグなしでグローバルに生えた fetch 関数を利用できるようになったのも採用の後押しとなりました。

参照:New globally available browser-compatible APIs

typescript-fetch と typescript-axios の比較

typescript-fetchtypescript-axios は生成方法のオプションに差異があり、デフォルト値が異なる場合や、対応するオプションがそもそも存在しない場合もあります。私のチームが使うオプションについて、実際に動作させて挙動を確認してみました。

(以下の比較は Docker イメージ openapitools/openapi-generator-cli:v6.2.0 で実行しています。)

オプション typescript-axios typescript-fetch
API 呼び出し処理の生成先 apiPackage で指定必須 ./apis (該当オプションなし)
モデルの生成先 modelPackage で指定必須 ./models (該当オプションなし)
モデルと API 呼び出し処理のファイル分割 withSeparateModelsAndApi = false (デフォルト値) 分割される (該当オプションなし)
パラメーターの受け渡しにオブジェクトを使用するか useSingleRequestParameter = false (デフォルト値) useSingleRequestParameter = true (デフォルト値)

これらのオプションを設定することで、私のチームではインターフェースに大きな差異なく使用できています。

typescript-fetch のケース変換処理

typescript-fetch では愚直に snake_case <-> camelCase 間の変換を行なっています。以下がOpenAPI スキーマと自動生成されたケース変換しているコードの一部です。ケースを動的に変換する方式と比較すると生成されるコード量としては多くなってしまいますが、複雑な TypeScript の型変換が不要で個人的には気に入っています。

paths:
  /search:
    get:
      responses:
        '200':
          $ref: '#/components/schemas/product'

components:
    schemas:
        search_response:
              properties:
                products:
                  type: array
                  items:
                    $ref: '#/components/schemas/product'
              type: object
        product:
          required:
            - name
            - price_tax_included
            - dot.separated
          properties:
            name:
              type: string
            price_tax_included:
              type: integer
            dot.separated:
              type: string
          type: object
// レスポンスのケース変換処理
export function ProductFromJSONTyped(json: any, ignoreDiscriminator: boolean): Product {
    if ((json === undefined) || (json === null)) {
        return json;
    }
    return {
        
        'name': json['name'],
        'priceTaxIncluded': json['price_tax_included'],
        'dotSeparated': json['dot.separated'],
    };
}

最後に

本記事ではケース変換の問題を発端に typescript-fetch を紹介しました。IE 11 サポートの終了や Node.js の対応などで Fetch API を利用できる環境が整ってきました。それに伴い OpenAPI Generator の typescript-fetch は今後より積極的に採用されると思っています。この記事が何かの助けになれば幸いです。

参照