QUO CARD Digital Innovation Lab Tech Blog

クオカード デジタルイノベーションラボの技術ブログです

リモートワークスタイルチェックをやってみました

デジタルイノベーションラボの齋藤です。

永和システムマネジメント アジャイル事業部さんがリモートワークスタイルチェックをやってみたという記事をアップされていました。ミスマッチを防ぐ為に良い取り組みだと思ったので、クオカード デジタルイノベーションラボでもリモートワークスタイルチェックをやってみました。

永和システムマネジメント アジャイル事業部さんの記事

https://blog.agile.esm.co.jp/entry/remote-work-style-check-esm-2022

@mugi_uno さんのリモートワークスタイルチェック

https://gist.github.com/mugi-uno/a794bc199ce7d663f01a434a6e72504c

クオカードでは現在全社的にリモートワークを導入していますが、デジタルイノベーションラボとは少し状況が異なっている為、今回はデジタルイノベーションラボに限った形でチェックをしています。

リモートワーク比重度

リモートワークの導入期間

どの程度リモートワークを導入しているか (≒ 組織としてのリモートワークへの慣れ)

👉「4: 継続してリモートワークを3年以上導入している」

デジタルイノベーションができてからまだ5年経ってないので4になりました。ちなみに設立当初からリモートワークを導入しています。

リモートワークの導入ポリシー

👉「3: 恒久的な導入だが、根本的な制度変更が行われる可能性はある」

設立当初からリモートワーク前提の組織な為おそらく今後リモートワークができなくなる事は無いですが、変更が行われる可能性はゼロではない為3にしました。

リモートワークのマジョリティ度合い

👉「4: 全員が常にリモートワークの可能性がある(出社している可能性もある)」

全員が基本的にリモートワークですが、たまに会社で抽選で配るメロンやチケットなどが当選した場合など、稀に出社することがあります。

申請・許可の必要性

👉「5: 申請や許可は必要ない」

申請は不要です。

リモートワークの適用範囲

👉「5: 業務に関わる全メンバー(正社員以外も含む)がリモートワークを利用できる」

正社員以外も全メンバーリモートワーク利用可能です。

出社義務

👉「4: 年に数回の頻度で出社する必要がある」

入社日はPC等備品受け取りやセットアップの為に出社をお願いしています。

リモートワークで必要なサービス・アプリケーションの取り扱い

👉「4: 業務で利用するサービス・アプリケーションは主にリモートワークを前提としている。一部リモートワークでは利用できないものがあるが、リモートワーク前提のものに移行中・または移行予定がある、あるいは何らかの代替手段によってすべて解決できる。」

基本的にほぼリモートで対応可能ですが、バーコードの精度チェック機など、物理的なデバイスがオフィスにしか無いものも一部あります。

情報のアクセス範囲

👉「5: リモートワーク・非リモートワークでアクセスできる情報に差はない」

差はありません。

その他

✅社内において一定の権限を持つ人物(役員など)がリモートワークを実践している

❎5,000円/月以上のリモートワークに関する手当がある

❎オフィスが存在しない

❎対外的にリモートワークに関する発信・発表を積極的に行なっている

社長も実践しています。またリモートワーク手当は4,000円/月です。

リモートワークの文化

同期・非同期コミュニーションの割合

👉「B: 非同期コミュニケーションが中心だが、一部で同期的なコミュニケーションが必要」 👉「D: 同期的なコミュニケーションが主で、非同期コミュニケーションツールは補助的な利用のみである」 こちらはチームによって状況が違う為、BとDにしました。エンジニアはSlack中心ですが、込み入った話になった場合はMTGも行っています。

テキストコミュニケーションの割合

👉「B: テキストが主で、補助的にビデオ通話・音声通話を用いる」

一つ前の質問と同じですが、Slack中心ですが、込み入った話になった場合はMTGも行っています。

コミュニケーション頻度

👉「B: ミーティングに加え、それ以外でも日々活発にコミュニケーションを取る。雑談もそこそこ。」

