もくテク「弥生品質を支えるQAエンジニアの取り組み」を開催しました!

こんにちは。弥生でエンジニアをしている、たけです。
2022年6月16日(木)にもくテクを開催しました。

いつもの開催報告に入る前に、嬉しいニュースがありますので先にご紹介させていただきます!!

毎月多くの方々にご参加いただいているもくテクですが、今回は162名の方々にお申し込みいただき、初めてconnpassのイベントランキングにランクインしました!
もくテクをいつも応援していただき、本当にありがとうございます!

それでは、大盛況だった6月のもくテクの当日の様子をご紹介します。

6月のテーマは「弥生品質を支えるQAエンジニアの取り組み」

イベントページはこちら(※開催は終了) mokuteku.connpass.com

より良い製品・サービスを作っていくためには、高い品質を担保することは重要な要素の1つですが、どのように品質を担保していくか悩まれている方々も多いと思います。
そこで、今回のイベントでは弥生のQAエンジニアが登壇し、弥生の製品・サービスの品質を支えるための取り組みや体制についてご紹介しました。

セッションの内容

弥生の品質保証体制の紹介と取り組み by 春田優子

まずは春田さんが、弥生の品質保証体制とその取り組みについて発表しました。

弥生では1つの開発チームの中にPM、TechL、QLという役割を持つメンバーがいて、プロジェクトをリードしています。 「QL」=「Quality Leader」は品質面のリードを行っており、一般的に言う「QAエンジニア」の役割を担っています。

このようなチーム構成を取ることで、開発メンバーと密にコミュニケーションを取りながら品質保証に取り組むことができます。 しかし、その一方で「他のチームのQLメンバーとのつながりが弱い」という課題がありました。
今回の春田さんの発表では、そのような課題を解決するべくQL横ぐしのチームを作ったことや、そのチームでどのような工夫を行ってきたかについてお伝えしました。

組み合わせテストとハイレベルテストケースで、テストをやり切った話 by 家登あずさ

続いては家登さんの発表です。

家登さんは弥生にQAエンジニアとして入社してから、サービスのファーストリリースに立ち会うことが多かったそうです。今回の発表では最近リリースされた、弥生の「証憑管理サービス」の品質保証のためにどのようなテスト技法を用いたかについてお話ししました。

まずは参加者の方々に、「知っているテスト技法」についてリアルタイムでアンケートを取りました。その結果はこちらです!

そして、「証憑管理サービス」のリリースはタイトルの通り「組み合わせテスト」と「ハイレベルテストケース」でやり切ったことをお話ししました。
「証憑管理」という言葉にあまり馴染みがない参加者の方もいらっしゃるかもしれないため、「証憑管理サービス」を「カレー」に例えてどのようなテストケースを作成したかご説明しました!

品質廻戦 :【ベテラン(製品) x ベテラン(QAエンジニア)~テスト設計の終わりなき戦い~】 by 高木信吾

家登さんからのバトンタッチを受けて、次は高木さんの発表です。

弥生で25年もの間、品質保証を行っている、ベテラン中のベテランの高木さん。その長い歴史において、どのような道を歩んできたのかについて発表しました。

弥生に開発プロセスが導入された後、上流工程にあるバグの温床に気づきその解消ために戦い続けたことや、巨大なサービスの品質保証のため仲間とともに立ち上がったことなど、時代ごとに様々な戦いが繰り広げられてきたことがわかる発表でした。
発表タイトルも含めて要所要所(?)にツッコミポイントが盛り込まれており、ベテランの発表は一味違うなと思われた方がきっと多かったことでしょう…!

QAエンジニアが開発したっていいじゃない by 川本拓也

続いて、川本さんの発表です。

川本さんは、最初の春田さんの発表に出てきたQL(Quality Leader)だけではなく、組織横断的なプロジェクトのQAエンジニアも兼務しています。 今回は、Backlog(タスク管理ツール)関連のツールを開発したことで、何を得られたかについて発表しました。

上のスライドの「Backlogにデータ移行するツール」については、ぜひ 弥生 Advent Calender 2021の記事 もご覧ください。

ツールの開発を通じて開発や詳細設計書への苦手意識がなくなったと語る、川本さん。
検討漏れの事前の検出やエンジニアと円滑なコミュニケーションができるようになり、「攻めのエンジニア」へと変化することができたそうです。

出戻り社員が語る弥生の品質保証リーダーの魅力とは? by 福田柾也

最後の発表は福田さんです。

弥生に中途入社し、社内API開発チームで品質保証の仕事を担当していた福田さん。
その後、一度弥生から離れてプロジェクト管理の仕事をしていましたが、今年の4月に弥生に戻り再び品質保証の仕事を始めました。

今回の発表では、そんな福田さんだからこそわかる弥生の品質保証リーダーの魅力をお伝えしました。

福田さんが魅力に挙げていた「上流から下流工程まで幅広く携われること」、「チームの一員として開発エンジニアと同じ目線に立てること」、「QAエンジニア同士の交流が盛んなこと」は、これまでの発表者の方々も言及していたことですね。

次回&次々回のご案内

最後に、7月と8月に開催予定のイベントのご案内です!

7月14日(木)開催予定:「教えて!テックリード」

7月のもくテクでは、弥生のテックリードが集まって、質問や疑問にお答えします。
質問も受け付けていますので、テックリードのことを知りたい方や聞いてみたいことがある方はぜひご参加ください!

mokuteku.connpass.com

8月18日(木)開催予定:「もう戻りたくない!業務効率化のあれこれ Vol.2」

8月のもくテクは、以前にご好評いただいた「業務効率化」がテーマです!
「繰り返しや時間のかかる作業を減らしたい!」、「少しでも開発を楽にしたい!」とお悩みの方にぜひご覧いただきたいイベントです。

mokuteku.connpass.com

一緒に働く仲間を募集しています

弥生では一緒に働く仲間を募集しています!
「QL(品質保証リーダー候補)」「品質管理」の名称でQAエンジニアを募集をしています。

QL(品質保証リーダー候補)はこちらから。

herp.careers

品質管理こちらから。

herp.careers

QAエンジニア以外のエンジニアも募集しています。 herp.careers

もくテク「『100アカウントを見据えた』AWSマルチアカウント運用」を開催しました

こんにちは、弥生エンジニアの岩佐です。

毎月恒例のもくテク、5月は「『100アカウントを見据えた』AWSマルチアカウント運用」というテーマで開催しました。 イベント当日の様子をご紹介していきます!