毎日朝会を実施しており、それ以外でもSlackでコミュニケーションを取っています。雑談は多くは無いかもしれません。

働く時間

👉「B: 全員働く時間は定まっていないが、コアタイムなどで重なる時間が存在する」

コアタイムはありませんが、チームメンバーで朝会の時間を調整しており、その時間は重なっています。 現在は8:30から9:30の間に業務を開始する人が多いです。

最後に

結構積極的にリモートワークを実施しているのではないかと思います。リモートワークになってから他チームの人たちと雑談する機会がなくなったという声があり、課題になっています。

クオカード デジタルイノベーションラボではソフトウェアエンジニアを募集しています。

OSSへの支援について

クオカードでは様々なOSSを利用していますが、現在は業務として技術的な貢献をする余裕が無い為、寄付という形で支援をすることにしました。

今回は社内の多くのプロジェクトで利用しているJUnit 5に寄付をしました。

https://junit.org/junit5/

今後もJUnit 5に限らず、できるだけ継続的に多くのOSSの支援をしていきたいと考えています。

クオカードではソフトウェアエンジニアを募集しています。

https://quo-digital.jp/

研修体験記:Spring Core(社外)

あけましておめでどうございます、三度のJavaよりメシが好きなデジタルイノベーションラボの山本です。

2022年最初の記事を投稿させていただきます。

年明けにプロダクトグループメンバー数名でSpringFWのハンズオン研修を受講したので今日はそれについて書きたいと思います。

私は、10年以上エンジニア(主にJava)としてシステム開発に携わっており、SpringBootはここ数年よく使うFWとなっております。 基本的なSpringの知識は業務を通じて、中でもわからなかったものに関しては別途本やサイトで調べて補完してきました。 本と言っても、最低限必要そうなところを読むといったスタイルで最初から最後まで通して読んだ本はありませんでした。

弊社でも多くのプロダクトのコア技術としてSpringBootを採用しており、 それぞれバックグラウンドの異なるエンジニアのSpringの知識の平準化を図るべく、社外のハンズオン研修の受講が去年計画されました。 それが実現した、今回受講した研修はこちらとなります。

VMware Tanzu認定 Spring Core: Training https://www.casareal.co.jp/ls/service/openseminar/vmware/v013

研修は、四日間で9:30 - 17:30(昼休憩1時間)で行われました。

1日目:Spring Frameworkの基礎

2日目:アスペクト指向プログラミングに関して

3日目:SpringBoot(MVCトランザクション管理など)

4日目:SpringBoot(REST API作成、Test, SpringSecurityなど)

1〜2日目は、メニュー通りSpringFrameworkやAOPの基本に関して取り扱い、古い記憶をリファインメントしつつ、もちろん中には初めて知ることもある内容でした。 残りの日は、SpringBootを中心に取り扱い、今まさに業務で使用している内容でした。 中でも個人的にはテストケースを書くたためにSpringが提供してくれる機能を一部しか使っていないことを知ることができたのが今回の研修での一番の収穫でした。

私の今までの経験上、とりわけテストケース作成は最初に作成した人のものをベースに右へ倣えで作成していくパターンが多く、 テストケースの書き方を深堀りした記憶も改善しようと考えたこともなく、いかにその状況に「漬かって」いたかを実感しました。

四日間という期間は少し長かったですが、新年のスタートを切るには良い研修経験となりました。 また機会があれば、別の研修にも参加したいと思います。

それでは、メシ行ってきます。

OpenAPI を利用した開発フロー

こんにちは、デジタルイノベーションラボの飯島です。 今回は、OpenAPI を利用した開発とデプロイまでのフローをざっくり紹介します。

アプリケーション構成

バックエンドは Kotlin + SpringBoot, フロントエンドは TypeScript + React になります。 バックエンドが用意した API をフロントエンドが叩く、一般的なアプリケーションです。

フロー

1. OpenAPI 定義出力用の Interface を実装したコントローラーを作成