5月のテーマ「『100アカウントを見据えた』AWSマルチアカウント運用」

イベントページはこちら(※開催は終了) mokuteku.connpass.com AWS活用に余念がないといっていいほど力を入れている弥生ですが、実際にどのように利用しているのか、現場の声をご紹介しました。

今回のもくテクでは、弥生の実業務でも利用しているAWSマルチアカウントの運用方法をご紹介します。

1つのAWSアカウントに複数のサービスを入れてみたが「これで大丈夫かな?」「サービス増えたら破綻するのでは・・・?」と心配しているエンジニア向けに、シングルアカウントで発生する問題とマルチアカウント化することによる解決方法をお伝えする予定です。 マルチアカウント化にあたり個社ごとに検討しなければいけない課題についてもお伝えしますので、ぜひご参加ください!

セッションの内容

ここからは各セッションの内容をご紹介していきます。 (※発表資料の公開については検討中です)

登壇者紹介

佐々木さんと峯岸さんの自己紹介
今回のイベントは猫好きCTO兼チーフテクニカルリーダーの佐々木さんと犬好きテクニカルリーダーの峯岸さんに登壇していただきました。

弥生のAWSアカウント構成の変遷 by 佐々木

弥生のAWS環境の問題点
まずは佐々木さんから、弥生におけるAWSアカウントの利用方法をどのように改善していったのかをお話ししました。
改善前の問題点や、新環境でクリアしなければならないこと、クリアするための仕組みなどをざっくりとご紹介しました。 新環境の構成図も交えた発表だったので、弥生のAWSアカウントを取り巻く環境がわかりやすかったと思います。

ControlTowerの導入 by 峯岸

ControlTowerについて佐々木さんと峯岸さんのやり取り
ここからは実際にAWSマルチアカウント構築の作業を行った峯岸さんの発表です。
シングルアカウント環境からマルチアカウント環境に切り替えるにあたって、ControlTowerや関連サービスを導入してどのようなアカウント構成にしていったのかご紹介しました。 実際の運用の様子も交えて、弥生でどのように活用しているかご紹介したので、皆さんの環境をマルチアカウント環境にする際にも役立つ内容だったのではないでしょうか。

AWSマルチアカウント環境のガバナンス & セキュリティ強化 by 峯岸

不正利用を監視するためのダッシュボード構成
10分間の休憩をはさみ、引き続き峯岸さんの発表です。
ガバナンス & セキュリティにクローズアップして、マルチアカウント環境特有の問題や解決方法をご紹介しました。 峯岸さんが実際にハマってしまったポイントも紹介していただきました。「あるある~」と思われた方もいらっしゃるかもしれません。 峯岸さんのこれまでの努力を垣間見ることができ、日々の苦労が伝わってくる発表でした。

Q&Aコーナー

今回のもくテクでは参加者の皆さまからのご質問にお答えするQ&Aコーナーを用意しました。 そのときにいただいたご質問を一部ご紹介します。

Q.AWSマルチアカウントの際に、権限を開発チームに渡してトラブルなど起きなかったですか?

A.セキュリティリスクが発生する可能性がある設定ミスが発生したりしました。しかし、セキュリティガードレールのおかげで自動検出&自動削除されています。

Q.シングルアカウントからマルチアカウントにして、コストが高くなったりしないですか?

A.製品サービス単体のAWS利用料金という意味だとさほど変わっていないです。AWSは従量課金が基本なので、1アカウントで100使っても10アカウントで10ずつ使ってもほとんど変わらないからです。ただ、セキュリティ監視の仕組みとかSIEMとか、その辺は過去の環境よりも追加で構築しているので、その辺のコストは増えていますね。
(ただ、誰が使っているんだがわからないリソースとかが無くなった分、利用料はとんとんかもしれない)

Q.前半パートでお話されていた、1日8時間×8日間の教育イベントってすごいですね。会社として、どうやってそのような時間とコストを確保し実現できたのでしょうか?

A.教育に投資することで将来のコストを削減することができるとの考えがあるので、様々な予定を調整して工数を捻出しました。
8時間×8回×100名なので凄い工数ですよね。ダメもとで申請したのですがサクッと承認されて私自身(佐々木さん)もびっくりしました。

こちらの教育イベントについては以前もくテクでも取り上げました。

2021年 もくテクを再開し、月1回の開催を継続しました! - 弥生開発者ブログ

今年は去年よりもパワーアップして開催するとかしないとか。楽しみですね!
参加してみたい!と思った方!今ならまだ間に合うかもしれません。弥生で一緒に働きませんか?

次回&次々回のご案内

6月のもくテク

次回6月は『弥生品質を支えるQAエンジニアの取り組み』というテーマで開催します。
弥生の製品・サービスの品質を支えているQAエンジニア達の取組みをご紹介します。

ご興味ある方はぜひご参加を! mokuteku.connpass.com

7月のもくテク

さらに、次々回7月は『教えて!テックリード』というテーマで開催します。
弥生のいろんな開発チームのテックリードに集まってもらい、質問や疑問に答えます!
参加者からの質問も受け付けていますので、イベント申し込み時のアンケートにぜひご記入下さい!(できる限り座談会で取り上げる予定ですが、数が多い場合は全てにお答えできない場合もありますのでご了承下さい。) mokuteku.connpass.com

一緒に働く仲間を募集しています

www.yayoi-kk.co.jp

herp.careers

初心者エンジニアが機械学習の研究開発をする中で思ったこと

※本記事は旧ブログからの転載記事です(過去投稿日:2020/12/19)

はじめまして。SMARTチームエンジニアの鍋谷と申します。

今回は機械学習の研究開発業務を行ってきた中で、初心者エンジニアの私が感じた点を書きます。

はじめに

まずは簡単に自己紹介します。

  • 2019年4月 弥生に入社・・・開発ほぼ未経験で入社
  • 2019年6月 開発本部に配属・・・開発研修を受ける
  • 2019年11月 テスト自動化チームに配属・・・自動テストの開発・運用を行う
  • 2020年4月 SMARTチームに異動・・・機械学習の研究開発業務を行う

機械学習の特徴と、研究開発の重要性

普通プログラムで何かの判断を行いたい!と思ったら、以下のような流れを考えます。 しかし、ルールを事前にはっきりと形にできないものもありますよね。
そういう場合、仮に最初にルールを決めたとしても、誤って判断されてしまうことが大半でしょう。

機械学習は、「正しい入出力をペアとしたデータをもとに、判断ルールを自動で改善していく手法」です。1
つまり、判断をしてきた経験をもとに、どんどんとルールが改善されていくわけです。 このルール改善の過程を、「学習」と呼びます。

学習の結果どのようなルールができるかは、学習の際に用いるデータや、学習方法の設定などによって異なります。
そして、どのようなときにどのようなルールとなるかを、 一度も学習させずに予測することは難しいです。
つまり、「最終的にどれだけ成果が出るか」は、実際にモデルを動かしていかなければわかりません
これは普通のプログラムとは異なる、機械学習の大きな特徴です。

とはいえ、どれだけ成果が出るのか全くわからない状態では、会社としてプロジェクトに大きく投資することは難しいでしょう。
だからこそ、実運用するプログラムを作る前に、研究開発でモデルを動かし試行錯誤することが、機械学習のプロジェクトでは特に重要であると考えます。
実運用の状況を想定して研究開発ができていれば、その知見を活かして成果をある程度予測できるでしょう。
これが、私の考える研究開発の重要性です。

研究開発をするために大事なこと

「何を実現したいか」を明確にする

これは機械学習以外のプロジェクトについてもいえることですが、 特に機械学習の研究開発をする上で、「何を実現したいか」を明確にすることは非常に大事だと感じました。 理由は以下です。

「何を実現したいか」で用いる手法が変わる

一口に「機械学習」といっても、古今東西いろいろな手法があります。 そしてこれらの手法はそれぞれに特徴があります。
プロジェクトで何を重視するのかがわかっていないと、最適な手法を選ぶことができません。

たとえば、単純な線形回帰でそれなりの正解率が出る問題について、それなりに正解率が高ければいい、運用コストは抑えたいという場合を考えます。
この場合に、非線形なディープニューラルネットワークをGPU環境で学習してモデルを作成する......といった手法を選択するのは、オーバースペックで高コストとなってしまいます。

このように、「何を実現したいか」を把握してタスクの性質を捉えたうえで、使用する技術を選定することが大事です。

「何を実現したいか」で評価指標が変わる

機械学習の研究開発では、複数のモデルを用意して性能を比較していきます。
このとき、単純に「正解率」をモデルの評価指標とすればいいかというと、必ずしもそうではありません。

単純な二値分類について、予測結果と実際の分類結果をある区分に属するかどうかで整理すると、以下の図のようになります。 この図において、評価指標は以下のようにいろいろあります。

  • 正解率・・・すべての判定に対する真陽性と真陰性の割合
  • 再現率・・・実際の陽性のうち真陽性として検出できたものの割合
  • 適合率・・・陽性と判定したもののうち真陽性だったものの割合

そして、最適な評価指標は場合によって異なります。

たとえば「プログラムでコロナウイルスの感染者かどうかを一次予測したい」「陽性の可能性が少しでもある人を逃したくない」という場合であれば、 評価指標として「再現率」を重視します。

一方で、「会計システムで勘定科目を予測したい」「誤った勘定科目には分類したくない」という場合であれば、評価指標として「適合率」を重視します。

このように、ものによって最適な評価の指標は変わります。2
だからこそ、「何を実現したいか」を明確にし、どのような指標で判断すべきなのかを考えなければなりません。

技術動向に常にアンテナを張っておく

機械学習分野は最近ホットな話題ということもあり、非常に研究成果のアップデートが早いです。
技術選定をした2年後にもっといい技術が出た...ということも多々あります。
このため、最新の技術動向がどうなっているかを追っていないと、そのときに最適な手法の選択ができません。

弥生では大学の先生とディスカッションや機械学習に関する勉強会への参加など、 このような技術動向を知る機会をいただいています。これはかなり恵まれていますし、勉強のモチベーションも上がります。

機械学習以外についても知る

運用に乗せるための研究開発をする中では、「機械学習以外の知識」も必要です。

たとえば、モデルの性能比較においては、測定誤差を踏まえて考察を行うなど、統計の知識を持って取り組む必要があります。
また、運用環境の検討では、メモリやCPUなどのリソースの使用状況を監視することが状況改善の考察につながります。
さらに、クラウド上で動かすということであれば、クラウドコンピューティングの知識も必要となります。
機械学習を実際に動かすシステムの全体像がどのようになっているかを知っておくことも、いわずもがな重要です。

このように、「機械学習の研究開発」をするといっても、実際の運用を考えるためには機械学習以外の知識が必要です。
機械学習の知識とエンジニアとしての知識の両方をもっと勉強して、頑張っていきたいです。

さいごに

研究開発は新しく覚えることも考えることも多く、大変刺激的で楽しい業務です。
興味を持っていただけるかたは、採用ページを是非ご覧ください。 www.yayoi-kk.co.jp


  1. 厳密に言えばこれは機械学習の中でも教師あり学習の定義です。

  2. 余談ですが、再現率と適合率のどちらも同じくらい重要という場合は、これらの調和平均をとったF値という指標がよく用いられます。

Spring Batchをジョブの完了を待機して安全に終了する方法

こんにちは、情報システム部エンジニアの飯田です。昨年のAdvent Calendarでは技術イベントの話を書きましたが、普段はお客さまの契約や課金、請求などを管理するシステム(以降、「課金システム」と呼びます)の設計から開発、保守・運用まで幅広く担当しています。

今回はその課金システムで使用しているSpring Batchについて書いていきたいと思います。Spring Batchを知らない人でもできるだけシンプルなサンプルコードで概要が分かるようにしていきますのでぜひ読んでみて下さい!

Spring Batchとは?

Javaを使っている多くの人が知っているSpring Frameworkのモジュールの1つで、Batchという名の通り大量のデータを処理するための機能が提供されています。Spring MVCやSpring Securityなどと比べるとマイナーな部類だと思うので、実際に触ったことのある人は意外と少ないのではないでしょうか。(私も今のチームに来るまで使ったことがありませんでした。)

バッチ処理と書くと馴染みがないかも知れませんが、「予め用意された大量のジョブを適切に管理・実行して最後まで処理してくれる」ものだと思ってもらえば大丈夫です。

課金システムでのSpring Batchの使い方

課金システムでもバッチ処理のためにSpring Batchを使っていますが、大量ジョブの実行の他に、お客さまの操作によって随時発生するジョブの処理にも利用しています。Spring Batchは通常、全てのジョブが完了したら自動的に終了しますが、後に説明する方法を用いることで終了せずに他のジョブを待ち構えるようにできます。