簡単なサンプルですが、以下のような Interface を作成し、implement してコントローラーを作成します。

@Tag(name = "UserCreate", description = "ユーザーの登録")
interface UserCreateController {

  @Operation(summary = "ユーザー登録")
  @ApiResponses(
      ApiResponse(responseCode = "200", description = "登録成功"),
  )
  fun create(user: User): HttpResponse
}

data class User(val name: String)

実装クラスに直接アノテーションを記載しても良いのですが、API 定義と実装が混在すると可読性が下がるため、Interface を分けています。

アノテーションspringdoc-openapiで提供されているものを使っています。

2. Interface から OpenAPI 定義の yaml ファイルを生成する

openapi-generator gradle plugin を使用し、Interface から OpenAPI 定義の yaml ファイルを生成します。

3. 生成された yaml ファイルから、フロントエンド用のコードを生成する

OpenAPI Generator を利用して、バックエンドの API を呼び出すための TypeScript コードを生成します。 生成されたコードの内部で使用される Http client については、axios や fetch, ajax など複数の中から選択できます。 docker での生成にも対応しているので、バックエンド側で生成した openapi.yml を引数に generator を実行します。

4. 生成された API 呼び出しコードを利用してフロントエンドをコーディングする

React で画面を作り、3 で生成されたコードを使用してバックエンドとやりとりするようにします。 以下のコードでは、UserCreateApi, createConfiguration, ServerConfiguration がそれぞれ自動生成されたコードになります。 先ほどバックエンドの Interface に定義した create メソッドが UserCreateApi に用意されています。

  const api = new UserCreateApi(
    createConfiguration({
      baseServer: new ServerConfiguration(url, {}),
    })
  )

  api.create({
    name: "taro"
  })

生成されたコードも TypeScript なので、型安全に使用することができます。

5. Slack からデプロイする

弊社ではほぼ全てのプロジェクトが Slack からデプロイできるようになっています。 動作確認などで検証環境にデプロイする用にデプロイマン、本番環境にリリースする用にリリースマンという bot を作ってあるので、コマンド一つで GitHub から簡単にデプロイすることができます。

最後に

バックエンドの API 定義箇所とフロントエンドの呼び出し箇所は、受け渡すデータは同じなのに言語が違うことで書き方が変わってくるため、こうして自動化できるとコーディングがかなり楽になります。 open-api generator の具体的な使い方などはまた別の機会に記事にしたいと思います。

チームのコミュニケーションについて

クオカード デジタルイノベーションラボの齋藤です。

今回はチーム内のコミュニケーションについて説明します。

MTG

デジタルイノベーションラボではCOVID‑19の影響で2020年の春頃からフルリモートになっています。 現在はスクラムで進めており、毎日チーム毎にデイリースクラムを実施しています。

Google Meet、Slackのhuddleなどチームによって使うツールは異なっていますが、MTGは全てオンラインで行っています。たまに他部署とMTGを行う事がありますが、そちらも基本的に全てオンラインになっています。

またスプリント毎のスプリントレトロスペクティブの他に、月1でチーム全体での振り返りを行っています。

Slack

デジタルイノベーションラボではできるだけチームで協力して業務を進める形にしようとしており、割と頻繁にコミュニケーションを取っている方ではないかと思います。

仕様の認識合わせや実装中に困っている事などはまずSlackで議論し、込み入った話になってきたらhuddleで話し合うという進め方になっています。

スプリント中のやりとり

フルリモートになる前からできるだけ自分が何をしているか、確認されなくても周囲の人に伝わるようにしようとしていましたが、リモート中心になってからよりそこを意識するようにしています。

周囲が何をしているかわからないと、リリース直前になって問題に気づいてリリース延期になったり、もしくは周囲からのヘルプを得られず嵌ってしまうなどの問題が起きやすいと考えています。

具体的にはGitのpushをまめに行う、Jiraのチケットにまめにコメントする、またプログラムが完成してからドキュメントを書くのではなく、実装中にWIPという形でドキュメントを公開し、随時追記・修正していく等の進め方にしています。