ただそうすると、デプロイなどでシステムを終了する際に実行中のジョブを中断してしまう可能性が出てきます。課金システムは内部・外部の様々なサービスやシステムと連携しているので、処理が中途半端だとお客さまに影響が出ますし、復旧作業も結構大変です。そこで導入したのがタイトルにある「ジョブの完了を待機して安全に終了する方法」となります。

Spring Batchに入門してみよう!

それでは実際にSpring Batchの簡単なアプリを書いてみましょう。

プロジェクトのひな型はSTS(Spring Tool Suite。Spring向けにカスタマイズされたEclipseベースのIDE)の「New Spring Starter Project」またはウェブ上の「Spring Initializr」から簡単に作成できます。今回はsample-batchという名前のGradleプロジェクトにして、依存ライブラリにSpring BatchとLombok、H2 Databaseを選択します。

SampleBatchApplication.javaというクラスが生成されていますがこれには触らず、Spring Batchの設定を行うJava Configのクラスを作成します。

Spring Batchのステップとジョブの定義

@Configuration
@EnableBatchProcessing  // 1. Spring Batchの機能を有効にする
@RequiredArgsConstructor
public class SampleBatchConfig {

  private final JobBuilderFactory jobs;
  private final StepBuilderFactory steps;
  
  @Bean
  protected Step step1(Tasklet tasklet1) {
    return steps.get("step1").tasklet(tasklet1).build();  // 2. ステップを定義する
  }
  
  @Bean
  protected Step step2(Tasklet tasklet2) {
    return steps.get("step2").tasklet(tasklet2).build();
  }
  
  @Bean
  protected Job sampleJob(Step step1, Step step2) {
    return jobs.get("sampleJob").incrementer(new RunIdIncrementer())
        .start(step1).next(step2).build();  // 3. ジョブを定義する
  }
}
  1. @EnableBatchProcessingをどこかのクラスに書けばSpring Batchの機能が有効になり、内部で色んなコンポーネントを生成してくれます。
  2. 生成されたコンポーネントの中のStepBuilderFactoryを使って「step1」と「step2」の2つのステップを定義します。
  3. 同じく生成されたJobBuilderFactoryを使って「step1」と「step2」を順番に実行する「sampleJob」というジョブを定義します。

ジョブ定義のincrementer(new RunIdIncrementer())は実行するジョブのIDを設定するもので、詳しくは後のサンプルで説明します。

@RequiredArgsConstructorはLombokのアノテーションで、finalフィールドをセットするデフォルトコンストラクタを自動生成してくれます。さらにSpringの機能でシステムの起動時にフレームワークで生成されたコンポーネントを自動で設定(DI)してくれます。実装量が少なくなって嬉しいですね!

ステップの処理の実装

@Component
@Slf4j
public class Tasklet1 implements Tasklet {  // 1. Taskletを実装する

  @Override
  public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext)
      throws Exception {
    log.info("tasklet1が実行されました。");  // 2. ステップの処理を記述する
    Thread.sleep(3 * 1000);
    return RepeatStatus.FINISHED;
  }
}
  1. 簡単な処理であればTaskletというインタフェースを使います(これをタスクレットモデルと呼び、より複雑な処理にはチャンクモデルを使いますが、今回は説明を省きます)。
  2. execute()にステップの処理を記述し、最後にRepeatStatusを返します。今回はログ出力とスリープ処理だけを行い、完了を表すRepeatStatus.FINISHEDを返しています。

Tasklet2も同様に用意して、これでもう必要最小限の実装は完了です!早速実行してみましょう。

Job: [SimpleJob: [name=sampleJob]] launched with the following parameters: [{run.id=1}]
Executing step: [step1]
tasklet1が実行されました。
Step: [step1] executed in 3s7ms
Executing step: [step2]
tasklet2が実行されました。
Step: [step2] executed in 3s3ms
Job: [SimpleJob: [name=sampleJob]] completed with the following parameters: [{run.id=1}] and the following status: [COMPLETED] in 6s69ms

この通り、Spring Batchが起動してsampleJobを実行し、step1とstep2が順番に処理されました。ようこそSpring Batchの世界へ!

ジョブをスケジュール実行してみよう!

上の例ではジョブが完了するとSpring Batchが自動で終了しますが、Springのスケジュール機能を有効にするとアプリを終了せずに定期的にジョブを実行できるようになります。課金システムではこれを利用して毎日決まった時間に特定の処理を行ったり他のシステムからリクエストされたジョブを随時実行したりしています。

それではスケジュール実行を試してみましょう。まず次のクラスを追加します。

スケジュール処理の実装

@Component
@EnableScheduling  // 1. スケジュール機能を有効にする
@RequiredArgsConstructor
public class SampleBatchLauncher {

  private final JobLauncher jobLauncher;
  private final Job sampleJob;
  
  private JobParameters jobParameters;
  
  @Scheduled(cron = "*/5 * * * * *")  // 2. スケジュール処理を実装する
  public void launchSampleJob() throws JobExecutionException {
    this.jobParameters = sampleJob.getJobParametersIncrementer()
        .getNext(this.jobParameters);  // 3. ジョブのパラメータを更新する
    this.jobLauncher.run(this.sampleJob, this.jobParameters);  // 4. ジョブを実行する
  }
}
  1. @EnableSchedulingを書けばスケジュール機能が有効になります。
  2. スケジュール機能を有効にすると@Scheduledの付いたメソッドが指定したスケジュールで実行されるようになります。サンプルでは5秒おきに実行するようにしています。
  3. ジョブの実行に必要なJobParametersを作成します。ここでジョブ定義の時に設定したincrementerが登場し、以下の図のように前回のJobParameter(初回はnull)を渡すとrun.idを1増やしたパラメータを生成してくれます。Spring Batchではパラメータが全く同じジョブは同一と見なされて実行をスキップするので、このようにIDを設定します。
  4. ジョブとパラメータを指定してジョブを実行します。

ジョブ実行の設定変更

spring:
  batch:
    job:
      enabled: false

最初のサンプルではJobLauncherを使わなくてもジョブが実行されていましたが、これはデフォルトだと@Beanで生成されたジョブが実行されるようになっているためです。application.yml(またはapplication.properties)で実行しないようにできるので設定しておきましょう。

これでスケジュール実行の準備が整いました!実行するとrun.idがインクリメントしながらジョブが定期的に処理されている様子が分かります。