また悩んでいる事やこれからやろうとしている事をSlackのTimesでコメントしたりしています。

このような対応を行う事により、もし進め方や進めている作業に問題があった場合は早めに周囲の人からコメントをもらえたり、困っている内容について知見のあるメンバーからヘルプを受けたりできるようにしています。

Zoom飲み

上記のように、チーム内のコミュニケーションはそれなりにうまくいくようになってきたと思います。ただ他チームのメンバーとやりとりする機会がそれほど多く無い為、新しく入社したメンバーが他チームにどういう人がいるかわからない、また馴染むのに時間がかかるという問題が起きています。

その為希望者でZoom飲みを実施してみましたが、何の準備もしていなかった為か緊張感のある飲み会になってしまいました。

何かやることを決めた方が話しやすくなりそうというアイデアが出たのでゲームをやってみたところ盛り上がったので、今後も色々と試行錯誤していこうと思います。

開発マシンをM1 MaxのMacBook Proに切り替えることにしました

クオカード デジタルイノベーションラボの齋藤です。

2021/10/19 にM1 Maxを搭載した新型MacBook Proが発表されたので、エンジニア全員の開発マシンを切り替える事にしました。

今まではメモリを32Gにする為にMacBook Pro 15インチもしくはMacBook Pro 16インチを使っていましたが、基本的に14インチにすることにしました。

以下切り替える事にした理由です。

・現在使っている開発マシンの償却は終わっておらず、問題なく開発できているという声もあったものの、ビルド時間短縮など、早めに切り替えた方がROIが高いと判断し切り替える事にしました。デジタルイノベーションラボでは極力マネージドサービスを利用するなど、コストを払えば効率化できる部分はそのように進めています。

・COVID‑19の影響で全員ほぼフルリモートワークの状況になっていますが、所属しているチーム外の人と話す機会がなくなった、また質問がしにくい等で、COVID‑19の感染状況が落ち着いたら出社する日があっても良いのではという声がメンバーから上がっていました。16インチは2キロ以上と持ち歩くのは少し重い為、また家では外付けモニタを使っている人が多い為、基本的に14インチにすることにしました。

ちなみに個人的には新型Mac miniを渇望していたのですが、今回発表されなかったので現行モデルを購入することにしました。

QUOカードPay オンラインストアをリライトしました

こんにちは、日本のテトリスプレイヤーの上位 0.5% に入るデジタルイノベーションラボの飯島です。 今回は Java のプログラムをリライトした話を書きます。

背景

2020年6月にQUOカードPayオンラインストアの内製化を行いました。 その時は EC2 で動いていた Java プログラムを ECS に乗せるために必要な修正を加えましたが、ソースコードの大半には手を加えていませんでした。 内製化から1年と少し経ちますが、バグの修正や新規機能追加の際に複雑なソースコードの解読に時間を取られることが多く、これはまずいということで2ヶ月間他の機能追加無しでリライトをすることにしました。

方針

プロダクトの全体的な設計から見直すのが理想でしたが、そこまでやると果てしない時間がかかってしまうため、少ない時間で効果が得られそうな箇所や、今後よく触りそうな箇所を重点的にリライトするようにしました。

実施した内容

Kotlin 化

QUOカードではサーバーサイド実装の言語としてKotlinを採用しています。 今回リライト対象となるコードはJavaで書かれていたため、まずKotlin化をすることにしました。 Kotlin化により nullとmutableの扱いが限定されるので、可読性の向上とバグの減少が見込まれます。

null まわりの修正

元々のソースコードでは @Nullable や @NotNull が使われていなかったため、引数に null が入ってくるのか、戻り値として null が考えられるのかがぱっと見でわからないようになっていました。 そのため不要な null チェックが蔓延していたり、また必要な箇所に null チェックが足りなかったりという状態でした。

Kotlin では基本的に null は許容されず、型の後に ? をつけることで nullable になります。