新しいジョブを実行します。{run.id=1}
Job: [SimpleJob: [name=sampleJob]] launched with the following parameters: [{run.id=1}]
Executing step: [step1]
tasklet1が実行されました。
Step: [step1] executed in 3s25ms
Executing step: [step2]
tasklet2が実行されました。
Step: [step2] executed in 3s7ms
Job: [SimpleJob: [name=sampleJob]] completed with the following parameters: [{run.id=1}] and the following status: [COMPLETED] in 6s55ms
新しいジョブを実行します。{run.id=2}
Job: [SimpleJob: [name=sampleJob]] launched with the following parameters: [{run.id=2}]
Executing step: [step1]
tasklet1が実行されました。
Step: [step1] executed in 3s26ms
Executing step: [step2]
tasklet2が実行されました。
Step: [step2] executed in 3s5ms
Job: [SimpleJob: [name=sampleJob]] completed with the following parameters: [{run.id=2}] and the following status: [COMPLETED] in 6s51ms

デフォルトだとジョブの実行は同期処理なので、このサンプルだと前のジョブが完了してから5秒後に次のジョブが実行されます。非同期処理にすることもできますが、設定ではなく実装を修正する必要があります。

ジョブの完了を待機したい!

前置きが長くなりましたが、ここからが今回の記事の本題です。スケジュール実行によりSpring Batchが稼働し続けるようになったので終了は手動ですることになりますが、もしこの時にジョブが実行中だった場合、以下のように実行途中で異常終了してしまいます!

Thread interrupted while locking for repository update
HikariPool-1 - Shutdown initiated...
HikariPool-1 - Shutdown completed.
Encountered an error executing step step2 in job sampleJob

java.lang.InterruptedException: sleep interrupted

これでは困るのでジョブが完了するまで待機するようにできないか調べたのですが、どうやらSpring Batchではそのような機能はないようです。通常は処理完了後に自動で終了するものなので仕方ありませんが、どうにかして待機を実現したかったので独自に実装することにしました。

終了処理の実装

@Component
@RequiredArgsConstructor
@Slf4j
public class SampleBatchShutdownHandler {
  
  private final JobExplorer jobExplorer;

  @EventListener  // 1. イベントを受け取るメソッドを定義する
  public void shutdown(ContextClosedEvent event) throws InterruptedException {
    log.info("実行中のジョブを確認します。");
    for (int count = 0; count <= 30; count++) {
      Set<JobExecution> runningJobs = this.jobExplorer
          .findRunningJobExecutions("sampleJob");  // 2. 実行中のジョブを取得する
      log.info(runningJobs.size() + "個のジョブが実行中です。");
      if (runningJobs.size() > 0) {
        if (count == 30) {
          log.warn("実行完了待ちがタイムアウトしました。");
          break;
        }
        Thread.sleep(1 * 1000);  // 3. 実行中のジョブがなくなるまで待機する
      } else {
        break;
      }
    }
    log.info("アプリケーションを終了します。");
  }
}
  1. @EventListenerを付与したメソッドでContextClosedEventを受け取るようにすると、アプリケーションが終了する直前(正確にはApplicationContextが閉じられた時)に呼び出してくれるようになります。
  2. Spring Batchが生成したJobExplorerがジョブの検索機能を持っており、実行中のジョブを取得することができます。
  3. 実行中のジョブがあれば待機(最大30秒)し、なければそのままメソッドを抜けてアプリケーションの終了に移ります。

このように、Springのイベントの仕組みを利用してジョブの完了を待機する処理を書いてみました。これでジョブの実行中に終了してみると…。

新しいジョブを実行します。{run.id=1}
Job: [SimpleJob: [name=sampleJob]] launched with the following parameters: [{run.id=1}]
Executing step: [step1]
tasklet1が実行されました。
Step: [step1] executed in 3s23ms
Executing step: [step2]
tasklet2が実行されました。
Application shutdown requested.
実行中のジョブを確認します。
1個のジョブが実行中です。
1個のジョブが実行中です。
Step: [step2] executed in 3s6ms
Job: [SimpleJob: [name=sampleJob]] completed with the following parameters: [{run.id=1}] and the following status: [COMPLETED] in 6s50ms
0個のジョブが実行中です。
アプリケーションを終了します。
HikariPool-1 - Shutdown initiated...
HikariPool-1 - Shutdown completed.

この通り、実行中のジョブがなくなるまで待機し、安全に終了できるようになりました!

フレームワークを使うと実装量を減らせたり意識することが少なくなったりして便利ですが、基本的な使い方から少し外れると独自で対応の必要な部分が出てくるかも知れません。その場合はフレームワークに対する理解が問われますので、仕組みを把握して適切な実装をしたいところですね。それでは今回はこの辺で!

あとがき

実は今回の実装は、最初は@EventListenerではなく@PreDestroyを使っていました。@PreDestroyはそのbeanが破棄される時に呼び出されるので本来は他のジョブの待機には間に合わないのですが、古いバージョンのSpring Batchを使っていた時には何故かそれで問題なく動作していたのです。

それがバージョンアップしたら動作しなくなったので、「どこかで仕様が変わったのだろうか?これは技術ブログのネタにできるかも!」と思い、自宅で古いバージョンの環境を用意して再現を試みました。STSやGradleのバージョンも落とす必要があるので、何気に古い環境を作るのって手間が掛かるんですよね。

で、再現させた結果、古いバージョンでも@PreDestroyでは動作しませんでした(笑)つまり原因不明。おそらく他のコンポーネントとの参照関係などによりたまたま最初の方に呼び出されてジョブ監視できていたのだと考えています。

今考えれば@PreDestroyは不適切だと思いますが、調査している時には案外気付けないもの。なので皆さん、フレームワークへの理解は深めておきましょう!

お知らせ

弥生では一緒に働く仲間を募集しています! herp.careers

GitHub Actionsでterraform planするworking directoryを動的に抽出する

システム開発部Misocaチームエンジニアの mizukmb です。

TerraformのCIをGitHub Actionsで実装する際に工夫した時の話を紹介します。

GitHub ActionsでCIしたいけどworkflowをどうやって書けばいいの?

TerraformコードをGitHubレポジトリで管理する場合、1 GitHubレポジトリ = 1 Terraform working directory よりも 1 GitHubレポジトリ = 複数Terraform working directory といったmonorepo構成になる事が多いと思います。

例えば以下のようなproduction環境とstaging環境それぞれのworking directoryとそれらから利用されるterraform moduleを1つのGitHubレポジトリで管理するといったケースです。