val a: String? = null
val b: String = null // コンパイルエラー

Java から Kotlin に変換する際には型に ? をつけておき、可能なら ? を削除するという方針でリライトしました。 ただ、この修正は当初考えていたよりもずっと難しく、まだかなりの量の ? が残ってしまっています。 理由としては、元々のソースコードを読んでも使われている値が null を許容するのかしないのかの判断が難しかった為です。 例えばメソッドの引数で言うと、そのメソッドを呼んでいるメソッド、そのメソッドの引数がそのまま使われているなら更にその呼び出し元、というように追いかけて虱潰しに調べないといけません。 クラスのフィールドで言えば、それが使われている全ての箇所を調べないと判断ができません。

変数の不変化

Kotlin では、val で宣言した変数は不変 (immutable) になり、後から変数に値を代入しようとすることができません。これにより、一度初期化した変数がソースコード中のどこかで変更されないことがコンパイラレベルで保証されるので、変数に何が入っているのかを把握するためにソースコードを深く潜っていかなくてもよくなります。(詳しくは「副作用」の箇所に書きます) リライトにあたり、ローカル変数でもクラスのフィールドでもとにかく可能な限り val で宣言することを意識しました。

綴りミス(タイポ)を修正

当たり前ですが綴りミスは無い方が良いです。 コードリーディングの妨げになりますし、正しい綴りで検索をかけた時に引っかからなくなります。 例えば、Downloadで検索したのに、Donloadと綴りミスしていたら一生引っかかりません。 (DonloadはもしかしたらDonald なのでは?と社内で議論になりました) 今回は一つのクラス内に2, 3個綴りミスがあるというレベルだったので、他の項目のリライト中に直せる箇所だけ直す、という対応をとりました。

修正時の注意点

thymeleafなど実行時に名前を解決する処理がある場合、Java側の綴りミスを直してコンパイルが通っても実行時にエラーが発生します。 この部分のリライトは影響範囲の調査に時間がかかるため、今回は後回しにしています。

String の配列をクラスに変更

例えば名前と年齢を持つ犬を表現したい時、一般的にはクラスを作ると思います。

data class Dog(val name: String, val age: Int)

これをStringの配列で表している箇所がありました。

// 0番目の要素に名前、1番目の要素に年齢を入れる
String[] dog;

年齢を取り出す場合は Integer.parseInt(dog[1]) となっています。 さらに、犬をたくさん扱う場合はこうなっていました。

String[][] dogs;

実際にはdogsのような体を表す変数名はつけられておらず、result のような名前だったため、変数宣言から何を意味するのか読み取れず苦戦しました。 こちらは Dog クラスを用意することで対応しました。

フラグ保持用の Map<String, String> を List に修正

飼い主に対して複数の猫が紐づくモデルを考えます。 単純に DB から猫を取ってくると、このようなクラスにマッピングできます。

class Cat {
    private Person person; // 飼い主
    private String name;
}

DB から取ってきた List に対してループを回す中で、既に登場した飼い主を保持しておく Map を作っている箇所がありました。

Map<Person, String> map;
for (Cat cat: cats) {
    if (!map.get(cat.person).equals(“”)) {
        // キーが存在しない場合の処理
    } else {
        // キーが存在する場合の処理
        map.put(cat.person, “”);
    }
}

最初はこの map が何をしているのかを理解するのに時間がかかりましたが、単純に処理済みの飼い主を保持しているだけということがわかりました。

val processedPersons: List<Person>;

としてリストに持たせて変数名を分かりやすくすることで解決しました。

private メソッドを別クラスの public メソッドにする

他のクラスが持つべきメソッドが private メソッドとしてコントローラーやサービスに実装されていたので、そういうものはあるべき場所に実装を移動しました。

private String getDogInfo(Dog dog) {
    return dog.getName() + “ ” + dog.getAge().toString();
}

⬇️

data class Dog (val name: String, val age: Int) {
    val info: String
        get() = “$name $age”
}