❯ tree terraform-repo                         
terraform-repo
├── modules
│   ├── ecs
│   │   ├── main.tf
│   │   ├── output.tf
│   │   └── variables.tf
│   ├── iam
│   └── vpc
├── production
│   ├── app
│   │   ├── main.tf
│   │   ├── output.tf
│   │   └── variables.tf
│   └── vpc
└── staging
    ├── review_app
    ├── staging_app
    │   ├── main.tf
    │   ├── output.tf
    │   └── variables.tf
    └── vpc

11 directories, 9 files

こうしたmonorepo構成で terraform plan 等の実行を想定したCIを構築したい場合に困りがちなのは どのworking directoryが対象であるか の判別が難しいという事です。

上の例の場合だと、 production/app/main.tf を編集したGitHub Pull Request (以降、PR) では production/app だけ terraform plan できれば十分ですし、 production/vpcstaging/vpc を編集したPRでは両方で terraform plan してほしいです。

さらに modules/ 以下のファイルを編集した場合は、 そのモジュールを利用しているworking directoryが対象になり 、単なるファイル変更があったディレクトリで terraform plan を実行すれば良いという話ではなくなってきます。

こうした要件を満たしたGitHub Actions worflowをどのように作成すればよいかを次の章で説明します。

terraform plan対象のworking directoryを抽出する

tj-actions/changed-files でPRの変更ファイル一覧を取得し、k1LoW/github-script-rubyを使ってファイルから terraform plan 対象のworking directoryを抽出するRubyスクリプトを実行し、outputsとして後続のjobに渡すといった解決策をとりました。

working directoryの抽出jobは actions/github-script でも同じ事ができますが、開発メンバーが普段からRuby on Railsを使った開発を行っているという背景から k1LoW/github-script-ruby を使う事にしました。

jobs以下はこのように書いてます。

jobs:
  setup:
    runs-on: ubuntu-latest
    outputs:
      targets: ${{ steps.output-targets.outputs.targets }}
      plan_targets: ${{ steps.output-targets.outputs.plan_targets }}
    steps:
      - uses: actions/checkout@v2
      - name: Get changed files
        id: changed-files
        uses: tj-actions/changed-files@v18.6
      - name: Output target dirs
        id: output-targets
        uses: k1LoW/github-script-ruby@v2
        with:
          script: |
            # steps.output-targets.outputs.targets
            #   PRの変更したファイルから `valid_dirs` に含まれるファイルのみを抽出し、outputs.targetsを作成する
            # steps.output-targets.outputs.plan_targets
            #   outputs.targesからterraform plan対象のディレクトリを抽出し、outputs.plan_targetsを作成する
            require 'json'
            valid_dirs = [
              'production/app',
              'production/vpc',
              'staging/staging_app',
              'staging/review_app',
              'staging/vpc',
              'modules/ecs',
              'modules/iam',
              'modules/vpc
            ]
            files = '${{ steps.changed-files.outputs.all_changed_files }}'.split
            targets = valid_dirs.select { |v| files.select { |d| d.match?(v) }.size > 0 }
            plan_targets = targets.map do |t|
              # modules以下を変更した場合は参照してるworking directoryをplan_targetsの対象とする
              case t
              when 'modules/ecs', 'modules/iam'
                ['production/app', 'staging/staging_app', 'staging/review_app']
              when 'modules/vpc'
                ['production/vpc', 'staging/vpc']
              else
                t
              end
            end.flatten.uniq
            puts "Target dirs: #{targets}"
            puts "Plan target dirs: #{plan_targets}"
            core.set_output('targets', targets.to_json)
            core.set_output('plan_targets', plan_targets.to_json)

  terraform_fmt:
    runs-on: ubuntu-latest
    needs: setup
    strategy:
      matrix:
        target: ${{ fromJSON(needs.setup.outputs.targets) }}
    steps:
    - uses: actions/checkout@v2
    - name: Setup terraform
      uses: hashicorp/setup-terraform@v1
      with:
        terraform_version: ${{ env.TERRAFORM_VERSION }}
    - run: terraform fmt -recursive -diff -check ${{ matrix.target }}

  terraform_plan:
    runs-on: ubuntu-latest
    needs: setup
    defaults:
      run:
        working-directory: ${{ matrix.target }}
    strategy:
      matrix:
        target: ${{ fromJSON(needs.setup.outputs.plan_targets) }}
    steps:
    - uses: actions/checkout@v2
    - uses: aws-actions/configure-aws-credentials@master
      with:
        role-to-assume: ${{ env.AWS_ROLE_FOR_PLAN_ARN }}
        aws-region: ap-northeast-1
    - uses: hashicorp/setup-terraform@v1
      with:
        terraform_version: ${{ env.TERRAFORM_VERSION }}
    - name: Setup github-comment, tfcmt
      run: |
           curl -sL -o github-comment.tar.gz https://github.com/suzuki-shunsuke/github-comment/releases/download/v${{ env.GITHUB_COMMENT_VERSION }}/github-comment_${{ env.GITHUB_COMMENT_VERSION }}_linux_amd64.tar.gz
           sudo tar -C /usr/bin -zxvf github-comment.tar.gz
           curl -sL -o tfcmt.tar.gz https://github.com/suzuki-shunsuke/tfcmt/releases/download/v${{ env.TFCMT_VERSION }}/tfcmt_linux_amd64.tar.gz
           sudo tar -C /usr/bin -zxvf tfcmt.tar.gz
    - name: Run terraform plan
      run: |
           terraform init
           tfcmt plan -- terraform plan -no-color
           github-comment hide
      continue-on-error: true
      env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

jobs.setup

このjobの目的は、 terraform planterraform fmt といった本来CIでやりたい事の前処理として、対象working directoryを抽出しoutputsを設定、そして後続のjobに渡す事です。outputsには targetsplan_targets の2つを設定します。それぞれの役割は以下の通りです。

  • targets
    • modules/ 含むworking directory一覧
    • fmtやtflintで使う事を想定
  • plan_targets
    • modules/ の変更があった場合、利用しているworking directoryのみを抽出
    • なので modules/ はこちらに含まれない
    • planやapplyで使う事を想定

stepsですが、まず tj-actions/changed-files でPRの変更ファイルの一覧を取得します。outputsは勝手に設定されるので uses で指定するだけで終わります。READMEから使用可能なoutputsを確認できます。

k1LoW/github-script-ruby ではoutputsの targetsplan_targets に渡す値を抽出するRubyスクリプトが書かれています。modulesを利用するworking directoryの動的な抽出が難しそうだったのでcaseを使った分岐を入れています。monorepo内にあるworking directoryの数が多いとしんどくなるかもしれません。

まとめ

GitHub ActionsでPRの変更ファイルから terraform plan の対象となるTerraform working directoryの動的な抽出を行うworkflowの書き方について書きました。これによってplan結果をPR毎に簡単に確認できるようになり、開発体験の向上を感じています。

参考文献

workflowの作成にあたってsuzuki-shunsuke/tfactionの実装や設計思想を参考にしており、強く影響を受けています。説明を省略していましたが、 terraform_fmtterraform_planstrategy.matrix 使うといったアイデアはまさにこちらを参考にしたものです。working_directoryの動的抽出の方法もtfactionに刺激された結果として実装アイデアを思いつく事ができました :)

お知らせ

弥生では一緒に働く仲間を募集しています! herp.careers

もくテク「エンジニア就活生必見! 新卒エンジニアってどうなの?」を開催しました

こんにちは、弥生の相澤です。
毎月恒例のもくテク開催報告です。

イベント概要

今回は就活生や学生に向けたイベントを開催しました。

今回のテーマは『エンジニア就活生必見! 新卒エンジニアってどうなの?』 今回のもくテクは、「弥生が気になっている」という就活生のための弥生開発本部紹介企画! このイベントでは弥生に新卒入社した2年目~5年目のメンバーが登壇し、研修や配属先、今任されている仕事について包み隠さずお話しさせていただきます! 採用選考には一切関係のないカジュアルな会なのでぜひ気軽に参加してください😼

前半にLTで弊社の新卒の経験をお伝えし、後半に参加者の方々から送っていただいた質問をパネルディスカッションで回答するという構成で進めました。

LTの内容

『開発本部紹介』 by 中山 健太

開発本部のリーダーが本部についてをご紹介しました。
組織の取り組みから、キャリアパス、成長支援についてなどを熱い思いとちょっとの笑いを交えてお話しました。
例えば、開発本部のキャリアパス例としてエンジニアからテックリード、プロジェクトマネージャー、品質リーダーなど様々なキャリアを選択できます。キャリアアップを支援するため、新卒の社員には入社直後プログラミング研修があったり、新卒・中途問わず入社後定期的に技術研修やマネジメント研修などを受ける機会があります。
他にも1on1という定期的に上司と1対1で話す時間や、本部全大会という本部員全員が集まる会で各チーム/人の取り組みをLTとして発表する時間があったりと、様々な取り組みによってエンジニアの活躍や成長が支えられています。

『研修って大変なの?新卒技術研修Q&A』 by 田邊 慎史

新卒2年目の社員が開発未経験だった過去の自分に向けて、研修を受けた経験や受ける際のポイントについてQ&A形式でお話しました。
弊社では最初から学ぶ前提で研修などの計画が組まれているため入社後からのスタートでも大丈夫なこと、講師や先輩など周囲のサポートを活かせることなどを発表しました。

『知識の無い新卒が新サービスを作るチームに参画できた物語』 by 増田

新卒3年目の社員が新サービスチームに参画したいという希望を伝えてからの経験についてお話しました。
希望通りのチームに参画してからは「複数人で実装したい」と仕事の進め方を提案したり、StorybookCodeceptJSなど弊社でまだ使っていなかった新しい技術を調査したりして新しいことに挑戦されています。

『開発未経験でもAPIを実装できる⁉️~弥生スポットライト賞を受賞するまでの取り組み~』 by 松坂 莉奈

入社直後は開発未経だった験新卒4年目の社員が、それから新卒2年目で社内賞を受賞するまでに至った経験についてお話ししました。
10個以上のAPIをGo言語で作成するために、
①まずは既にあるものを理解しようとする
②そこで出た疑問はすべて解消する
③やるべきことを明確にする
④自力で調べる力を身に着ける
というステップを踏んで腕を磨いていったことを発表しました。

『「上司」って激ムズ…!新卒4年目サブリーダーの葛藤と感謝』 by 野川

新卒5年目の社員が人事ライン(※)というチームのサブリーダーになった経験と、そこで気づいた上司として大切なポイントやチームメンバーの声をお話しました。
 ※開発本部では物を作るファンクションというチームと、勤怠管理・評価・メンタルケアなどの人を見る人事ラインというチームが存在します。今回は人事ラインチームのサブリーダーについての内容になります。
・完璧じゃなくていい
・信頼関係が何より大事
・部下の話を"聴く"
・Iメッセージで伝える
ということを意識してメンバーをサポートし、チームメンバーから「本音で話すことができ課題の解決に近づけた」「近い目標として自分のキャリアを考える際にも助けられている」という声をいただいています。

パネルディスカッションの内容

司会は開発本部紹介をしたリーダーが、パネラーは新卒2~5年目のLT登壇者と運営司会者が登壇しました。
ありがたいことに当日前や開催中も参加者の方からご質問をいただきました。
ここで本番で回答できなかった質問をこちらの記事で回答させていただこうと思います。

質問内容

エンジニア就活の時期感(インターン、ES、内々定)、持っておいた方がいい能力/資格/実績、就活始めるまでどういう心持で生活すればいいか

回答

エンジニア就活の時期感(インターン、ES、内々定)について

FY22については11月頃からエントリーを受けつけ、内々定は年明けくらいから出しています。
また、現状インターンは行っておりません。

持っておいた方がいい能力/資格/実績

能力、資格は特にありません。
実績は下記のとおりです。(※必須ではなく、あると歓迎、という意味合いです)
・自ら目標を立て、やり切った経験
・チームを巻き込み、成果をあげた経験
・社会に対して何かしら課題意識を持っており、貢献したいと思っている

就活始めるまでどういう心持で生活すればいいか

私個人の考えですが、勉強やサークル活動、バイトなど興味があることにどんどん打ち込めば良いと思います。
また、そうやって打ち込んできた事柄を周りの人に話したり何かにまとめたりして自分の言葉でたくさん伝えてみると、就活での引き出しが増えて、結果的に自分に合った会社が見つかると思います。

次回のご案内

次回は『『100アカウントを見据えた』AWSマルチアカウント運用』というテーマで開催します。

今回のもくテクでは、弥生の実業務でも利用しているAWSマルチアカウントの運用方法をご紹介します。 1つのAWSアカウントに複数のサービスを入れてみたが「これで大丈夫かな?」「サービス増えたら破綻するのでは・・・?」と心配しているエンジニア向けに、シングルアカウントで発生する問題とマルチアカウント化することによる解決方法をお伝えする予定です。 マルチアカウント化にあたり個社ごとに検討しなければいけない課題についてもお伝えしますので、ぜひご参加ください!