String クラスや各種ライブラリが提供するクラスなど、自分で修正することができない型に対するメソッドに関しては、拡張関数を作成して対応しました。 型をラップしたクラスを作って必要なメソッドのみを生やすという方法も考えられますが、今回は手間も考慮して拡張関数方式にしました。 拡張関数を使うと、メソッドのネストをチェーンで記述できるようになるので、単純な可読性も向上しました。

private String addExclamationMark(String s) {
    return s + “!”;
}

public void a() {
    addExclamationMark(
        addExclamationMark(
            addExclamationMark(“hello”))) 
}

⬇︎

fun String.addExclamationMark(): String = “$this!fun a() {
    “hello”
        .addExclamationMark()
        .addExclamationMark()
        .addExclamationMark()
}

メソッドから副作用を取り除く

副作用のあるメソッドはコードリーディングの妨げになります。

public class Fox {
    private String name;
    private int age;

    // setter & getter
}

public void a() {
    Fox fox = new Fox();

    setName(fox, “taro”);
    fox.setAge(3);

    System.out.println(fox);
}

private void setName(Fox fox, String name) {
    fox.setName(name + “-chan”);
}

このコードでは、最初に宣言された時の fox と println した時の fox では状態が異なります。println 時点での fox の状態を知るには、fox が宣言された場所から println される場所までの全ての行を読まないといけません。もし fox が宣言された時点から変更されないことが保障されるなら、println がどれだけ離れていても fox の状態はすぐ把握できます(そもそも変数の宣言と使用箇所が離れていること自体がよくないですが)。

data class Fox (val name: String, val age: String)

fun a() {
    val fox = Fox(getName(“taro”), 3)
    println(fox)
}

private fun getName(name: String): String = “$name-chan”

Kotlin だとこのように書けば、宣言時から変更されることはありません。

副作用を持つメソッドの見分け方として、まず戻り値のない(voidの)メソッドは怪しいです。setXXX という名前のメソッドもほぼ副作用があります(中には BigDecimal のように新しいオブジェクトを返すものもありますが)。あとはリライト中のメソッドから呼び出しているメソッドの中を逐一確認していきます。副作用を持つメソッドを呼び出しているメソッドも副作用があることになるので、呼び出し階層を全て辿る必要があります。

修正方法ですが、副作用としてセットする値を返すようにします。上の例でいうと、setName の中で fox に値をセットしていたところを、getName として値を返し、呼び出し元の方で使用するようにしています。 何重にもネストしたプライベートメソッドの中に副作用がある場合は、こうやって少しずつ副作用を上のメソッドに押し出していき、最後に呼び出し元で使用するようにします。 副作用の無いメソッドが完成したら、副作用のある元のメソッドは Deprecated でマークし、「副作用があるため、極力 XXX を使用すること」とコメントしておきます。 こうすると Intellij などの IDE では呼び出し箇所に取り消し線が引かれるため、今後リライトするときの目印となります。

終わりに

リライトをすればするほど「これは一から書き直した方が早いのでは。。」という気持ちに見舞われましたが、実際はソースコードの奥深くに隠された仕様を発見するための時間が必要になるので、やっぱり時間が取れたタイミングでリライトしていくのが現実的かな、とも思いました。

また、技術的負債は放っておくと返済にかかるコストが上がっていくと言うのを身を以て体験しました。クリーンアーキテクチャの最初の方に、「時間がある時に綺麗にすればいいと思うかもしれないが、そんな時間は一生来ない」のようなことが書いてありましたが、自分も実装するときはそれを意識しようと思いました。今回は何をするにもソースコードの解読に多大なコストがかかってしまう状態になっていたのでリライトの時間を設けざるを得ませんでしたが、最初から綺麗なコードだったらもっと生産的なことに時間を割けたのに、、と考えると、常にその時点で最も読みやすいコードを書く努力は大事だと思います。(ただ、リライトしてコードを綺麗にしていく作業自体は楽しかったです。)