AWSのアカウント運用についてがっつりお話しさせていただきます。
ご興味のある方はぜひご参加ください。

mokuteku.connpass.com

一緒に働く仲間を募集しています

www.yayoi-kk.co.jp

herp.careers

もくテク「freee×弥生!ユーザーの喜びの声を愛でる会」を開催しました

f:id:issi-y:20220328130855j:plain

こんにちは、弥生のいっしーです。

すっかり春ですね。先週末は近所の公園で桜を愛でてきました。
リモートワークはついつい運動不足になりがちなので、季節を感じるイベントは心も身体もリフレッシュできておススメです。

実は3月は、お客さまの喜びの声を愛でる機会もありました。
こちらもとっっっても良かったので、当日の様子をご紹介します。

3月のもくテクは「freee×弥生!ユーザーの喜びの声を愛でる会」

今回はなんと、普段は競合として見られがちなfreee株式会社さんとのコラボイベントでした!

今回のイベントでは、freeeと弥生それぞれから会計ソフトの開発に携わるエンジニアが登壇し、お互いの会社や製品・サービスの開発についてトークします。
トークの中でユーザーの方からの「喜びの声」をご紹介し、会計をはじめとするバックオフィス業務向けのソフト開発の楽しさを全力でお伝えする予定です!
会社の壁を越えて開発の楽しさ、やりがいを分かち合うこの貴重な機会を、ぜひお見逃しなく!!

YouTube Liveでの限定生放送*1でしたが、多くの方にご視聴いただくことができました。

お互いを探り合う自己紹介

ゲストはfreeeの中部オフィスで開発本部長を務めていらっしゃるmoai(山崎)さん。弥生からはCTOの佐々木と弥生会計オンラインのテクニカルリーダーを務めている狩野が登壇しました。自己紹介の結果、狩野が実は転職活動時にfreeeさんも検討していたことや、moaiさんと佐々木には両社のカジュアル面談で会うことができることが判明。また、両社ともコロナ禍で一気にリモートワークが進んだ話を和気あいあいとしていました。

f:id:issi-y:20220331115306p:plain
画面左上から時計回りにファシリテーターの佐々木(弥生)、moaiさん(freee)、狩野(弥生)

似ている部分、違う部分が見えてきた会社紹介

場も温まってきたところで両社の会社紹介です。
freeeさんも弥生も、スモールビジネスのバックオフィスを支えるプロダクトをリリースしているだけあり、ミッションは非常に似ていて共感できるものでした。「我々のお客さまはどんな人たちでどんな課題を抱えているのか?」という話題についても、お互いに頷くばかり。

両社ともスモールビジネスが世の中に良い影響を与える存在だと考えている点は共通していましたが、弥生は「事業コンシェルジュとして、事業のあらゆるフェーズで発生する困りごとを一緒に解決する」、freeeさんは「統合型経営プラットフォームを提供することで、だれもが自由に自立的に経営できる(経営のハードルを下げる)」というアプローチの違いを感じました。

f:id:issi-y:20220331115237p:plainf:id:issi-y:20220331115322p:plain
弥生の会社紹介

f:id:issi-y:20220331115335p:plainf:id:issi-y:20220331115346p:plain
freeeさんの会社紹介

ユーザーの喜びの声を愛でる

そして、いよいよ今回のメインパートです!
ここからは、お客さまからの喜びの声の多かった機能を開発したメンバーも参加してお話しいただきました。freeeさんからはfreee会計のマネージャーをしているnoripee(加藤)さん、弥生からは弥生会計デスクトップのテクニカルリーダーの寺﨑(柴犬)*2が登壇しました。

それぞれのプロダクトの機能やお客さまの喜びの声を聞きながら、

  • タグとか数年経つと管理が大変になりますよね
  • あー、これはかゆいところに手が届いていいですね
  • この機能、うちもやりたいとは思ってるんですけどできてないんですよ
  • リリースした機能が喜んでもらえると嬉しいですよね
  • お客さまの声はちょくちょくチェックしてます

といった開発者らしい意見交換をされていて、とても楽しそうでした。

freee

freeeさんからは「ワークフローの共有機能」「タグの非表示機能」「試算表・月次推移での検索条件の保存機能」の3つをご紹介いただきました。

f:id:issi-y:20220331115403p:plain
タグの非表示機能

■ユーザーの喜びの声(一部抜粋)

  • ワークフローに共有機能が実装されてて地味にいいやん、って思った
  • 待ってました!!最高です!!
  • 検索条件の保存機能がイイね

弥生

弥生からは「前期データの参照機能」を紹介しました。

f:id:issi-y:20220331115413p:plain
前期データの参照機能

■ユーザーの喜びの声(一部抜粋)

  • 前年度データの参照が楽になった。やるじゃん
  • かなり効率化です
  • 神実装

感想タイム

イベントの最後に、登壇者から今回の感想をいただきました。

  • 普段の業務だとどうしても競合他社という目線で見てしまうが、今回はその目線抜きのポジティブな感じで、お互いの良いところやお客さまの反応について話せたのがよかった
  • 業界を盛り上げていく良きライバルとして、切磋琢磨しながらこれからも一緒に頑張っていけたらと思った
  • ユーザーさんの利用率や声を知れるのはやはり楽しいし嬉しい
  • お客さまにいいものだと思ってもらうだけでなく、自分たち自身もこれはいいものなんだと言える機能開発をしていきたい

f:id:issi-y:20220331115425p:plain
みなさん、いい笑顔!

freeeさん、今回は本当にありがとうございました!
また機会があればぜひコラボさせてください!

次回のご案内

さて、次回は「エンジニア就活生必見! 新卒エンジニアってどうなの?」というテーマで開催します。
就活シーズンに入り、進路に悩まれている方も多いのではないでしょうか?
このイベントでは弥生に新卒入社した2年目~5年目のメンバーが登壇し、研修や配属先、今任されている仕事について包み隠さずお話しします! 選考とは関係ありませんので、弥生の新卒エンジニアの生の声を聞きに、ぜひお気軽にご参加ください。

mokuteku.connpass.com

一緒に働く仲間を募集しています

弥生ではスモールビジネスを一緒に盛り上げていってくれる仲間を募集しています! herp.careers

*1:今回の配信は公開する予定はありません。

*2:「あ、どうも。こんな姿で失礼します」と登場した柴犬に、会場が笑いに包まれたのはここだけの話。