株式会社ラクスのブログ - TECH PLAY

TECH PLAY

株式会社ラクス

株式会社ラクス の技術ブログ

968

はじめに Webアプリケーションにおけるレートリミット、サーキットブレーカー、リトライの役割 リトライ サーキットブレーカー レートリミット レートリミット、サーキットブレーカー、リトライの実装 サンプルアプリケーションの実装 リトライ、サーキットブレーカー、レートリミットを追加 まとめ 年に1度の技術イベント「RAKUS Tech Conference」を開催します!! はじめに こんにちは!エンジニア2年目のTKDSです。 今回は、レートリミット・サーキットブレーカー・リトライについて調べた内容を紹介し、ライブラリを使ってGoで実装してみます。 Webアプリケーションにおけるレートリミット、サーキットブレーカー、リトライの役割 リトライ リク エス トが失敗した場合に再試行します。 リトライは、一時的な障害に対して効果を発揮します。 ネットワークの瞬断やサービスの一時的な過負荷など、やり直せば解決できそうな問題による失敗をシステム内のリトライでカバーすることで、ユーザーからは特に問題なく見える状態を維持したまま処理失敗の リカバリ ーができます。 サーキットブレーカー サービスを監視し、設定した条件がみたされると「オープン(リク エス トを受け付けない)」状態になり、しばらくすると「ハーフオープン(一部だけ受け入れ)」状態になり、システムが回復したかどうか一部のリク エス トを通して確認します。 回復したことが確認できれば「クローズ(通常状態)」に、だめなら「オープン」になります。 サーキットブレーカーは、復旧に時間のかかる障害に対して効果を発揮します。 そのままリトライし続けても回復する見込みが低い、もしくは回復まで時間がかかる場合、一旦アクセスを遮断することで障害が起きたサービスへの無駄なアクセスや無駄なリソースの消費を避けることができ、サービスが回復するまでの時間を稼ぎます。 レートリミット 設定した以上のリク エス トが来たときに、一時的にアクセスを制限します。 レートリミットを使うとサービスに過負荷がかかることを防ぐことができます。 また、 DoS攻撃 などからアプリケーションを守ることができます。 これらの機能を採用することでシステムの耐障害性、安定性、パフォーマンスを向上させられます。 レートリミット、サーキットブレーカー、リトライの実装 この節では、Goで実際にレートリミット、サーキットブレーカー、リトライの機能を実装していきます。 サンプルアプリケーションの実装 今回はライブラリを活用して実装していきます。 自分で1から実装しない理由はいくつかあります。 多くの人が利用しているライブラリはバグが発見されやすく、自分で実装するより信頼性が高い 採用時点でのベストプ ラク ティスを適用できる 複雑な動作の適切な処理を自身で考える必要がない このような観点からライブラリを使います。 以下がサンプルアプリケーションです。 リク エス トパラメータに指定したURLにアクセスし、存在する場合はカウントを1プラスし、ない場合はエラーを返します。 package main import ( "context" "errors" "fmt" "net/http" "os" "os/signal" "syscall" "time" "github.com/go-redis/redis/v8" "golang.org/x/exp/slog" ) var ( rdb *redis.Client ) func init() { rdb = redis.NewClient(&redis.Options{ Addr: "redis:6379" , // Redisサーバーのアドレス Password: "" , // パスワードなし DB: 0 , // デフォルトのDB }) } func hostExists(url string ) bool { resp, err := http.Get(url) if err != nil { return false } defer resp.Body.Close() return resp.StatusCode == http.StatusOK } func handler(w http.ResponseWriter, r *http.Request) { host := r.URL.Query().Get( "host" ) if host == "" { http.Error(w, "Host parameter is missing" , http.StatusBadRequest) return } if hostExists(host) { count, err := rdb.Incr(context.Background(), "counter" ).Result() if err != nil { http.Error(w, "Could not increment counter" , http.StatusInternalServerError) return } fmt.Fprintf(w, "Counter: %d \n " , count) } else { http.Error(w, "Host not found" , http.StatusNotFound) } } func main() { addr := ":8080" handler := http.HandlerFunc(handler) server := &http.Server{Addr: addr, Handler: handler} idleConnsClosed := make ( chan struct {}) go func () { c := make ( chan os.Signal, 1 ) signal.Notify(c, os.Interrupt, syscall.SIGTERM) // SIGINT, SIGTERM を検知する <-c ctx, cancel := context.WithTimeout(context.Background(), 10 *time.Second) defer cancel() slog.Info( "Server is shutting down..." ) if err := server.Shutdown(ctx); err != nil { if errors.Is(err, context.DeadlineExceeded) { slog.Warn( "HTTP server Shutdown: timeout" ) } else { slog.Error( "HTTP server Shutdown: " , err) } close (idleConnsClosed) return } slog.Info( "Server is shut down" ) close (idleConnsClosed) }() slog.Info( "Server is running on " , addr) if err := server.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) { slog.Error( "HTTP server ListenAndServe: " , err) } <-idleConnsClosed } docker compose up --build で起動します。 成功するリク エス トと失敗するリク エス トを送って動作確認をしてみましょう。 成功: curl "http://localhost:8080?host=http://example.com" 失敗: curl "http://localhost:8080?host=http://.com" 下の画像のように動作確認ができます。 リトライ、サーキットブレーカー、レートリミットを追加 サンプルにリトライ、サーキットブレーカー、レートリミットを追加していきます。 コードは以下の通りです。 変更が入った、initとhostExists、handlerだけ記載します。 詳細は Github をみてください。 func init() { // Redisサーバーのアドレスを設定 rdb = redis.NewClient(&redis.Options{ Addr: "redis:6379" , // Redisサーバーのアドレス Password: "" , // パスワードなし DB: 0 , // デフォルトのDB }) cb = gobreaker.NewCircuitBreaker(gobreaker.Settings{ Name: "Redis" , Timeout: 30 * time.Second, ReadyToTrip: func (counts gobreaker.Counts) bool { return counts.ConsecutiveFailures >= 3 // 3回連続失敗でトリップ }, OnStateChange: func (name string , from gobreaker.State, to gobreaker.State) { log.Println( "Circuit breaker state changed" , "name" , name, "from" , from.String(), "to" , to.String()) // サーキットブレーカーの状態が変わるたびにログ出力 }, }) rateLimiter = ratelimit.New( 1 , ratelimit.Per( 10 *time.Second)) // 1リクエスト/秒 client = retryablehttp.NewClient() client.RetryMax = 3 // 最大3回リトライ } func hostExists(url string ) bool { req, err := retryablehttp.NewRequest( "GET" , url, nil ) if err != nil { return false } resp, err := client.Do(req) if err != nil { return false } defer resp.Body.Close() return resp.StatusCode == http.StatusOK } func handler(w http.ResponseWriter, r *http.Request) { // レートリミットの適用 rateLimiter.Take() host := r.URL.Query().Get( "host" ) if host == "" { http.Error(w, "Host parameter is missing" , http.StatusBadRequest) return } if hostExists(host) { // サーキットブレーカーの適用 body, err := cb.Execute( func () ( interface {}, error ) { count, err := rdb.Incr(context.Background(), "counter" ).Result() if err != nil { return nil , err } return fmt.Sprintf( "Counter: %d \n " , count), nil }) if err != nil { http.Error(w, "Could not increment counter" , http.StatusInternalServerError) return } fmt.Fprintf(w, body.( string )) } else { http.Error(w, "Host not found" , http.StatusNotFound) } } では同様に、起動して動作確認していきます。 docker compose down -v してきれいにしておきましょう。 docker compose build --no-cache でビルドし、 docker compose up で起動します。 リトライを起こすリク エス ト リトライはリク エス トが失敗した場合に起こります。 curl "http://localhost:8080?host=http://.com" を実行すると先程と違い、retryのログがサーバー側に出力されていることがわかります。 これでリトライが機能していることが確認できました。 レートリミットを起こすリク エス ト レートリミットの部分を以下のように変更しましょう。 このライブラリでは、レートリミットを超えると指定した時間処理を待機するようになっています。 10秒間に1回のリク エス トに制限してあります。 レートリミットがかかると、リク エス トに10秒かかっていることがわかります。 これで、レートリミットが機能していることが確認できました。 サーキットブレーカーを起動するリク エス ト 今回のサーキットブレーカーは3回連続失敗で、30秒間 タイムアウト するようにしています。 まず、起動しているRedisを止めます。 これでRedisへのアクセスが失敗するようになりました。 サーキットブレーカーの変化がログに出力されています。 三回失敗したあとにサーキットブレーカーの変化が起きています。 これでサーキットブレーカーの動作確認ができました! まとめ 今回はレートリミット・サーキットブレーカー・リトライについて調べた内容を紹介し、ライブラリを使ってGoで実装しました。 ライブラリを使えば比較的簡単に実装できるので、ぜひ実装してみて下さい。 マイクロサービスには必須の機能だと思うので、記憶にとどめて置きたいと思います。 ここまで読んでいただきありがとうございました! 年に1度の技術イベント「RAKUS Tech Conference」を開催します!! 今年も ラク ス開発本部主催の技術カンファレンス、「RAKUS Tech Conference 2024」を開催します! 「RAKUS Tech Conference」は、 SaaS 開発における取り組みや知見を紹介する、 ラク ス開発本部主催の技術カンファレンスです。 ラク ス開発本部のミッションに込めた想いをエンジニア/デザイナーが生の声でお届けします。 皆さまのご参加、お待ちしております! techcon.rakus.co.jp
RAKUS Tech Conference 2024とは? 開催概要 開発本部長メッセージ RAKUS Tech Conference 2024の見どころ 本イベントを視聴・参加するメリット!! 申込特典! タイムテーブル 過去のRAKUS Tech Conference RAKUS Tech Conference 2022 RAKUS Tech Conference 2023 参加者からのフィードバック ご参加お待ちしております! 技術広報のyayawowoです! 今年も ラク ス開発本部主催の技術カンファレンス、「RAKUS Tech Conference 2024」を開催します! techcon.rakus.co.jp RAKUS Tech Conference 2024とは? 「顧客をカスタマーサクセスに導く圧倒的に使いやすい SaaS を創り提供する」 開発本部のミッションに込めた想いをエンジニア/デザイナーが生の声でお届けします。 株式会社 ラク スは「ITサービスで企業の成長を継続的に支援します!」をミッションに掲げ、経費精算システムの「楽楽精算」や、メール共有・管理システムの「メールディーラー」など延べ83,000社を超えるお客様に SaaS サービスを提供してきました。 「RAKUS Tech Conference」は、 SaaS 開発における取り組みや知見を紹介する、 ラク ス開発本部主催の技術カンファレンスです。 開催概要 日時: 2024/8/7(水)14:00-18:00 会場: オンライン(Zoom) ※connpassのメッセージ機能、およびイベントページ内の「参加者への情報」欄にて開催前にURLを通知いたします。 参加費: 無料 主催: ラク ス RAKUS Tech Conference 2024 公式サイト: https://techcon.rakus.co.jp/2024/ ハッシュタグ : #RAKUSTechCon 開発本部長メッセージ ラク ス開発本部は「顧客をカスタマーサクセスに導く圧倒的に使いやすい SaaS を創り提供する」をミッションに掲げています。   私たちは、2000年代初期の SaaS 開発時から徹底して顧客視点を大切にし、顧客のペインポイントを理解し、その解決に向けて様々な取り組みを行ってきました。一方、組織が急拡大する中で、エンジニア一人ひとりの顧客に対する解像度が低下するという課題にも直面しました。   本カンファレンスでは、私たちが直面した困難とその乗り越え方、顧客視点を保つための具体的な取り組みを、CTOやPdM・EM・エンジニア・デザイナーが現場のリアルな声でお届けします。   顧客志向の開発を重視し、真のカスタマーサクセスを目指す皆様に、私たちの知見とインスピレーションを少しでも共有できればと思っています。 RAKUS Tech Conference 2024の見どころ 本イベントを視聴・参加するメリット!! ARR300億円越えの SaaS 開発組織の裏側を知ることができる ラク ス開発本部の戦略や現状の課題、今後のビジョンを知ることができる 急拡大するマルチプロダクト SaaS 開発の知見を持ち帰ることができる 長期プロダクトに携わるエンジニアの働き方が聞ける 開発プロセス 内のデザイナーのあり方を知れる インフラ部門の技術選定の秘訣を知ることができる ラク スは生成AIの活用にどう取り組んでいるか? 申込特典! お申込みの方へ後日、RAKUS Tech Conference 2024の アーカイブ 動画を配信いたします。 ※当日参加できない方も対象となります。 タイムテーブル 開始時間 タイトル 登壇者 14:00 オープニング 14:10 ラク スCTOが語る顧客視点を重視したプロダクト開発 開発本部 本部長 兼 執行役員   公手真之 14:40 マルチプロダクトでのプロダクトマネージャーのリアル 東京開発統括部 製品管理課 課長  稲垣剛之 15:10 拡大するマルチプロダクト SaaS の顧客理解にデザイン組織はどう取り組んでいるか 開発推進部 プロダクトデザイン課 課長  小林肇 開発推進部 プロダクトデザイン課  今村 沙穂理 15:30 急成長する大規模プロダクト開発のマネジメント課題とアプローチ 東京開発統括部 楽楽精算開発部 部長  髙橋康弘 楽楽精算開発部 開発1課 課長  小宮山和彦 楽楽精算開発部 開発1課  涌井友輔 16:00 パフォーマンス向上とリソース管理のためのアプローチ 株式会社 ラク スライト クラウド 企画課  牧野寛知 株式会社 ラク スライト クラウド BMバックエンド開発課  上原崇 16:20 急成長するサービスを支えるためのインフラ戦略 インフラ開発部 副部長  藤井靖弘 16:40 楽楽精算のQA改革 東京開発統括部 QA課  金子佳樹 17:00 新たな顧客課題に挑む17年目の進化とモダナイゼーション 大阪開発統括部 配配メール開発課 課長  大塚正道 大阪開発統括部 配配メール開発課  井上良太 開発推進部 フロントエンド開発1課  亀ノ上孝雄 17:30 クロージング トーク 大阪開発統括部 統括部長  矢成行雄 プログラム詳細は、以下サイトをご確認ください。 RAKUS Tech Conference2024 RAKUS Tech Conference 2024 - connpass RAKUS Tech Conference 2024|IT勉強会・イベントならTECH PLAY[テックプレイ] 過去のRAKUS Tech Conference 今回の開催で3回目の開催となる、RAKUS Tech Conferenceの今までの発表内容を振り返ってみたいと思います。 RAKUS Tech Conference 2022 初めてRAKUS Tech Conferenceを開催した年でした! 発表内容は以下の通りです。 【発表内容】 ラク スのエンジニア組織について 楽楽精算のサービスと共に成長するエンジニア組織の3年間とこれから トラブルゼロで乗り切ったTypeScript移行 息の長いサービスのPHP8バージョンアップで見えた課題と解決法 進化を止めない、"レガシー"との向き合い方 SaaS マルチヒット メーカー ラク スのインフラ戦略 発表資料は以下ブログにまとめておりますので、ご興味ある方は是非ご確認ください! tech-blog.rakus.co.jp RAKUS Tech Conference 2023 ラク スではローンチ20年を超えるプロダクトもあり、決して目新しい技術ばかりを扱っているわけではありませんが、一人ひとりが地道な" カイゼン "のための努力、そして"挑戦"を続けています。 そんな弊社だからこその取り組みを発表させていただきました。 【発表内容】 短納期でも進化をあきらめなかった新規プロダクト開発 フロントエンド横断組織のチーム トポロジー ベテラン社員が抜けても若手が成長できるエンジニア組織づくり デザイン組織が社内下請けから脱却するためにやったこと ゼロから始める クラウド ネイティブ 「開発優先」の中で取り組む組織的な新技術への挑戦 発表資料は以下ブログにまとめております。 是非ご確認ください! tech-blog.rakus.co.jp 参加者からのフィードバック 過去RAKUS Tech Conferenceに参加された方からは、以下のようなコメントをいただいております。 ラク スさんの実際の開発チームについて知ることができました。ありがとうございました。 各パート非常にわかりやすく発表していただいていたので、とても勉強になりました。 単なる最新技術の紹介でなく、地に足のついた実際の業務に役立てられるお話を聞けました。御社の雰囲気も伝わってきて大変興味深く拝見させていただきました。 Cloud利用の理想と現実のような、現場ならではの内容など大変参考になりました。 興味のあった話をあますことなく聞けてとてもおもしろかったですし、有用でした。 今回の話を実際の業務で活かせるところを見つけて、活用したいと思います。 ベテランの方だけでなく、若手でもイベントの運営をしたり、リーダーが行っていた一部の業務に携われるといったように、裁量をもって仕事に取り組める環境が良いと感じました。 単発の勉強会は参加していましたが通しのイベントは初めてでした。ボリュームがあってよかったです。 稼働している レガシーシステム からモダンな開発システムへの移行期の悩みや、エンジニアやビジネス側にUIUXデザインの重要性の理解が進みづらいという点も非常に共感いたしました。          ※RAKUS Tech Conference 2022、RAKUS Tech Conference 2023の参加後アンケートより引用 ご参加お待ちしております! 開発本部のミッションである「顧客をカスタマーサクセスに導く圧倒的に使いやすい SaaS を創り提供する」ために開発本部一丸となって、日々精進しております。 当日参加できない方も申込特典として アーカイブ 動画をお送りしますので、是非お気軽にご参加ください! 弊社の技術取り組みが、皆さまにとって少しでもお役に立てれば幸いです。 皆さまのご参加、お待ちしております! techcon.rakus.co.jp
はじめに こんにちは。楽楽販売開発課のm_tkoとthree_yagiです。 今回は、私たちが所属しているサポート対応チームの業務を紹介します。 目次 はじめに 目次 サポート対応とは? 組織体制 取り組み 仕様確認系のお問い合わせ 調査系のお問い合わせ 工夫している点 回答観点について 通知botの運用 お問い合わせの傾向分析 今後の展望 最後に サポート対応とは? まず、サポート対応とは何かについて説明します。 基本的に、お客様からのお問い合わせはカスタマーサポートを担っている部署が回答をしています。 カスタマーサポートの基本的なフロー ただ、そのお問い合わせの中にはエンジニア視点での調査が必要なものがあります。 そういったお問い合わせをエンジニア側で引き受けて調査を行うことを「サポート対応」と呼んでいます。 エンジニア側で問合せ対応を行う場合のフロー エス カレーションされるお問い合わせは、仕様確認や調査が主になります。 ラク スだけに存在するポジションではなく、どのシステム会社でも似たような業務をされている立場の方が多いと思います。 一部では「 エス カレーションエンジニア」という名称で呼ばれることもあるそうです。 組織体制 現在、サポート対応チームは4名で業務を回しています。 役割自体には大きな差はなく、日替わりで担当者を決めて対応に当たっています。 問い合わせが来るタイミングや量はまちまちなので、開発業務と並行して対応にあたっています。 取り組み カスタマーサポートチームから エス カレーションされたお問い合わせは、社内で運用している楽楽販売に登録されます。 具体的にどういったお問い合せが来るのか、それらのお問い合わせについてサポート対応チームがどのような対応を行っているのかをご紹介します。 仕様確認系のお問い合わせ 楽楽販売では、利用目的に合わせて柔軟に対応できる高いカスタマイズ性が特徴の一つとなっています。 それゆえ設定できる箇所が多く、仕様を完全に把握するのが難しいという側面があります。 そのため、お客様から以下のようなお問い合わせをいただくことがよくあります。 「〇〇〇という設定は可能か」 「〇〇〇という挙動は正しいか」 「〇〇〇との連携は可能か」 このようなお問い合わせのうち、1次窓口であるカスタマーサポートチームでは判断のつかない仕様については、サポート対応チーム向けに仕様確認依頼が登録されます。 依頼が登録されるとサポート対応チームでは以下のような調査を行い、仕様を確認して回答しています。 検証環境での動作確認 当時の設計資料の確認 機能の設計・開発担当者への ヒアリ ング コードの調査 地道な調査が必要になりますが、これらのお問い合わせから仕様不備が見つかることもあり、サポート対応チームの大事な仕事の1つとなっています。 調査系のお問い合わせ 調査系のお問い合わせとしては以下のようなものがあります。 「〇〇〇を実行したが想定通りの結果にならなかった」 「〇〇〇というエラーが発生した」 「楽楽販売から送信したメールが届かない」 設定ミスなどが原因であればカスタマーサポートチームが調査・回答を行いますが、設定に問題がなかった場合や、システム不備の可能性があるものについてはサポート対応チームに エス カレーションされ、こちらで調査を行います。 調査では主に以下のようなことを行っています。 関連設定の確認 本番環境のログの調査 エラーが出力されていないか 想定通りのログが出力されているか 意図しない操作が行われていないか 検証環境での再現確認 不具合が確認された場合は、不具合の原因調査 これらの調査を行い、発生原因を探っていきます。 内容によっては、インフラチームに依頼して必要なデータを取得したり、設定を複製した環境で デバッグ を行ったりと取れる手段を駆使しながら調査を行います。 原因が判明した場合は、お客様の環境に合わせた回避方法の検証も行います。 事象の「原因」と「回避方法」をカスタマーサポートチームに回答して完了となります。 工夫している点 回答観点について 楽楽販売は設定次第で様々な業務に利用することができるので、お客様ごとに利用目的は異なります。 そのため、お問い合わせ対応では以下を考えながら取り組むようにしています。 なぜこの機能を使っているのか 機能を通して実現したい動きはなにか どういった状態であれば理想的か 単純にお問い合わせ内容に対して回答するだけでなく、お問い合わせに至った背景を理解し お客様の目的に合わせた回答をすることで、納得度の高い回答を目指しています。 通知 bot の運用 調査系のお問い合わせでは「業務が止まってしまっているので数時間以内に原因と回避方法を教えてほしい」といった急ぎのものが来ることがあります。 こういったお問い合わせにもすぐ対応できるように、お問い合わせが社内の楽楽販売に登録・更新された際に社内のチャットツールでお知らせする通知 bot を運用しています。 お問い合わせ登録時の bot による通知 担当者の割り振りや対応の進捗の共有もチャット上で行っています。 お問い合わせの傾向分析 対応が完了したお問い合わせについては、内容に応じてカテゴリを割り振り傾向分析を行っています。 月次でカテゴリ毎の問い合わせ件数や対応にかかった時間などを集計し、どういったカテゴリのお問い合わせが増えているかを分析しています。 分析した結果は今後のお問い合わせ対応の改善を行うための参考としたり、「お問い合わせが多い機能 = 改善の余地がある機能」としてシステム改善に繋げています。 今後の展望 サポート対応チームの現在の優先事項は 「エンジニア向けのお問い合わせ件数を減らす」 ことです。 そのために、現在はカスタマーサポートを担っている部署側でお問い合わせを解決できるように、 エンジニア側によくお問い合わせが来る質問の一覧を作成 技術的な用語の説明資料を作成する といったアクションを行っている段階です。 しかし、最終的に目指すべきゴールは 「お客様の疑問・もやもやを無くす」 ことです。 実現方法はいくつかあげられるかとは思いますが、やはりエンジニアとしてはシステムの改善を行うことでお客様の満足度向上に繋げていきたいです。 現状はお問い合わせの対応がメインとなっており、システムの改善までは至れていない状態です。 ただ、逆に言えばお客様との距離が近い位置にあり、どういった課題感があるのかを身をもって知ることができるのが強みだと考えています。 その強みを生かして、今後は お問い合わせ内容の分析 ➡ システムの改善点の抽出 ➡ システム改善の設計・実装 を一手に担えるようなチームにしていきたいです。 最後に 楽楽販売開発のサポート対応チームについて紹介させていただきました。 あまり注目されない、且つ地味なポジションではありますが、システムを支える上では必要不可欠な存在だと思っています。 お客様に快適にシステムをご利用いただくために、今後も工夫しながら業務を続けていきます。
BigDecimalの値保持について BigDecimalから値の抽出 誤った表記変換方法 正しい文字列を取得する方法 まとめ お金の計算など正確に Java で計算をするうえで欠かせない BigDecimal ですが、 一部 JDK バージョンで挙動に変更が入っていました。 この改修により問題に直面してしまったため備忘録がてら挙動をまとめることにしました。 BigDecimal の値保持について まず、本題に入る前に BigDecimal はどのように値を保持しているかを見てみましょう。 BigDecimal は以下の要素を保持しています。 intCompact 数値の 仮数 部を保持する intVal BigDecimal のスケーリングされていない値 precision 保持している 仮数 部の桁数 scale 少数のスケール では実際に見てみましょう。 BigDecimal bigDecimal1 = new BigDecimal( "3.14e+25" ); BigDecimal bigDecimal2 = new BigDecimal( "31400000000000000000000000" ); BigDecimal の内部値例 bigDecimal1は指数表現を指定し作成されているため intCompact には314、 precision には3、 scale には-23が格納されています。 また、bigDecimal1は指数表記なので intVal に値は保持されていません。 bigDecimal2は通常の数値表記を指定し作成されているため intCompact にはLong最低値、 intVal にはスケーリングされていない値、 precision には26が格納されています。 このように BigDecimal は同じ数値でありながらも内部の値が異なることがあるというパターンは存在します。 BigDecimal から値の抽出 それでは本題です。 先ほどの例で見た通り BigDecimal の値保持には2つのパターンがありました。 これらのtoStringすると以下のようになります。 bigDecimal1 : 3.14E+25 bigDecimal2 : 31400000000000000000000000 当然 BigDecimal 作成時に指定したものになりますね。 ここで例えば、この文字列を画面表示に使用したい場合について考えてみます。 3.14E+25 のように表示しても数値自体は誤りではないですが、 金額の表示などであれば非常にわかりづらいですよね。 ではbigDecimal2は良いとしてもbigDecimal1はどうすればよいでしょうか。 誤った表記変換方法 指数表記になってしまった BigDecimal を通常の数値表記の戻す方法として誤っている例を挙げてみます。 何を使用するかというと BigDecimal のメソッドの movePointRight(0) です。 movePointRight は小数点を右に指定した分だけずらすというメソッドです。 つまり右に小数点を0個ずらすということです。 BigDecimal bigDecimal1 = new BigDecimal( "3.14e+25" ).movePointRight( 0 ); BigDecimal bigDecimal2 = new BigDecimal( "31400000000000000000000000" ).movePointRight( 0 ); JDK のバージョンごとの結果は以下の通りになりました。 JDK バージョン bigDecimal1 bigDecimal2 8 31400000000000000000000000 31400000000000000000000000 11 31400000000000000000000000 31400000000000000000000000 17 3.14E+25 31400000000000000000000000 21 31400000000000000000000000 31400000000000000000000000 なんと JDK17だけ bigDecimal1が指数表記のままとなってしまっています。 ※以下は使用した JDK の ディストリビューション とバージョンです。 JDK バージョン Amazon Corretto 8 1.8.0_412 Amazon Corretto 11 11.0.23 Amazon Corretto 17 17.0.11 Amazon Corretto 21 21.0.3 正しい文字列を取得する方法 movePointRight(0) を使用した BigDecimal をtoStringで表示させた際にJDK17のときのみ 指数表記になってしまうということがわかりました。 では正しい値を取り出す方法はあるのでしょうか。 もちろん存在します。 BigDecimal のtoPlainStringメソッドです。 これを使用すると保持している内部データが異なっていても通常の数値表現(文字列)を返してくれます。 このメソッドは JDK 5から実装されているので問題になっていない JDK バージョンでも積極的に使用したいですね。 まとめ JDK バージョンによって BigDecimal が動作が一部異なるという現象に遭遇し今回詳しく調べて見ました。 JDK17でのみ挙動が異なっており、最新のLTSであるJDK21では発生していないので 今後修正される可能性もあるかもしれません。 しかし、 Java 側で通常の数値表記を取り出すメソッドが用意されているので今後はそれを使用したほうが良さそうです。 BigDecimal を使用する場面というのは間違うことが許されないような数値を扱う場面が殆どかと思います。 今後の更新も要チェックですね。
はじめに HTTPS(HTTP Over TLS)とは SSL/TLS HTTPSの流れ 実際に通信を観察 自己署名証明書の用意 サーバーの作成 WireSharkの準備 リクエストを送信して観察 まとめ はじめに エンジニア2年目のTKDSです! 普段何気なく使ってるほとんどのWebサイトが対応している HTTPS 通信の仕組みについて調べてみました。 本記事では、 Wireshark を用いて HTTPS の内部動作を解析し、どのようにしてデータが保護されているのかを具体的に解説します。 記事の後半では、 Wireshark を使って実際の通信データを観察し、暗号化プロセスの詳細を確認してみます。 HTTPS (HTTP Over TLS )とは HTTPS (HTTP Over TLS )は、HTTPの暗号化版で、ウェブサイトとブラウザ間の安全な通信を実現する プロトコル です。 TLS を使用して、HTTP通信を暗号化することで、データの機密性、データの整合性、通信先サーバーの信頼性を確認できます。 SSL / TLS SSL (Secure Sockets Layer)と TLS (Transport Layer Security)は、インターネット上でデータを暗号化して送受信するための プロトコル です。 これらの プロトコル は、ウェブブラウザとウェブサーバー間の通信を保護し、データの盗聴や改ざんを防ぐために使用されます。 SSL は1990年代初頭に Netscape 社によって開発されました。 最初のバージョンである SSL 2.0は1995年にリリースされましたが、セキュリティ上の 脆弱性 が発見され、1996年に SSL 3.0に置き換えられました。 その後、1999年に SSL 3.0を基にした TLS 1.0が登場し、現在では TLS 1.3が最新バージョンとして使用されています。 HTTPS の流れ HTTPS は次の流れで通信を行います。 クライアントがサーバーに HTTPS 接続を要求 サーバーが SSL / TLS 証明書(公開鍵も含む)をクライアントに送信 クライアントが証明書を検証し、サーバーの身元を確認 クライアントがセッション鍵(対称鍵)を生成 クライアントがセッション鍵をサーバーの公開鍵で暗号化して送信 サーバーが 秘密鍵 を使ってセッション鍵を復号、この時点で、クライアントとサーバーの両方が同じセッション鍵を共有、以降の通信はこのセッション鍵を使って暗号化 クライアントが暗号化されたHTTPリク エス トを送信 サーバーがリク エス トを復号して処理 サーバーが暗号化されたHTTPレスポンスを送信 クライアントがレスポンスを復号して表示 HTTPS の流れの図を以下に記載します。 暗号鍵をクライアントとサーバー間の通信で使うことで安全に通信していることがわかりました。 実際に通信を観察 次に HTTPS を観察してみます。 通信先のサーバーをGoで用意して、 curl で通信します。 自己署名証明書 の用意 今回使う証明書を用意します。 sudo apt-get update sudo apt-get install openssl openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout server.key -out server.crt サーバーの作成 今回使用するhttpサーバーを用意します。 ファイル名はmain.goを想定しています。 起動は、 go run main.go で行ってください。 HTTPサーバー package main import ( "fmt" "net/http" ) func helloHandler(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello, World!" ) } func main() { http.HandleFunc( "/" , helloHandler) fmt.Println( "Starting HTTP server on :8080" ) if err := http.ListenAndServe( ":8080" , nil ); err != nil { fmt.Println( "Error starting HTTP server:" , err) } } HTTPS サーバー package main import ( "fmt" "net/http" ) func helloHandler(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello, World!" ) } func main() { http.HandleFunc( "/" , helloHandler) fmt.Println( "Starting HTTPS server on :8443" ) if err := http.ListenAndServeTLS( ":8443" , "server.crt" , "server.key" , nil ); err != nil { fmt.Println( "Error starting HTTPS server:" , err) } } WireShark の準備 通信のキャプチャは WireShark を使います。 以下の手順にしたがって準備してください。 インストール sudo apt-get update sudo apt-get install wireshark sudo dpkg-reconfigure wireshark-common sudo usermod -aG wireshark $USER newgrp wireshark 起動 sudo wireshark キャプチャするネットワークインターフェースの選択 今回は localhost なので、loを選択します。 キャプチャの開始 開始ボタンを押すとキャプチャが開始されます。 HTTPの場合、上部のフィルタに http.request and tcp.port == 8080 を入力します。 HTTPS の場合、上部のフィルタに tls and tcp.port == 8443 と入力します。 リク エス トを送信して観察 HTTP http用のサーバーを起動してからリク エス トを送る。 curl http://localhost:8080 通常のHTTP通信が行われているのが確認できます。 フィルタのhttp指定を外してすべての通信をみても、 https ではないので暗号化は行われていないことがわかります。 HTTPS HTTPS 用のサーバーを起動してからリク エス トを送る。 curl -k https://localhost:8443 TLS ハンドシェイクが行われていることが確認できます。 暗号化されているのでどのhttpメソッドを実行しているか、エンドポイントのパスはなにかなどは通信内容から読み取れなくなっています。 TLS の流れを少しみてみましょう。 Client Hello TLS (Transport Layer Security)接続の開始時にクライアントからサーバーに送信される最初のメッセージです。 クライアントがサポートするセキュリティ設定をサーバーに通知し、サーバーとクライアントの間で使用する暗号化方法の ネゴシエート を開始します。 Server Hello Server Helloをクライアントに送信し、使用する TLS のバージョン、暗号スイート、圧縮方法を確定します。 Change Cipher Spec, Application Data Change Cipher Specをクライアントに送信し、以降の通信が暗号化されることを通知 以上が TLS の流れでした。 まとめ ここまで読んでいただきありがとうございました! この記事ではHTTPと HTTPS の通信を観察し、通信内容を比較しました。 ふとした疑問から調べはじめた内容でしたが、知識の復習やツールの使い方を思い出す役に立ちました 。 普段意識せずに使っているものも、実際に調べてみることで、知識の定着につながりました。
こんにちは、モバイル開発チームのhyoshです。 弊社では各分野の特定のテーマに沿ってエンジニアが議論する「TechCafe」というイベントを定期開催しています。 そして先日私を含めた弊社モバイル開発チームが2度目となる「モバイルTechCafe」を開催しました! 今回のイベントでは「 Google I/O 2024とWWDC24で気になったセッション」について語り合いました。 弊社のメンバーが事前にまとめてきた情報にしたがって、他の参加者に意見を頂いて語り合いながら学びました。 今回はその内容についてレポートします。 Google I/O 2024 デベロッパー基調講演 Android 開発ツールの新機能 Google Play の新機能 Android の新機能 WWDC24 基調講演 Xcode16の新機能 Swiftの新機能 Swift Testingについて まとめ Google I/O 2024 公式ページ: Google I/O 2024 開催期間:2024/5/14 (現地時間) Google I/O は Google が毎年開催する開発者向けの大規模カンファレンスです。 モバイルエンジニアにとっては Android やFlutter開発における最新情報が入手できる場となっています。 本年度はやはりAI活用といったところが目玉になると見込まれていましたが果たして結果は…? デベロッパ ー基調講演 io.google セッション概要 基調講演ということもあり開発者ツール、 Android 、Flutter、Firebaseと幅広く開発者向けの新情報が発表されました。 注目されていたAI関連ではGemini 1.5 Flash の提供開始、GeminiAPI開発者 コンペティション 開催、Gemini NanoがPixel8 Proから搭載などやはり多くの時間を割いて紹介がされていました。 メンバーの感想 Gemini Nano は通信介さないので低遅延・セキュアなアプリ実装が可能。 Chrome にも搭載されるのでコスト低く 拡張機能 開発なども取り組むことができそう。 遂に Google が Kotlin Multiplatform(KMP) を公式サポート。Room等のライブラリもKMP対応されたし、今後も充実してくれれば移行もしやすく採用ハードルも下がりそう。 クラウド 開発環境の Project IDX がBeta版として公開。CodeOSSベースなので VSCode でFlutter開発している人には選択肢になるかも。環境構築不要なので勉強会とかにもよさそう。 Android 開発ツールの新機能 io.google セッション概要 恒例の最新版 Android Studio の紹介ですが本年度は Android Studio Koala が紹介されました。 目玉はやはりGeminiとの連携ですがそれ以外でも開発が便利になる新機能が目白押しで利用するのが楽しみになる発表でした。 メンバーの感想 チャットだけだったStudioBotからコード補完もできるGeminiに進化。選択範囲に対してプロンプトで細かい指示出せるのも使いやすそう。インライン ブレークポイント とかも地味に便利で嬉しい。 FirebaseはCrashlyticsがGemini連携し原因や対策を提案してくれるようになったので、クラッシュ調査で手早く洞察得るのに活用したい。 Google Play の新機能 io.google セッション概要 多様な国やデ バイス に対しても最適な表現をすることで更に深くリーチし、より安全なアプリ提供を実現できる新しい機能が紹介されました。 特にゲームアプリについてユーザーにより魅力を伝えファンを増やしていくプラットフォームとしていく事に力を注いでいる姿が印象的でした。 メンバーの感想 セキュリティポリシー 違反や不安定な サードパーティ ライブラリを審査時に教えてくれるのは助かる。 レビュー提出前に一度出したアプリを削除できる機能はずっと欲しかったのでありがたい。リリースプロセスの柔軟性を上げる事ができる。 Android の新機能 io.google セッション概要 基調講演でもフォーカスされていたGemini Nanoの活用例の他、最新OSである Android15の機能紹介 がされました。 メンバーの感想 Kotlin2.0からCompose コンパイラ がKotlin リポジトリ に同梱されるようになったので複雑な互換性管理がなくなり嬉しい。 Android15からEdge to Edgeがどのアプリでもデフォルトになりデザインが変わるので、次回のtargetSdk対応は苦労しそう。 WWDC24 公式ページ: WWDC24 - Apple Developer 開催期間:2024/6/10~6/14 (現地時間) WWDC は Apple が毎年開催する開発者向けの大規模カンファレンスです。 Apple の最新製品や技術が紹介されるのでエンジニアだけでなくファンにも待ち遠しいイベントですね。 iOS エンジニアとしては新OSや開発環境が気になるところですが当メンバーはどこに興味を持ったのでしょうか…? 基調講演 developer.apple.com セッション概要 数日間に渡るイベント全体のサマリともいえる基調講演は昨年度の Vision Proのように目玉となる製品が発表される再注目のセッションです。 今回は何と言っても待望の Apple 製品搭載AIである Apple Intelligence の紹介に当チームも湧き立ちました。 メンバーの感想 各アプリからシームレスにAI利用できるUIUXの作り込みはさすが Apple という感じ。 ChatGPTとも併用というのが意外だったけどハイブリッドで使い分けるのは互いの強みを活かせて最適なのかも。 Xcode16の新機能 developer.apple.com セッション概要 iOS 開発環境であるXodeについて最新機能が紹介されました。 期待されていたAI活用によるコード補完やプレビューや デバッグ 機能の強化など利用者にとっては気になる情報が盛りだくさんでした。 メンバーの感想 macOS が最新の Sequoia で Apple Siliconという条件は厳しいけどコード補完を早く使いたい。完全ローカルで動作して学習データにも利用されないとのことでプライバシーにも配慮されてるのはさすが。 Copilotも使っているが並行して試せそうなので回答を比較してみたい。 Swiftの新機能 developer.apple.com セッション概要 今年で10周年という事でSwiftの歴史を振り返りつつ最新のSwift6の新機能が紹介されました。 Swift6では特に非同期でのデータ競合の安全性を向上させる新しい言語モードが強調されていました。 メンバーの感想 歴史を見ているとSwift2→3は破壊的変更が多く永遠にビルドが通らず辛かったことを思い出した。 Mac 前提というのが試したくてもハードル高いので今後環境に依らず開発できるように クロスプラットフォーム 開発機能に注力してくれるのは嬉しい。 Swift Testingについて developer.apple.com セッション概要 Swiftを使用してコードをテストするための新しいパッケージ、Swift Testingについて紹介されました。 従来のテスト フレームワーク であるXCTestの辛い部分が大幅に改善されていそうで非常に期待感の大きい内容でした。 メンバーの感想 記述がシンプルになり可読性が大きく上げられそう。テストも並列実行されるとのことで実行時間短縮にも繋がりそうなのですぐに移行したい。 XCTestは触ったことないが Junit だとできてることが多いので恵まれてるなと思った。 まとめ 以上、「 Google I/O 2024とWWDC24で気になったセッション」を語り合ったTechCafe当日の内容を簡単にまとめさせていただきました。 紹介した以外でもより実践的な事例を紹介したテクニカルセッションや、ハンズオン形式で新しい技術を学べるCodeLabも公式ページにはあるので更に深く学びたい方はぜひご覧いただければと思います。 今回のTechCafeを通じて図らずもAIの最新機種への搭載、開発環境でのコード補完への活用などのトレンド情報に関しては Google と Apple それぞれの特徴や注力ポイントを比較できるような形になりました。 当チームは日頃は各メンバーが各OSに専念して開発することが多いので、そういった面でも自分がよく知っていたり知らなかったりする情報を共有できた非常に有意義なイベントでした。 TechCafeは上記のように勉強会としての効果も高いと思うので、興味を持たれた方は初回開催するまでの道 のりを まとめた以下ブログもご覧いただけますと幸いです。 tech-blog.rakus.co.jp モバイルTechCafeも次回第3回開催を目指してテーマを吟味中ですので、ぜひご参加いただけますと幸いです。
チームの紹介 チームのミッション チーム体制と役割 チームの文化 取り組み事例 オブジェクトストレージのリプレイス 楽楽精算のインターネット通信で利用される帯域の増加対策 今後の展望 はじめまして。楽楽精算のインフラのマネージャーを務めている永易です。 楽楽精算のインフラチームの組織体系について、現在までと今後についてをお話させていただきます。 チームの紹介 チームのミッション 楽楽精算のインフラを適切なコストで安定させる お客様に楽楽精算を安心して利用していただくために、インフラチームとして安定したサービスの提供を責務としています。 一方、企業として利益を確保する事も必要であり、サービス品質とコスト(売上原価)のバランスを大切にしてます。 チーム体制と役割 現在の楽楽精算インフラメンバーは5人で構成されており、キャリアに合わせてメインの業務を保守運用担当と設計担当に分けています。 具体的な業務内容の内訳としては以下になります。 メンバーのキャリアに合わせて少しずつ設計を担当する領域を増やし、経験を積ませることで思考力や行動力を向上させます。 チームの文化 私たちのチームは、主体的な問題提起をし、改善提案を行うメンバーで構成されています。 楽楽精算は年間成長率(CAGR)30%を超える売上を何年も続けており、2024年3月期には144億4600万を達成しました。 この目まぐるしい成長に伴い、サービスの拡大とともにインフラ規模も拡大してます。大規模なサービスには多角的な視野と技術的な工夫が必要であり、人員の増加を最小限に抑えながらも組織の安定を図るために、メンバーの コンピテンシー (主に思考力、行動力)の成長が重要です。 以下に、当チームのメンバーに求められる具体的な コンピテンシー の一例を示します。 全体を俯瞰する視点を持つ : システム全体を把握し、数年後を予測して問題を多角的に捉えること 類似事象のチェック : 類似事象の分析を通じて、根本原因の特定と解決に繋げる 根本原因の深堀り : 問題の根本原因を追求し、再発防止策を考える これらの コンピテンシー を通じて、「何故その仕事を行うのかを考え、行動できる組織」を目指してます。 取り組み事例 設計分野では、サーバにインストールされているOSの保守終了に伴い、新しいOSへリプレイスする事が作業として多いですが、それ以外に直近1年以内に取り組んできた事例を紹介します。 オブジェクトストレージのリプレイス 楽楽精算はオンプレミス環境でオブジェクトストレージを構築して利用しています。法的要件である「 電子帳簿保存法 」や「 インボイス 制度」の導入に伴い、1顧客あたりのオブジェクトストレージの利用量が急増しました。 その結果、「費用コスト」「サーバ増設コスト」が年間成長率(CAGR)を大きく上回る事が予想されました。 売上に対する原価の大幅な増加による利益低下を懸念し、「増設が簡単」で「ラック収容率が高い」オブジェクトストレージの調査を始めました。 具体的なアクションは以下になります。 要件定義 見積取得 コスト試算 新オブジェクトストレージの技術検証結果から切り替え可能か判断 社内報告とリプレイススケジュール調整 現状コストが高い原因を洗い出し、コスト配分の高い課題を解決すれば全体的に問題が解決する方針で要件定義を進めました( パレートの法則 )。 結果として、 AWS のS3を使うよりも安く、10年後の年間コストが既存環境より数億円のコスト削減が達成し、可用性や性能も向上しました。 楽楽精算のインターネット通信で利用される帯域の増加対策 楽楽精算のサービス拡大に伴い、現在契約しているデータセンターのネットワーク回線プランの帯域では数年後に不足することが予想されました。安定したサービス提供のために帯域を確保しつつ、コストバランスを取るためにデータセンター業者に相談を開始しました。 現在契約しているデータセンター拠点、および別拠点のネットワーク契約プランを調査 3年後までのラック発注数を予測したフォーキャスト資料の作成 データセンター業者への提案と交渉 交渉結果を次年度の予算に反映 課題として、現在契約しているデータセンターには、楽楽精算を安定してサービス提供できるネットワーク帯域契約プランが存在しませんでした。そのため、データセンター業者と協力し、双方にメリットのあるプランを策定する必要がありました。 結果として、新しいプランを策定してもらい、帯域単価コストの削減に成功しました。この成功により、データセンター業者のメンバーと楽楽精算のインフラチームとの交流が続いています。 今後の展望 昨今の サイバー攻撃 の増加に伴い、私たちはセキュリティ対応を強化しています。また、昨年、 AWS のEKS環境を用いて Kubernetes やコンテナ技術をメンバーの一部が習得しました。これらの技術をチーム全体で汎用的に活用できるようにするため、適切な環境と時間を提供し、技術力を生かした設計に取り組んでいます。これにより、運用の効率化や原価の削減を進めています。 これらの技術の積み重ねを通じて、より大規模なネットワーク設計に繋げ、大規模災害対策への道筋を確立していきます。
はじめに Testcontainersとは Testcontainersのメリット ハンズオン 環境設定 goプロジェクトの作成 必要なパッケージのインストール テストコードの作成 コンテナリクエストの設定 コンテナの起動 コンテナのホストとポートの取得 結果の確認 まとめ はじめに こんにちは! エンジニア2年目のTKDSです! 前回は Dagger を紹介しました。 今回もコンテナ技術を活用して、テストを容易にするツールについて紹介します。 今回取り上げるのは、統合テストやエンドツーエンドテストのためにDockerコンテナを利用するライブラリ、Testcontainersです。 Testcontainersとは Testcontainersはさまざまな プログラミング言語 ( Java 、Go、 Python 、Node.jsなど)向けに提供されており、Daggerと同様にテスト用のコンテナを簡単に作成することができます。 前回の記事で紹介した ユースケース 2と同様のことが実現できるため、テスト内にコンテナの起動コードを直接書くことが可能です。 Testcontainersを使用することでモックを使用せずに外部サービスに依存するテストを書くことが可能になります。 Testcontainersのメリット 次にTestcontainersのメリットについて紹介します。 モックを使わずにテストをかける 前述のようにコンテナでDBなどの外部サービスを用意することでモックに依存しないテストが書けます。 コンテナで用意できる対象は、DBに限らず、MQやNoSQL、Key- value storeなど多岐に渡ります。 事前に用意されている モジュール やコンテナイメージを直接指定してコンテナを作ることができます。 2つ目の方法については後ほど詳しく説明します。 事前に用意されたモジュールがある 前項目で述べたようにTestcontainers moduleが用意されているため、面倒な準備なしに様々な ミドルウェア が使用可能です。 ローカル環境と CI 環境の両方で一貫した環境の用意が可能 コンテナで環境が用意できるため、ローカルでは実行できるのにCIではテストが落ちる、またはその逆など環境依存のテストの不安定さを取り除けます。 参考 - https://testcontainers.com/getting-started/ - https://testcontainers.com/guides/ - https://testcontainers.com/ ハンズオン では、実際にTestcontainersを使ってみます。 今回はGoを使います。 環境設定 筆者の環境 shun@shun-ThinkPad-P14s-Gen-4:~$ cat /etc/os-release PRETTY_NAME="Ubuntu 22.04.4 LTS" NAME="Ubuntu" VERSION_ID="22.04" VERSION="22.04.4 LTS (Jammy Jellyfish)" VERSION_CODENAME=jammy ID=ubuntu ID_LIKE=debian HOME_URL="https://www.ubuntu.com/" SUPPORT_URL="https://help.ubuntu.com/" BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/" PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy" UBUNTU_CODENAME=jammy shun@shun-ThinkPad-P14s-Gen-4:~$ docker version Client: Docker Engine - Community Version: 26.1.3 API version: 1.45 Go version: go1.21.10 Git commit: b72abbb Built: Thu May 16 08:33:29 2024 OS/Arch: linux/amd64 Context: default Server: Docker Engine - Community Engine: Version: 26.1.3 API version: 1.45 (minimum version 1.24) Go version: go1.21.10 Git commit: 8e96db1 Built: Thu May 16 08:33:29 2024 OS/Arch: linux/amd64 Experimental: false containerd: Version: 1.6.32 GitCommit: 8b3b7ca2e5ce38e8f31a34f35b2b68ceb8470d89 runc: Version: 1.1.12 GitCommit: v1.1.12-0-g51d5e94 docker-init: Version: 0.19.0 GitCommit: de40ad0 事前にDockerのインストールは済ませておいてください。 goプロジェクトの作成 mkdir go-testcontainers cd go-testcontainers go mod init go-testcontainers 必要なパッケージのインストール go get github.com/testcontainers/testcontainers-go go get github.com/testcontainers/testcontainers-go/modules/postgres go get github.com/jackc/pgx/v4 テストコードの作成 コード全体は github においてあります。 Testcontainersに関わる部分だけ説明します。 今回は事前に用意されたモジュールを使わずにDBを用意します。 コンテナリク エス トの設定 この部分では、コンテナを起動するためのリク エス トを設定しています。 req := testcontainers.ContainerRequest{ Image: "postgres:latest" , ExposedPorts: [] string { "5432/tcp" }, Env: map [ string ] string { "POSTGRES_DB" : "testdb" , "POSTGRES_USER" : "testuser" , "POSTGRES_PASSWORD" : "testpassword" , }, WaitingFor: wait.ForListeningPort( "5432/tcp" ), } Image : 使用するDockerイメージを指定します。ここではpostgres:latestを指定しており、最新の PostgreSQL イメージを使用します。 ExposedPorts : コンテナの公開ポートを指定します。 PostgreSQL はデフォルトで5432ポートを使用するため、5432/ tcp を指定しています。 Env : コンテナ内の 環境変数 を設定します。ここではデータベース名、ユーザー名、パスワードを設定しています。 WaitingFor : コンテナが準備完了になるまで待機する条件を指定します。ここでは、5432ポートがリスニング状態になるのを待ちます。 参考 - https://golang.testcontainers.org/quickstart/ - https://github.com/testcontainers/testcontainers-go/blob/v0.31.0/container.go#L123 コンテナの起動 ここでは、設定したリク エス トに基づいてコンテナを起動しています。 postgresContainer, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ContainerRequest: req, Started: true , }) if err != nil { t.Fatal(err) } defer postgresContainer.Terminate(ctx) testcontainers.GenericContainer : 汎用的なコンテナを起動するための関数です。 ctx : コンテキストを渡します。これは操作のキャンセルや タイムアウト を管理するために使用されます。 ContainerRequest : 先ほど設定したコンテナリク エス トを渡します。 Started: true : コンテナがすぐに起動するように指定します。 コンテナのホストとポートの取得 ここでは、起動したコンテナのホストと マッピング されたポートを取得しています。 host, err := postgresContainer.Host(ctx) if err != nil { t.Fatal(err) } port, err := postgresContainer.MappedPort(ctx, "5432" ) if err != nil { t.Fatal(err) } postgresContainer.Host(ctx) : コンテナのホスト名を取得します。通常は localhost になります。 postgresContainer.MappedPort(ctx, "5432") : コンテナ内の5432ポートがホストのどのポートに マッピング されているかを取得します。 結果の確認 実際にテストを実行し、結果を確認してみましょう。 go mod tidy go test -v ./… 無事テストが実行されたことが確認できました! 画像のように、通常のgo testで実行することができ、非常に簡単にDB依存のテストが実行できます。 まとめ 今回はTestcontainersについて紹介しました。 今まではモジュールを使った方法しか知らなかったため、今回のハンズオンで触れてみた内容は新たな学びになりました。 自分でやりたいことを設定できるのであれば、Daggerの代わりに使ってみるのもありかもしれません。 特に、コンテナを用意するだけでCI/CDパイプラインを書かない・Goを使用しない等であれば有力な選択肢になりそうです。 ここまで読んでいただきありがとうございました。
改善施策を決めるまで 旧アーキテクチャ概要 旧アーキテクチャの問題分析 新アーキテクチャ概要 最後に こんにちは、楽楽販売開発課の岡本です。 弊社では10年を超える長寿プロダクトをいくつも擁していますが、私が担当しているプロダクトもそんな長寿プロダクトの一つです。 さて、どのように優れたプロダクトでも10年以上開発を続けていれば、少なくない量のコード負債を抱えてしまうもので、我々の開発チームでもこの問題に日々悩まされております。 このような状況を打開すべく、昨年9月に開発チーム内に改善専門部隊が立ち上がりました。 本記事では、改善部隊が行った施策の1つである 「バリデーション アーキテクチャ の変更」 を取り上げて紹介しようと思います。 改善施策を決めるまで 先の項でも紹介した通り、我々のプロダクトは少なくない量のコード負債を抱えてしまっています。 ですので、改善部隊が発足時点で改善したい内容はいくらでもあり、正直何を選べば良いのかはっきりとしない状態でした。 このような時に ”感と経験を頼りにピンポイントに改善箇所を見つける” ようなことができれば、手っ取り早いのですが、あいにく私にはそのような技量はないので、まずは開発チームのメンバーが実際にどう感じているのか ヒアリ ングしてみることにしました。 ヒアリ ングの際に注意したのは 「課題を明確にする」 という点です。 課題が明確でないまま ヒアリ ングを行うと方向性が揃わず着地できないことが多いからです。 今回は、「開発速度を上げるには」という課題を設定してメンバーに ヒアリ ングを行い改善の解像度を徐々に上げていきました。 こうして、決定したいくつかの施策のうちの1つが「バリデーション アーキテクチャ の変更」です。 では、事項以降「バリデーション アーキテクチャ の変更」の実施概要にてついて紹介していきます。 旧 アーキテクチャ 概要 まずは、旧 アーキテクチャ の概要を紹介しておきます。 基本的な処理の流れは、以下の図の通りです。 (一部機微な情報が含まれるのでクラス名等は実際のコードから変更を加えています。) 旧 アーキテクチャ の処理フロー 図だけでは、イメージを掴みづらいと思いますので、コード例も載せておきます。 <?php class UserController { /** HTTPリクエストのエンドポイントメソッド */ public function register () : void { // 1.リクエストパラメータを取得 /** * @var array<string, string> $requestParamMap * Note: [name => 'ユーザ氏名', age => 'ユーザ年齢'] */ $ requestParamMap = $ this -> getRequest () -> getParams () ; // 2.リクエストパラメータから バリデーション実行オブジェクト を生成 $ builder = new UserValidationBuilder ( $ requestParamMap ) ; $ validationProcessor = $ builder -> getRegisterValidation () ; // 3.バリデーションを実行して結果出力 if ( !$ validationProcessor -> isValid ()) { echo $ validationProcessor -> getMessage () ; return ; } } } class UserValidationBuilder { /** * @param array<string, string> $requestParamMap * Note: [name => 'ユーザ氏名', age => 'ユーザ年齢'] */ public function __constoracluct ( array $ requestParamMap ) { } public function getRegisterValidation () : ValidationProcessor { // 1. リクエストパラメータとバリデーションのマッピングを指定 $ validationMapping = [ 'name' // $requestParamMap['name'] の値を getNameValidate で指定された内容で検証 => $ this -> getNameValidation ( 'ユーザ氏名' ) , 'age' => $ this -> getAgeValidation ( 'ユーザ年齢' ) ] ; // バリデーション実行オブジェクトを返す return new ValidationProcessor ( $ validationMapping , $ this -> requestParamMap ) ; } private function getNameValidation ( string $ itemName ) : array { // 2. ユーザ氏名のバリデーションを設定 return [ new NotEmptyValidation ( $ itemName ) , // 必須 ] ; } private function getAgeValidation ( string $ itemName ) : array { // 3. ユーザ年齢のバリデーションを設定 return [ new NotEmptyValidation ( $ itemName ) , // 必須 new IsNumberValidation ( $ itemName ) , // 数値入力のみ ] ; } } 旧 アーキテクチャ の問題分析 さて、旧 アーキテクチャ ですがコード例を見てどのように思われたでしょうか? 私は、これを見た瞬間「読みにくいな~」という印象を持ったことを覚えています。 では、なぜ「読みにくい」と感じたのか、これがわからないままでは改善の方針も立てられないので、まず最初にこれの 言語化 を実施しました。 ■実際に 言語化 した内容 リク エス トパラメータが配列で扱われているので、パラメータ追加等の改修で発生する影響度が調査しづらい 対象項目の指定と、その項目のバリデーション内容の指定が、別々の関数で行われており認知負荷が高い (画面をスクロールしないと項目に対するバリデーション内容がわからない) どうでしょうか? 色々と意見もあるかと思いますが、私はこのように 言語化 し、そこから以下ような大方針を立て アーキテクチャ の設計を行うことにしました。 リク エス トパラメータを配列で扱うことをやめる どの項目に、何のバリデーションが実施されるのかを、画面スクロールなしに把握できる状態にする Java で言うところの BeanValidation のようなものを想定 そして、本方針のもとPOCを作成し、更に検討をかさねたすえ、無事新 アーキテクチャ の設計はFIX、昨年11月頃より本格的に導入を開始することができました。 新 アーキテクチャ 概要 では、最終的な新 アーキテクチャ の概要も以下に紹介しておきます。 (旧と同じく処理フロー図とコード例) 新 アーキテクチャ の処理フロー <?php class UserController { /** HTTPリクエストのエンドポイントメソッド */ public function register () : void { // 1.リクエストパラメータをFormに変換 /** * @var array<string, string> $requestParamMap * Note: [name => 'ユーザ氏名', age => 'ユーザ年齢'] */ $ requestParamMap = $ this -> getRequest () -> getParams () ; $ form = new UserRegisterForm ( $ requestParamMap [ 'name' ] , $ requestParamMap [ 'age' ] ) ; // 2.From の内容を検証(バリデーション実行オブジェクトではなく、実行結果が返ってくる) $ result = ValidationBuilder :: validate ( $ form ) ; // 3.結果出力 if ( !$ result -> isValid ) { echo $ result -> message; return ; } } } class UserRegisterForm { public function __construct ( // property と attribute でバリデーションマッピングを再現 // ValidationBuilder がこの定義を取り出して、これまで各自で実装していた ValidationProcessor を自動生成する #[ NotEmpty ( 'ユーザ氏名' )] public ? string $ name , #[ NotEmpty ( 'ユーザ年齢' )] #[ Number ( 'ユーザ年齢' )] public ? int $ age ) { } } 最後に こうして生み出された新 アーキテクチャ ですが、半年ほど置き換え作業を継続し、5月末段階時点で2割程度の完了率となっています。 なにせ、10年越しの負債を解消していっているので、そう易々とは進めさせてもらえないのが現状です。 とはいえ、チーム内から良いフィードバックを多々もらっており、少しづつでも負債解消が進んでいることをメンバーも実感できているようです。 以上、我々のチームの改善取り組みの紹介でした。
はじめに こんにちは! エンジニア2年目のTKDSです! この記事ではDaggerについて紹介します。 この記事は課内で行ったLTをもとにしたものです。 はじめに Daggerとは? アーキテクチャ概要 Dagger Function Dagger Module 実際につかってみる ユースケース1:テストのパイプラインを記述 ユースケース2:DB依存の単体テストでDBのコンテナを用意する まとめ Dagger とは? CIの関数(Dagger Function)化 関数を CLI 、 SDK 、HTTPリク エス トなどから実行可能 関数→モジュールにして再利用可能 既存のモジュール(自分や他人が作ったもの)を再利用可能 一度書けばどこでも(ローカルPC、Actionsなど)実行可能 以上の特徴を持つ、コンテナ内でパイプラインを実行する、プログラム可能なCI/CD エンジンです。 アーキテクチャ 概要 Daggerを構成する要素は以下の図のとおりです。 GraphQL ServerがDagger Engine内の各要素を操作します。 コンテナの実行、ファイルや ディレクト リとのやり取りなど基本的な操作はCore API として用意してあり、そこに作成したDagger Module(CI の塊:Dagger Function)を追加して、CIを実行可能にします。 Dagger Function CIの手順はDagger Engineが生成したコンテナ内で実行されます。 Dagger Functionの 特徴 は以下の三点です。 再現性(Reproducibility) 毎回同じ方法で実行され、実行環境外との依存関係が発生しません。 キャッシュ(Caching) 実行結果をキャッシュすることで、実行を高速化できます。 セキュリティ(Security) デフォルトではホスト環境(ホストファイル、 ディレクト リ、 環境変数 など)にアクセスできません。 特に便利なのは、再現性とセキュリティの特徴です。 Daggerはコンテナ内で実行され、デフォルトで実行可能な操作の権限が制限されているため、セキュリティが強化されます。 また、CI実行環境がコンテナ単位で独立しているため、ホスト環境や他のコンテナに依存せず、実行環境外との依存関係が発生しません。 Dagger Module 前述のDagger Functionをまとめたものです。 他の人が作ったものを利用することもでき、CIの再利用性を高めてくれます。 実際につかってみる 現在のドキュメント では以下の SDK が記載されています。 Go Python TypeScript 今回はこの中で一番好きな言語であるGoを使います。 インストール方法などは公式ドキュメントを参考にしてください。 ユースケース 1:テストのパイプラインを記述 1つめは操作の流れをコードに書き、Dagger CLI から実行する使い方です。 以下の画像のようにGoのコードで行う操作を記述します。各操作は次のとおりです。 Container().From : 新規のコンテナ作成 WithExec():コンテナ内で実行されるコマンド. 引数に変数の値を引き渡せる Container.Stdout() : 標準出力 dagger call scan-image --image-ref alpine:latest 上記のコマンドを実行すると結果は以下のように出力されます。 alpine:latest (alpine 3.19.1) ============================= Total: 2 (UNKNOWN: 0, LOW: 2, MEDIUM: 0, HIGH: 0, CRITICAL: 0) ┌────────────┬───────────────┬──────────┬────────┬───────────────────┬───────────────┬───────────────────────────────────────────────────────────┐ │ Library │ Vulnerability │ Severity │ Status │ Installed Version │ Fixed Version │ Title │ ├────────────┼───────────────┼──────────┼────────┼───────────────────┼───────────────┼───────────────────────────────────────────────────────────┤ │ libcrypto3 │ CVE-2024-2511 │ LOW │ fixed │ 3.1.4-r5 │ 3.1.4-r6 │ openssl: Unbounded memory growth with session handling in │ │ │ │ │ │ │ │ TLSv1.3 │ │ │ │ │ │ │ │ https://avd.aquasec.com/nvd/cve-2024-2511 │ ├────────────┤ │ │ │ │ │ │ │ libssl3 │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └────────────┴───────────────┴──────────┴────────┴───────────────────┴───────────────┴───────────────────────────────────────────────────────────┘ 脆弱性 チェックのCIを実行することができました。 ユースケース 2:DB依存の 単体テスト でDBのコンテナを用意する 2つ目は、コンテナの操作を直接テストコード内に書く使い方です。 実はDaggerのgo sdk はGoのコード内に記述しておくことで、Dagger CLI を使わずに実行することが可能です。 以下のようにテストコードを用意します。 実行結果は以下のとおりです。 Daggerを使って、 単体テスト のコード内にDBを用意するコードを記述して実行することができました! まとめ 今回はDaggerを紹介しました。 個人的には、 ユースケース 2のDB依存のテストケースにコンテナ起動まで記述する使い方がおすすめです! 今までは、別でコンテナを起動してテストを実行する必要がありましたが、Daggerを使えば、特にDBの起動状態を意識することなくテストが実行可能です。 また、コンテナを破棄すれば状態をリセットできるため、フレーキーテストなどの削減などにも役に立つのではないかと感じました。 今回の記事は以上です。 ここまで読んでいただきありがとうございました!
こんにちは、フロントエンド開発課所属の koki _matsuraです! 本記事では、E2EテストライブラリであるPlaywrightのv1.40 ~ 最新版v1.43で追加された機能の中から僕の独断と偏見でいくつかを紹介したいと思います。 では早速、紹介していきます! 以下は目次です。 v1.40の新機能 Test Generatorにアサーションコード生成機能 toBeVisibleアサーション toContainTextアサーション toHaveValueアサーション v1.41の新機能 screenshot関数のstyleオプション toHaveScreenshot関数のstylePathオプション v1.42の新機能 addLocatorHandler関数 タグの追加 v1.43の新機能 LocatorとFrameLocatorの相互変換 clearCookiesのフィルター機能 まとめ v1.40の新機能 Test Generatorに アサーション コード生成機能 Playwrightの目玉機能の画面上の操作をテストコードに書き起こしてくれる、Test Generatorにv1.40から アサーション コードを生成する機能が追加されました。 下画像はv1.39とv1.40のTest Generatorの初期画面を比較したものです。 v1.39 v1.40 違いは画面上部のツールです。 このツールには6つのアイコンがあります。 左から順に「 ツールバー の移動」「Test Generateの中断」「要素の取得」「toBeVisible アサーション 」「toContainText アサーション 」「toHaveValue アサーション 」を対応します。 今まではTest Generatorの中断や要素の取得はできていました。しかし、 アサーション は対応していなかったのでユーザが自身で書かなければなりませんでした。 v1.40からは簡単なテストであれば、最初から最後までTest Generatorで書くことが可能になります! toBeVisible アサーション 使い方は簡単で、目のマークをクリックし、 アサーション したい要素をクリックするのみです。 上画像のように「 ラク スの思い」というlinkをクリックすると、下記のテストコードが生成されます。 await expect ( page.getByRole ( 'link' , { name: 'ラクスの思い' } ) .first ()) .toBeVisible (); toContainText アサーション これも同様に、'ab'のアイコンをクリックして、 アサーション 対象をクリックします。 すると、textBoxが出てくるのでここに含むと思われるテキストを入力します。 チェックマークをクリックすればコードが生成されます。 await expect ( page.locator ( '#APjFqb' )) .toContainText ( 'ラクス' ); toHaveValue アサーション ここも同じです。 アサーション 対象をクリックするだけで生成されます。 await expect ( page.getByRole ( 'combobox' , { name: '検索' } )) .toHaveValue ( 'ラクス' ); v1.41の新機能 screenshot関数のstyleオプション VRT(Visual Regression Test)を行う際にscreenshot関数を使った人も多いと思います。 この関数にはオプションがいくつかあるのですが、v1.41でstyleオプションが追加されました。 このオプションには記述したスタイルを スクリーンショット 作成時に反映してくれます。 下記が具体的な書き方です。 await page.screenshot ( { style: ` ${ カスタムなスタイル } ` , path: ` ${ 画像の保存先のパス } ` , } ); どの様な場面で使えるのかを一例を交えて紹介します。 下画像のようなトップ画面があるとします。(Muiのテンプレを使わせていただきました) このトップ画面をスクショしたいのですが、一定の確率で邪魔なやつが出現します。 styleオプションが出るまでは、消す操作を行ったり、コード自体をいじったりして対応していました。ですが、一定の確率で出現する場合、消す操作をテストコードに入れると出現しなかった時、エラーを起こしてしまいます。 そういう時にこのstyleoptionが使えます。 使い方は簡単で、邪魔なやつの要素を指定して、非表示にするだけです。 テストコードだと下記の様になります。 邪魔なやつには id="obstruction-card" を付与しています。 await page.screenshot ( { style: "#obstruction-card { display : none }" , path: "tests/screenshot/sample.png" , } ); これだけで保存されたスクショには邪魔なやつは写っていないはずです。 toHaveScreenshot関数のstylePathオプション toHaveScreenshotは事前に保存していた画像と指定したpageまたはlocatorの画像を比較し、差分が出ている場合には差分の画像も出力してくれる便利な関数です。 この関数にも先ほどに似たスタイル系のオプションが増えました。 少し違うのは、 スタイルシート ではなく、 スタイルシート へのパスを記述する点です。 下記の様に書きます。 sample.png は事前に用意しておく画像です。 また、stylePath内のスタイルはこれからスクショする画像に対して、反映されます。 await expect ( page ) .toHaveScreenshot ( "sample.png" , { stylePath: "tests/sample.css" , } ); v1.42の新機能 addLocatorHandler関数 ※ これはExperimentalです。 この関数はLocatorとHandlerの2つの引数を取り、そのLocatorに対して、Handlerの内容を実行します。 await page.addLocatorHandler ( $ { Locator } , $ { Handler } ); 具体的な使い道としては、v1.41の一例で出したものと同じでE2Eテスト中に表示される邪魔なやつを処理したいときです。 上画像では「 Sign In」のボタンの上に邪魔なやつが表示されています。 もしこの状態で下記ようなSignInボタンを押す処理が入っているテストを実行すると失敗してしまいます。 test ( "Sign in ボタンを押すと、ログイン画面が表示されること" , async ( { page } ) => { await page. goto( "http://localhost:5173/" ); await page.getByTestId ( "sign-in-button" ) .click (); expect ( page.url ()) .toEqual ( "http://localhost:5173/login" ); } ); この様な場合に、邪魔なやつへの操作を最初に定義することで適切にテストが通ります。 async ( { page } ) => { await page.addLocatorHandler ( page.getByTestId ( "obstruction-card" ), async () => { await page.getByTestId ( "card-cancel-button" ) .click (); } await page. goto( "http://localhost:5173/" ); ... ) タグの追加 各テストに対して、タグがつけられる様になりました。 test関数の第二引数のオプションから指定します。 付けたいタグが1つの場合はstring型でも大丈夫です。 test ( 'sampleTest' , { tag: [ '@sample' ] , } , async ( { page } ) => { // ... } ); タグを指定できることによって、テストをタグ絞り込みで実行できます。 例えば、 @sample とタグ付けしたテストのみ実行したい場合は、下記の様にテスト実行を行います。 npx playwright test --grep @sample 今までのテスト実行では任意のテストのみ実行するのは難しかったので、個人的には非常に嬉しい機能です。 v1.43の新機能 LocatorとFrameLocatorの相互変換 LocatorにはFrameLocatorへ変換をかけるcontentFrameという関数、 FrameLocatorにはLocatorへ変換をかけるownerという関数が追加されました。 // FrameLocator型への変換 const frameLocator = locator.contentFrame (); // Locator型への変換 const locator = frameLocator.owner (); 個人的にはFrameLocatorオブジェクトを保持しておき、好きなタイミングでLocatorにすぐに変換できるowner関数が嬉しいです。 clearCookiesのフィルター機能 Cookie を削除するclearCookies関数のオプションにフィルターの機能が増え、特定の Cookie のみを削除するということが可能になりました。 使い方は下記の様に Cookie 情報を引数に与えることでフィルターをかけます。 具体的には、 cookie の name または、 domain または、 path を指定します。 await context.clearCookies ( { $ { Cookie情報 } } ); 具体的なテストコードの一例を載せておきます。 適当な Cookie を2つ設定し、 name : id-1 のものを削除しています。 test ( "clearCookies id-1" , async ( { context } ) => { await context.addCookies ( [ { name: "id-1" , value: "value-1" , url: "http://localhost:5173/" , } , { name: "id-2" , value: "value-2" , url: "http://localhost:5173/" , } , ] ); await context.clearCookies ( { name: "id-1" } ); const cookies = await context.cookies (); expect ( cookies ) .toEqual ( [ { domain: "localhost" , expires: -1 , httpOnly: false , name: "id-2" , path: "/" , sameSite: "Lax" , secure: false , value: "value-2" , } , ] ); } ); } ); まとめ 本記事ではPlaywright v1.40~v1.43の中から僕の独断と偏見でアップデートした部分を紹介させていただきました。 個人的にはexperimentalではあるのですが、addLocatorHandlerが一番嬉しい機能でした! Playwrightは去年あたりからユーザが増え、それに伴い、ものすごく便利な機能がどんどんと増えていて、開発で使っている身としては嬉しい限りです。 今回紹介していない機能もありますので、よければ公式サイトの Release Note を見ていただければなと思います。 最後まで読んでいただきありがとうございました!
はじめに こんにちは、新卒2年目の菊池(akikuchi_rks)です。 近年、 Android アプリ開発 のみならず、サーバーサイドの開発言語としてもKotlinが急速に注目を集めています。私自身もKotlinを使ってサーバーサイドの開発を行っており、豊富な機能やシンプルな文法に魅力を感じています。 Kotlinを使用していて特に感じるのは、そのコレクション関数の充実性です。コレクション操作はプログラミングにおいて頻繁に行われるため、これらの関数が豊富であることはKotlinの特長のひとつと言えると思います。 また、これに ラムダ式 を組み合わせることで、よりシンプルで効率的なコーディングが可能となります。 この記事では、Kotlinでコーディングをする際に欠かせないコレクション関数に焦点を当て、その中からいくつかピックアップして紹介していきます。 初心者の方でもわかりやすいように、具体的なコード例も交えながら説明していきますので、ぜひ最後までご覧ください。 はじめに コレクションとは、ラムダ式とは コレクションとは ラムダ式とは コレクション関数の活用例 map filter any, all, none associate, associateBy, associateWith groupBy partition コレクション関数を使うもう一つのメリット ミュータブルな変数を使わないほうがいい理由 1. 可読性が低下する 2. 予期せぬバグを生む まとめ コレクションとは、 ラムダ式 とは まずはそもそもコレクションとは何なのか、 ラムダ式 とは何なのか簡潔に説明します。 コレクションとは まず、Kotlinにおけるコレクションは複数の要素をまとめて管理するためのデータ構造です。 主な種類としては、 List 、 Set 、 Map などがあります。 List : 要素の順序付き集合。同じ要素を複数持つことができる。 Set : 要素の順序がない集合。重複する要素を持たない。 Map : Keyと値 Value のペアの集合。Keyは一意で重複しない。 val sampleList = listOf( 1 , 2 , 3 ) val sampleSet = setOf( 1 , 2 , 3 ) val sampleMap = mapOf( 1 to "one" , 2 to "two" , 3 to "three" ) println(sampleList) // [1, 2, 3] println(sampleSet) // [1, 2, 3] println(sampleMap) // {1=one, 2=two, 3=three} 上記のようにコレクションを宣言する際には listOf 、 setof 、 mapOf を用いて インスタンス を生成します。 これらのコレクションは読み取り専用のコレクションでそれぞれ宣言した後に要素の追加、削除、更新はできません。 後で要素の変更を行いたい場合にはミュータブル(変更可能な) MutableList 、 MutableSet 、 MutableMap を使います。 しかし、詳細は後述しますがコレクションを扱う場合は、書き換える必要がないものは変更不可のコレクションを使用するのが望ましいです。 そして、これらのコレクションを操作するための関数がコレクション関数です。 Kotlinの標準ライブラリとして用意されており、コレクションの変換、フィルタリング、グルーピングなど様々な操作を行うための関数が揃っています。 ラムダ式 とは ラムダ式 は、無名関数(名前を定義しない関数)をさらにコンパクトにし、最小限の記述量で定義できるようにしたものです。 val square: ( Int ) -> Int = { x -> x * x } val result = square( 5 ) // 25 この例では、 square という名前の変数に、 ラムダ式 が代入されています。 変数自体には名前が付ける必要がありますが ラムダ式 は Int を受け取り、Int を返す無名関数です。 { x -> x * x } という部分が ラムダ式 であり、 -> の左側に引数、右側に実際の関数の処理を記述します。 ラムダ式 は他の関数の引数として直接渡すこともできます。 fun operateNumber(x: Int , operation: ( Int ) -> Int ): Int { return operation(x) } val result = operateNumber( 5 ) { it * it } println(result) // 25 この例では、 operateNumber という関数が定義されています。 この関数は、整数値とそれを操作する関数を受け取ります。 このような関数を引数として受け取る関数に ラムダ式 を直接記述することで関数定義の手間を省くことができます。 ラムダ式 内で使われている it という引数は受け取るパラメータが1つしかない ラムダ式 を記述する際に使うことができる暗黙の引数で -> の左側に引数を宣言をする手間をさらに省くことができます。 前述したコレクション関数は関数を引数にとるものが多いため、 ラムダ式 と組み合わせて使用されることが多く、これらのコレクション関数と ラムダ式 を組み合わせることでシンプルで効率的なコーディングが可能となります。 コレクション関数の活用例 コレクション関数を ラムダ式 と組み合わせて活用することによりコードをシンプルにする具体例をいくつか紹介していきます。 map 以下は整数値のListの各要素を2倍にする際のコーディング例です。 val numbers = listOf( 1 , 2 , 3 ) val result = mutableListOf< Int >() for (i in numbers) { result.add(i * 2 ) } println(result) // [2, 4, 6] このようなListの各要素の値に何らかの変換処理を加えて別のリストを生成する際には map が利用できます。 map は ラムダ式 で各要素に加えたい変換処理を指定することで、その変換処理を加えた結果のリストを生成することができます。 val numbers = listOf( 1 , 2 , 3 ) val result = numbers.map { it * 2 } println(result) // [2, 4, 6] filter 以下は整数値のListから奇数のものだけを取り出す際のコーディング例です。 val numbers = listOf( 1 , 2 , 3 ) val result = mutableListOf< Int >() for (i in numbers) { result.add(i % 2 == 1 ) } println(result) // [1, 3] このようなListから特定の条件を満たす要素のみを抽出して取り出すような処理は filter を使うことで、シンプルに記述することができます。 filter では ラムダ式 に条件式を記述し、その式が true に判定される要素のみを抽出したListが生成されます。 val numbers = listOf( 1 , 2 , 3 ) val result = numbers.filter { it % 2 == 1 } println(result) // [1, 3] filter の逆で ラムダ式 の条件式が false に判定される要素のみを抽出したい場合には filterNot が使えます。 any, all, none 以下は整数値のListの要素に偶数か含まれているかを判定する例です。 val numbers = listOf( 1 , 2 , 3 ) var result = false for (i in numbers) { if (i % 2 == 0 ) { result = true break } } println(result) // true こちらは any を使うことでシンプルに記述することができます。 any はコレクションの要素に対して、指定した条件式が true になるものが存在するかどうかを判定します。 val numbers = listOf( 1 , 2 , 3 ) var result = numbers.any { it % 2 == 0 } println(result) // true any と同じようにコレクションの要素がある条件に合致しているかどうかを判定するコレクション関数に all や none があるので併せて紹介します。 any : 一つでも条件を満たす要素があれば true (空の場合は false ) all : 全ての要素が条件を満たせば true (空の場合は true ) none : 一つも条件を満たさなければ true (空の場合は true ) いずれも使い方は any と同様です。 associate, associateBy, associateWith 以下は文字列のリストから元の文字列をKey、その文字列の長さを Value としたMapを生成する例です。 val colors = listOf( "red" , "yellow" , "blue" ) val result = mutableMapOf< String , Int >() for (color in colors) { result[color] = color.length } println(result) // {red=3, yellow=6, blue=4} このようにListからMapを生成する際は associate が使えます。 associate は Key to Value といった形式のPair型を返す ラムダ式 を指定することで、指定したKeyと Value のMapを生成することができます。 val colors = listOf( "red" , "yellow" , "blue" ) val result = colors.associate { it to it.length } println(result) // {red=3, yellow=6, blue=4} associate と似たコレクション関数に associateBy , associateWith があります。 associate : ラムダ式 でKeyと Value をPair型で指定 associateBy : ラムダ式 でKeyを指定、 Value は各要素の元の値となる associateWith : ラムダ式 で Value を指定、Keyは各要素の元の値となる Keyをカスタムしたいか、 Value をカスタムしたいか、あるいは両方カスタムしたいかで使い分けます。 例として associateWith を用いて上記の処理を書き換えると以下のようなコードになります。 val colors = listOf( "red" , "yellow" , "blue" ) val result = colors.associate { it.length } println(result) // {red=3, yellow=6, blue=4} groupBy 以下は整数値のListの要素を偶数と奇数に分類する際のコード例です。 val numbers = listOf( 1 , 2 , 3 , 4 , 5 ) val evens = mutableListOf< Int >() val odds = mutableListOf< Int >() for (i in numbers) { if (i % 2 == 0 ) { evens.add(i) } else { odds.add(i) } } val result = mapOf( "even" to evens, "odd" to odds) このようなコレクションの要素をある特徴によって分類したい場合は groupBy を使用することでシンプルに記述することができます。 groupBy は ラムダ式 が返した値をKeyとしてその値ごとに各要素を分類し、Mapとして返します。 val numbers = listOf( 1 , 2 , 3 , 4 , 5 ) val result = numbers.groupBy { if (it % 2 == 0 ) "even" else "odd" } println(result) // {odd=[1, 3, 5], even=[2, 4]} ここでは、偶数の場合に返される文字列 even と奇数の場合に返される文字列 odd をKeyとしているため、リストの値が偶数の場合は even をKeyとする Value のリストに値が格納され、リストの値が奇数の場合は odd をKeyとする Value のリストに値が格納されます。 ちなみに、 groupBy の第二引数にさらに ラムダ式 を指定すると、各要素に任意の変換処理を加えた値をMapに詰めることができます。 groupBy と前述した map の処理を同時に行うことができ、かなりコードがコンパクトになります。 val numbers = listOf( 1 , 2 , 3 , 4 , 5 ) val result = numbers.groupBy({ if (it % 2 == 0 ) "even" else "odd" }, { it * 3 }) println(result) // {odd=[3, 9, 15], even=[6, 12]} partition groupBy の例のように、単純にリストの要素を2つのグループに分割するだけであれば partition も利用することができます。 val numbers = listOf( 1 , 2 , 3 , 4 , 5 ) val result = numbers.partition { it % 2 == 0 } println(result) // ([2, 4], [1, 3, 5]) partition は groupBy と同様にコレクションの要素を ラムダ式 で指定した条件で分割する関数です。 groupBy と異なるのは partition は ラムダ式 に true , false で判定される条件式を指定する必要がある点と、返される値がMapではなくPairであるという点です。 partition では ラムダ式 の条件が true となった要素がfirstのリストに格納され、 false となった要素がsecondのリストに格納されます。 コレクション関数を使うもう一つのメリット 上記の活用例であげたコードを見ていて気付かれた方もいるかと思いますが、コレクション関数を活用することで var や mutableList といったミュータブルな変数の使用を減らすことができます。 この変数の可変性を減らせることがコレクション関数を使用する重要なメリットの1つであると思っています。 ミュータブルな変数を使わないほうがいい理由 ミュータブルな変数をなるべく使わないほうがいい理由を以下に挙げます。 1. 可読性が低下する 変数が再代入できるようになっていると、その変数の値がどのように変化するかを追跡するのが難しくなります。 その結果、コードの理解やメンテナンスが難しくなります。 2. 予期せぬバグを生む 変数の再代入は、その変数を参照している他の部分に影響を与える可能性があります。 特に、大規模なアプリケーションでは変数の書き換えが思わぬ箇所に影響を与え、予期しないバグや振る舞いを発生させる可能性が高くなります。 これらのデメリットを踏まえると、ミュータブルな var や mutableList といった注意深く使用する必要があり、使用は最小限に抑えたほうが良いです。 以上の理由から、コレクション関数を活用してミュータブルな変数の使用を抑えることはコードの可読性、堅守性を上げることにも繋がると言えます。 まとめ Kotlinのコレクション関数と ラムダ式 はコードをシンプルで効率的にするための強力なツールです。 これらを使いこなすことで、コードの可読性を向上させ、より効率的に開発を進めることができます。 さらに、コレクション関数と ラムダ式 を活用することでミュータブルな変数の使用を抑えられ、コードの堅牢性向上も期待できます。 Kotlinには本記事で紹介したコレクション関数以外にも便利なものが数多く揃っているので、今後もこれらのコレクション関数を活用して効率的な開発を行っていきたいです。
※注意:本記事内での計測結果は記載の条件下によるものとなります。異なる環境においては異なる結果が予想されますのでご認識ください。 こんにちは。 株式会社 ラク スにて、主に先行技術検証を担当している「技術推進課」という部署に所属している鈴木( @moomooya )です。 ラク スの開発部ではこれまで社内で利用していなかった技術要素を自社の開発に適合するか検証し、ビジネス要求に対して迅速に応えられるようにそなえる 「技術推進プロジェクト」 というプロジェクトがあります。 このプロジェクトで「DBセキュリティ」にまつわる検証を行なったので、その報告を共有しようかと思います。 今回はDBセキュリティの中でも、DBデータの暗号化の話が中心となります。 ちなみに中間報告時点で公開した記事はこちらになります。 tech-blog.rakus.co.jp DBセキュリティについて 本記事におけるDBセキュリティ カバーする範囲 カバーしない範囲 今回扱うデータ暗号化手法 今回実施した検証内容 検証概要 比較した環境 共通した条件 AWS実行環境 テストデータ 検証結果 ディスク暗号化ではほぼ性能劣化は見られなかった 暗号化カラムへの操作 非暗号化カラムへの操作 CPU, メモリへの負荷 性能面以外の注意点 LUKSはバックアップ運用や、サーバー再起動時に注意が必要 まとめ 暗号化手法ごとの特徴 LUKS pgcrypto TDEforPG 導入するとしたら ストレージ盗難対策なら マイナンバーなどの機微情報を保護するためなら DB全体の暗号化をするなら DBセキュリティについて 本記事におけるDBセキュリティ カバーする範囲 本稿で扱う「DBセキュリティ」については入室管理、アカウント管理、権限管理、データ暗号化、アクセス監視といった部分を対象とし、中でも技術的な検証が必要になるデータ暗号化について検証を進めていきます。 これらは 個人情報の保護に関する法律についてのガイドライン(通則編) にて 10-6 技術的安全管理措置 として記述されています。 カバーしない範囲 「DBセキュリティ」の範囲として定義した通り、 ウェブアプリケーション 経由のセキュリティは扱いません 。 なので DBMS への通常アクセスによるセキュリティに関しては対象外とします。 今回扱うデータ暗号化手法 今回の検証で扱ったデータ暗号化手法は以下のとおりです。 ディスク暗号化 DBのデータファイルが格納されているディスクごと暗号化 LUKSを利用 DB機能による透過的暗号化 DBMS 側でデータ入出力時に暗黙的に暗号化/復号化を行う NEC 製の Transparent Data Encryption for PostgreSQL を PostgreSQL (以下、TDEforPG)に組み込んで利用 機能として列単位暗号方式(カラム指定で暗号化)と行単位暗号方式(指定テーブルの全カラムを暗号化)があるので両方検証 プロプライエタリ な製品なので詳細な計測結果は非公開とします アプリケーションによるデータ暗号化 アプリケーションによりデータを暗号化してから DBMS に格納 pgcryptoを利用 gitlab.com jpn.nec.com www.postgresql.org 今回実施した検証内容 検証概要 今回の検証では暗号化処理による性能劣化度合いの計測を中心に取り組んでいきます。 計測方法としては、非暗号化環境と各種暗号化環境を構築し、それぞれの環境で同一の性能計測テストを実施、測定結果の差分を暗号化処理によって劣化した性能として見ていきたいと思います。 性能計測テストでは 参照系としてselect 暗号化カラムを条件にした場合 非暗号化カラムを条件にした場合 更新系としてinsert, update を中心とした計測を行いました。 計測観点では 処理速度 CPU負荷 メモリ負荷 の観点で計測を行なっています。 比較した環境 非暗号化環境 この環境での計測結果を基準値とする LUKS環境 ディスク暗号化 pgcrypto環境 暗号化カラムと非暗号化カラムが混在するテーブルを持つ TDEforPG(列暗号方式) 暗号化カラムと非暗号化カラムが混在するテーブルを持つ TDEforPG(行暗号方式) 全てのカラムが暗号化されたテーブルを持つ 各環境は AWS EC2上に作成しており、EC2起動テンプレートを用いて環境構築を行った。 非暗号化環境の起動テンプレートをベースとして、暗号化手法のみが差分となるように各環境の起動テンプレートを作ることで環境差異を暗号化手法のみとなるようにした。 共通した条件 AWS 実行環境 EC2 m4.xlarge EBS gp2 10GB EC2で利用した インスタンス は性能が可変のTシリーズではなく、通常の汎用 インスタンス であるMシリーズを利用しています。 ストレージについてはEBSの汎用 SSD gp2を利用しています。 テストデータ 100万レコードのテーブル データの内容はランダムな値をデータとして格納 データ型は整数、実数、テキスト、真偽値を持つ テストデータとしては実際のサービスで利用したときの処理速度に近くなるようにある程度のデータを用意しました。 検証結果 暗号化処理が増えることにより数十%程度の性能劣化は想定していましたが、一部のパターンで注意すべき性能劣化を計測することができました。 ディスク暗号化ではほぼ性能劣化は見られなかった LUKSによる暗号化環境では、今回計測した全ての項目で非暗号化環境とほぼ同じ性能結果になりました。 厳密には128bitでの暗号化時は劣化なし。256bitの場合は10%程度の劣化が見られる感じです。 LUKSについてはディスクパフォーマンスの ベンチマーク も行いましたが、こちらではランダムライトで性能劣化が見られましたが、それ以外は性能劣化はありませんでした。 しかし、ウェブ上で事例を探すと SAS HDDの場合にシーケンシャルI/Oが劣化したり、NVMe SSD の場合にCPU負荷が上がる事例がありました。 今回はgp2を利用しましたが、EBS gp3を利用していたらCPU負荷の増加があったかもしれません。 暗号化カラムへの操作 どの暗号化方式でも処理速度の劣化は発生しました。 特に注意する必要があるのはpgcryptoで、暗号化したカラムを条件にしたselectで7,600倍 の処理速度の劣化が見られました。実測値として、非暗号化時の 74ミリ秒に対して9.4分 です。 TDEforPGの場合だと、行単位暗号化で4倍程度、列単位暗号化で2倍程度の劣化で済んでおり、性能面でかなり優位性があります。 暗号化カラムを条件とした検索が必要な場合はpgcryptoではなく、TDEforPGを選択する強い理由になると思います。 非暗号化カラムへの操作 pgcryptoに関しては非暗号化カラムを条件としたselectでも10〜20%程度の処理速度劣化の影響が見られました。 また pgcryptoではinsertでも75倍の処理速度劣化、updateでは25倍の処理速度低下 が見られました。 これはインデックスが有効な状態ではそれぞれ、17倍、18倍程度まで軽減されることを確認できました。 TDEforPGを用いた場合は、行単位暗号化、列単位暗号化ともに若干影響はあるように見えましたが劣化は5〜10%程度であり、あまり問題ない範囲かと思われます。 CPU, メモリへの負荷 今回の検証においては、すべての操作で暗黙的に暗号化/復号化が行われるTDEforPGの行暗号化方式で、CPUの平均で30%程度、メモリピークで30%程度の負荷増加が見られました。 それ以外の暗号化手法については誤差程度の差しか観測できませんでした。 性能面以外の注意点 LUKSはバックアップ運用や、サーバー再起動時に注意が必要 性能面ではあまり心配のいらないLUKSですが、ディスク全体を暗号化する都合上LUKSヘッダーという領域を必要とします。 LUKSヘッダーが破損するとディスク全体が読めなくなるため、バックアップ運用にてLUKSヘッダーもバックアップ対象に含める必要があります。 また、サーバー再起動時には都度暗号化ディスクの再マウントが必要になります。自動化することが可能なので設定しておくと良いと思います。 まとめ 暗号化手法ごとの特徴 LUKS Linux の標準機能の一つとなっており、性能面への影響も少ないため導入しやすい。 ただしディスク暗号化のため、ストレージの盗難 1 にしか対処できず OSへの不正ログイン DBMS への不正ログイン が行われた場合には効果がない。 pgcrypto PostgreSQL の公式モジュールとして提供されているため導入ハードルは低い。 また、アプリケーションでの暗号化となるため、 ストレージ盗難 OSへの不正ログイン DBMS への不正ログイン に対しても耐性をもつ。 しかし、性能面での影響は大きく、特に暗号化カラムを条件としたselectは非実用的に思える。 検索条件にならないような機微情報カラムをピンポイントで暗号化するようなケースが使い所となる。 実装面でもアプリケーションのDBアクセス部分の改修が必要になるためDB全体および広い範囲を暗号化するような用途には向かない。 TDEforPG DBMS 機能として動作する透過的暗号化機能なので ストレージ盗難 OSへの不正ログイン への対策となる。 性能面はpgcryptoよりも全体的に良好で、暗号化カラムを検索条件にしなければならないときには有力なソリューションとなる。 商用製品のためコスト面で何らかの対応が必要になるが、利用料金も極端に高額なわけではない 2 ため、昨今のセキュリティ意識の高まりを考慮すると現実的な落とし所を見つけられそうです。 導入するとしたら ストレージ盗難対策なら ストレージ盗難のリスクに対応するためであれば LUKS はデメリットが少なく、導入ハードルは低いといえます。 マイナン バーなどの機微情報を保護するためなら pgcrypto によるカラム単位での暗号化で良いと思います。検索条件にしなければ現実的な対応だと思います。 もし暗号化カラムを検索対象にする必要があるのであれば、 TDEforPGの列単位暗号方式 の導入を検討する必要があります。 DB全体の暗号化をするなら ストレージ盗難対策までで良ければ先述の通り LUKS が良いと思います。 OSへの不正ログインまで対応するのであれば、 TDEforPGの行単位暗号方式 を用いた透過的暗号化の導入を検討することになります。 いかがでしたでしょうか。 思っていたよりもツールによって棲み分けができているように思えました。 技術選定の助けとなれば幸いです。 物理ストレージの盗難ももちろんですが、仮想ストレージファイルの盗難も含みます。 ↩ 具体的な金額はお問い合わせください。 ↩
はじめに 皆さんこんにちは、新卒1年目新米エンジニアのkananpaです。 今回は、ネットワークにおいて重要な概念であるサブネットについて、実際の業務で学ぶ機会があったため、まとめてみました。 私自身、名前は聞いたことがあったものの今回はじめて詳しく調べました。 初学者の方にも理解してもらいやすいようにまとめたため、最後まで読んでいただけるとありがたいです。 はじめに サブネットとは IPアドレスとは サブネットマスクとは ネットワークアドレスの計算方法 PHPによる実装方法 まとめ サブネットとは サブネットとは、あるネットワーク内の小さなネットワークのことを指し、 この小さなネットワークに分割することをサブネット化するといいます。 このサブネット化には以下のような役割があります。 IPアドレス の効率的な管理 例えば、 IPアドレス を割り当てる際にクラスアドレッシングによりクラスBで割り当てるとき、最大65536個のデ バイス に割り当てることができます。 しかし、そんなに多くのデ バイス がない場合、多くの IPアドレス が未使用となり無駄になります。 サブネット化を行うと、格納するデ バイス の台数に合ったネットワークを構築できるため、無駄になる IPアドレス を減らすことができます。 このことにより、 IPアドレス の枯渇問題の解消にもつながります。 ネットワークルーティングのパフォーマンス向上 サブネット化されていない状態で数百万台ものデ バイス が存在するネットワークの中で一つのデ バイス と通信したい場合、一つ一つに通信し該当のデ バイス を探します。 しかし、サブネット化し小さなネットワークに分割することで、最初から該当のデ バイス の存在する小さなネットワークに通信できるため、無駄な通信を格段に減らすことができます。 組織の要件に合わせた柔軟なネットワーク設計とセキュリテイの強化 「あるデータにおいてデ バイス Aにのみアクセスを許可したい」など同じネットワーク内のデ バイス でもアクセス制限を設けたいといった場合があります。 そのような場合に、 サブネットマスク でネットワークを分けることで上記のような設計が容易になります。 IPアドレス とは IPアドレス とは、インターネットに接続するデ バイス すべてに割り当てられた住所のようなもので、0~255の数字4組で表現されたものです。 この IPアドレス の表記はネットワーク部と ホスト部 に分かれており、このネットワーク部と ホスト部 を識別するための数値の事を サブネットマスク といいます。 サブネットマスク とは サブネットマスク とは前述の通り、ネットワーク部と ホスト部 を識別するための数値の事で、この数値によりサブネット化されたネットワークの範囲を指定することができます。 サブネットマスク の表記には、 IPアドレス 同様0~255の数字4組で表現されたものと、 IPアドレス の後に「/」をつけてビット数を指定するものがあります。 以下の図のように「123.45.67.89」という IPアドレス に「255.255.255.0」という サブネットマスク が指定されている場合を考えます。 この サブネットマスク を2進数に直すと「1」の数が24個になります。この左から3組までがネットワーク部となり、3組の数値が一致するものが同じネットワークとなります。 ネットワークアドレスの計算方法 サブネット化されたネットワークに通信する場合、ルータは IPアドレス から サブネットマスク 値を用いてネットワークアドレスを算出して、割り当てます。 それではこの算出方法を紹介します。 ① IPアドレス と サブネットマスク 値を2進数に変換する ② 論理積 (AND演算)を求める これより算出された値がネットワークアドレスとなります。 論理積 では 以下表のように2つの入力値が共に真(1)の場合にのみ真(1)を出力し、それ以外の場合には偽(0)を出力します。 入力値1 入力値2 出力値 偽(0) 偽(0) 偽(0) 偽(0) 真(1) 偽(0) 真(1) 偽(0) 偽(0) 真(1) 真(1) 真(1) IPアドレス からネットワークアドレスを算出することで、その IPアドレス がその サブネットマスク 内に含まれているかを判別することができます。 PHP による実装方法 以下は上記ネットワークアドレスの算出を PHP で実装したものです。 IPアドレス は通常4組の数字で書き込まれるため、まずlong形に変換します。 そして、コンピューターは IPアドレス を2進法のコード (1と0の連数) として読み取るため、ビット 演算子 ( 論理積 )を用いて計算していきます。 <? php $ ipAddress = "123.45.67.89" ; //指定IPアドレス $ subnetMask = 24 ;     //CIDR形式のサブネットマスク指定 // 指定されたIPアドレスをlong型に変換 $ ipAddressLong = ip2long ( $ ipAddress ) ; // サブネットマスクを数値に変換. $ subnetMaskNumeric = -1 << ( 32 - $ subnetMask ) ; //CIDR形式でない場合はそのままip2long("255.255.255.0")を使えばよい // リクエストIPをサブネットマスクでビット演算. $ networkAddress = $ ipAddressLong & $ subnetMaskNumeric ; まとめ 今回はサブネットについて基本的な概念から計算方法までを紹介させていただきました。 私のようにネットワークについて触れる機会は少ないという方でも、 IPアドレス やサブネットといった名前を耳にする機会が多々あると思うので、 基本的な仕組みを理解したいという方に参考になればうれしいです。
はじめに こんにちは、株式会社 ラク ス開発本部長の公手です。 普段はブログを書くことが少ないのですが、今回は当社のエンジニアやデザイナーたちが特に大切にしている顧客視点について共有したいと思い、投稿することにしました。 この投稿を通じて、社内のエンジニアやデザイナーに顧客視点の重要性を再確認してもらい、それぞれの役割の中で使い勝手の良い SaaS を開発するためにどのようなアクションを起こすべきかを考えてもらえるきっかけになればとの狙いもありますし、 ラク スの開発組織が顧客視点を最優先に考える組織であることを、社外のエンジニアの皆さんにも知っていただければ幸いと考えております。 はじめに ラクスがプロダクト開発において徹底してきたミッション 欠けていた顧客視点(持っていると勘違い) 顧客視点の獲得に向けて 顧客視点獲得の効果 組織の成長による顧客視点低下への対策 ミッションの言語化と浸透 圧倒的な使いやすさを求めて ラク スがプロダクト開発において徹底してきたミッション ラク スの開発本部が掲げるミッションは、 「顧客をカスタマーサクセスに導く圧倒的に使いやすい SaaS を創り提供する」 です。 実は2017年と結構最近までは 言語化 されておりませんでしたが、2000年代初期の SaaS 開発時から徹底してこのミッションを果たしてきたと思っています。 ラク スはベストオブブリード型のプロダクトを提供することを戦略としています。 当社の代表的なプロダクトである、楽楽精算や楽楽明細という名称を聞くと、楽楽シリーズという統合スイート型プロダクトをイメージされるかもしれませんが、実際にはプロダクト間連携機能などはあまりなく、例えば楽楽精算は経費精算分野で、楽楽明細は請求書発行分野で、それぞれがベストとして選ばれるプロダクトになることを目標として単独進化・開発が進められてきました。 他プロダクトの仕様や使い勝手で引っ張られることもありません。その領域の顧客の声を真摯に受け止め、プロダクトに反映していけば、その分野においては顧客のペインを確実に解決できる良いプロダクトを最短で開発できます。 まだベストオブブリードという言葉も広まっていなかったこともあり、明確にそういう戦略を意識して開発してきたわけではありません(というのが私の感覚)。各プロダクトにおいて、その分野に特化して、ビジネスチームと開発チームが一丸となって顧客ペインの解決にひたすら取り組んできた結果、顧客に高く支持されるプロダクトにまで成長し、幾つかのプロダクトはその領域で高いシェアを獲得することができました。 ※複数の高いシェアを持つプロダクトがあることも ラク スの大きな特徴の一つです。 のちのちベストオブブリードという言葉を知り、「あぁ、僕らがやっているのはこれなんだ」と思った記憶があります。 この成功の背景には、エンジニアやデザイナーたちが「顧客の成功を支えるためには使いやすさを徹底的に追求すべきだ」という考えを自身に深く根付かせ、それを実際に徹底して実行してきたことがあります。 欠けていた顧客視点(持っていると勘違い) プロダクト開発(だけでなくあらゆるサービス)においては、顧客視点(顧客解像度を高く持ち顧客と同じ視点に立てること)を持つことは今や当たり前に語られていると思います。ソフトウェア開発の現場でも、エンジニアやデザイナーが単に技術的なスキルを持っているだけでは不十分で、実際にユーザーや顧客が求める価値を提供できるよう、その視点やニーズを深く理解することが当たり前に求められています。 わかったつもりになっているという罠がありますが、当社で最初の SaaS プロダクトの「メールディーラー」の開発を2003年に当社の創業メンバーから引き継いだ私もその罠にはまっていました。営業資料の読み込みや役員からのプロダクトの説明を受けただけで顧客を理解したつもりになっていました。 当時のメールディーラーはまだ PMF (Product-Market Fit)にも至っておらず、私と二人三脚でメールディーラー事業を担当していた当時の事業責任者HIさんは PMF を目指して奮闘していました。そうしてようやくECショップの店長さん達に刺さり、その領域で PMF を達成しました。そこで上がってくる機能要求は私の想像していたものと違うものも多く、HIさんと意見の相違もありました。顧客の解像度のレベルが全く違うために私が開発するものがHIさんのイメージするものと微妙に違うということが結構ありました。 顧客視点の獲得に向けて 当時は小さな ベンチャー企業 だったこともあり、夜中遅くまで開発をしていた(笑)私に大変気を使いながら、私の顧客視点強化を目指し、いろいろな取り組みを提案(やってくれ依頼)してくれました。 営業同行する 実際に営業もしてみる 営業の商談議事録を読む システム障害時後に報告書を書いて顧客のところに謝罪に行く プロダクトに関するメールでの顧客対応をすべてやる などです。 「こんなことまでやらないといけないのか」と思いつつやっていましたが、やるうちに顧客解像度が相当上がり、HIさんや新しく入った営業責任者とかなり近いレベルまで顧客理解が進みました。 メールのサポートは特にしんどかったですね。。操作の流れをテキストで説明するのは難しく、うまく説明できずに何度もメールでやり取りをする、時にはお電話で説明させていただくということありました。 説明しなくても理解してもらえるUI/UXを作ろう、と心の底から思いました(笑) 顧客訪問すると、使っているブラウザ、モニタのサイズなんかも目で見てリアルにわかります。 顧客視点獲得の効果 その後は認識のずれも相当少なくなり、仕様の決定などもスムーズになりました。 また、マインド面でも大きな変化がありました。人は困っている人を助けたくなる性質があり、知っている人だとなおさらその思いが強くなります。顧客解像度が高くなればばなるほど親身に顧客のことを考えられるようになり、早く顧客を助けたいという思いが強くなりました。新機能開発、バグの修正、トラブル時の対応速度もかなり早くなりました。顧客解像度の高いエンジニアとそうでないエンジニアでは行動が変わってきます。例えば、一つのバグが引き起こす事象によって、解像度が高ければ、単にその操作ができないということだけでなく、その操作ができないことにより、そのユーザーがどのように困るのかを想像できます。それが想像できるかできないかで、バグ修正の速度感や判断が変わってきます。これはインフラで障害が発生した場合などの対応でも同様のことが言えます。 その後しばらくはメールディーラーの開発チームでは営業同行やメールサポートを必ず経験させるようにしてきました。 組織の成長による顧客視点低下への対策 組織が大きくなるにつれ役割の分担が進み、カスタマーサクセス(CS)部門、営業部門、 マーケティング 部門、開発部門がそれぞれ専門組織化しました。開発部門は効率化を求めて開発に注力すべきとなってきました。営業やCSから席も遠くなるなどして顧客の声を感じられる機会も減り、エンジニアやデザイナーの顧客解像度は自然と低くなってきやすくなります。 とはいえ、エンジニアやデザイナーが300人近くなった現在でも、それぞれが高い顧客視点を保持できていると思っています。 どういう工夫があるのかというと、実は特段工夫はありません。当たり前に常日頃から顧客理解の大切さを訴え続けるのみです。 顧客視点を高く保つことの大切さは常々エンジニアマネージャーからメンバーに伝えられ、顧客ファーストで判断をしているのかメンバーへの問いかけが日々続けられています。 それ以外でも、(メールでの顧客対応はさすがにやらなくなりましたが)各プロダクト開発チームの判断で、CSのメール対応を読んだり、入社後の営業同行などを継続してくれていたりもします。 プロダクト開発の各工程にはCSや営業部門には積極的に入ってもらい、意見をどんどん吸い上げています。 ミッションの 言語化 と浸透 カルチャーとして顧客視点を最重要視し開発することは普通に行われていたのですが、 より浸透させて根付かせるため、また採用の際にも説明しやすいようにと2017年に冒頭で紹介したミッションとして 言語化 しました。 「顧客をカスタマーサクセスに導く圧倒的に使いやすい SaaS を創り提供する」 現在ではマネジメント層だけでなく、インナー ブランディング 担当チームがあり、ミッションの浸透や顧客視点強化を訴え続けてくれています。 半期に一度このミッションの浸透度や理解度、顧客理解の重要性などを部門内アンケートで確認しています。 圧倒的な使いやすさを求めて 開発組織にはバックエンドエンジニア、フロントエンドエンジニア、UI/UXデザイナー、プロダクトマネージャー、AIエンジニア、アーキテクトなど様々なエンジニア・デザイナーがおります。それぞれが自身の専門性を活かしながら「何がお客様にとって最高に使いやすいものなのか?」を追求して開発をしてくれています。高い顧客視点をもって我々のミッションを遂行していけば、今後も高く顧客に支持されるプロダクトを生み出していけるはずです。 社内のエンジニア・デザイナーの皆さんがこの投稿を読むことで、改めて顧客解像度を高く持つことの重要性を理解し、さらなる顧客視点の強化に努めていただけると幸いです。また、この投稿が(あまりたいしたことは書いていないですが)社外のエンジニア・デザイナーの皆さんの参考になり、よいプロダクトが世に生み出されることの一助となって人々のペインが解決されていけば幸甚でございます。 最後にですが、もし我々のミッションに共感されて興味を持たれた方がございましたら、ぜひカジュアル面談にお越しください。 私も含め当社のエンジニアマネージャーにていろいろとお話しできればと思っています。
こんにちは、メールディーラー開発課のUKoniです。 2023年9月のことですが、弊社で開催した 【ラクスMeetUp】持続的改善の実践/UI刷新・SQL改善・EOL対応 で登壇させていただきました。 そこで話した、長寿サービスの密結合システムからViewを分離した話をご紹介します。 発表資料 speakerdeck.com 発表資料 概要 作業内容 1. 旧画面のコードから機能一覧を作成する 2. IDEの機能を使用して、共通利用するロジックをメソッドに切り出す 3. 切り出したメソッドのユニットテストを作成する 4. ビューロジックとビジネスロジックを分割する 手順 ビューロジック JavaScriptコード HTMLコード(bladeファイル) ビジネスロジック Actionクラス Responderクラス その結果・・・ UIを新しくすることができました。 Before After 今回の結果 苦労した話 今後の展望 まとめ 概要 メールディーラーでは、最近のメールプロダクトのトレンドやお客様からのご要望などを検討した結果、UIを刷新することになりました。 詳細な背景は下記記事を、刷新内容はスライドをご参照ください。 市場トレンドと顧客ニーズを密に連携~老舗メールプロダクトのUI刷新プロセス~ | エンジニアストーリー技術・デザイン情報 | 株式会社ラクス キャリア採用 しかし、以下の理由から現状では無理な状態でした。 多機能かつ複雑なロジックのため、手動テストで品質が担保できない 自動テストがない 作成するにしてもビューロジックと ビジネスロジック が密結合の状態のため、細かい仕様の確認ができない 自動テストの作成が難しいのはビューロジックと ビジネスロジック が混在していることが原因です。 ならば、ビューロジックと ビジネスロジック を分離してしよう、ということになりました。 作業内容 こちらの説明の際に、以下のサンプルコードを元に説明します。 <?php // HTMLのコード start $ isEdit = $ _REQUEST [ 'mode' ] === 'edit' ; if ( $ isEdit ) { if ( $ _REQUEST [ 'input' ] === 'radio' ) { print "<input type='radio' id='male' name='gender' value='0'>男" ; print "<input type='radio' id='female' name='gender' value='1'>女" ; } else { print "<input type='text' id='input' name='text' value=''>" ; } } // HTMLのコード end // JavaScriptのコード start if ( $ isEdit ) { $ js = "alert('edit');" ; } else { $ js = "alert('view');" ; } print "<script> { $ js } </script>" ; // JavaScriptのコード end // PHPのコード start if ( $ isEdit ) { $ sum = 0 ; foreach ( $ _REQUEST [ 'count' ] as $ count ) { $ sum += $ count ; } } // PHPのコード end // HTMLのコード start print "<p> { $ count } </p>" ; // HTMLのコード end 1. 旧画面のコードから機能一覧を作成する 機能落ちや想定外の不具合を防ぐために、どういう機能があるかを一覧にしました。 サンプルコードの機能を一覧にすると、以下のようになります。 2. IDE の機能を使用して、共通利用するロジックをメソッドに切り出す 表題の通りです。 新UIでも使用するロジックを共 通化 します。 3. 切り出したメソッドの ユニットテスト を作成する こちらも表題の通りです。 ここでようやく、旧画面のロジックを「テスト可能な状態」にすることができました。 4. ビューロジックと ビジネスロジック を分割する Laravel/Vue.jsを導入して、テンプレートエンジンに渡すようにします。 手順 旧画面のコードをそのままLaravelへ移植 HTML作成のコードを コメントアウト 機能一覧に従って、新UIを実装 ③で作成した機能のActionクラスの自動テストを作成・実施 想定通りレスポンスパラメータが送信されているかを確認 不要なコードを削除( コメントアウト したコードなど) 機能一覧を使って、受入テストを実施(最終確認) 詳しい内容は、同じ課のメンバーが発表していますので、こちらをご参照ください。 fortee.jp 上記の作業をした結果、コードは以下のようになりました。 ビューロジック JavaScript コード const vm = { mounted () { if ( params . isEdit ) { alert ( 'edit' ) } else { alert ( 'view' ) } } } export default vm ; HTMLコード(bladeファイル) <?php @ if ( $ isEdit === 'edit' ) @ if ( $ input === 'radio' ) < input type = 'radio' id = 'male' name = 'gender' value = '0' > 男 < input type = 'radio' id = 'female' name = 'gender' value = '1' > 女 @ else < input type = 'text' id = 'input' name = 'text' value = '' > @ endif @ endif < script src = "/js/page_a.js" ></ script > < p > {{ $ count }} </ p > ビジネスロジック Actionクラス <?php class PageAAction { protected $ Domain ; protected $ Responder ; public function __construct ( Domain $ Domain , Responder $ Responder ) { $ this -> Domain = $ Domain ; $ this -> Responder = $ Responder ; } public function __invoke ( Request $ request ) : Response { return $ this -> Responder -> response ( $ request , $ this -> Domain -> get ( $ request )) ; } } Responderクラス <?php class PageAResponder { protected $ response ; protected $ view ; public function __construct ( Response $ response , ViewFactory $ view ) { $ this -> response = $ response ; $ this -> view = $ view ; } public function response ( Request $ request , $ data ) : Response { $ isEdit = $ request -> get ( 'mode' ) === 'edit' ; if ( $ isEdit ) { $ sum = 0 ; foreach ( $ request -> get ( 'count' ) as $ count ) { $ sum += $ count ; } } $ this -> response -> setContent ( $ this -> view -> make ( 'page_a' , [ 'isEdit' => ( $ isEdit ) ? 'edit' : 'view' , 'input' => $ request -> get ( 'input' ) , 'count' => $ sum , ]) ) ; return $ this -> response; } } その結果・・・ UIを新しくすることができました。 Before After 今回の結果 自動テストを作成したので、今後の追加実装や改修がしやすくなった クリティカルな不具合が少なかった 新UIへの移行の手法が確立できたので、他の機能でも活用できる 苦労した話 3000行超えの複雑なコードから機能一覧を作るのがしんどかった 旧画面のロジックを移植したことで変数のスコープが変化し、それが原因でバグが発生した 今後の展望 今回の刷新で対象外だった画面を新UIに改修 今回改修したロジックの リファクタリング ADR 実装に沿った実装にする デグレ 防止のために旧画面のロジックをそのまま移植しているため、適切な箇所に実装する まとめ 長寿サービスの密結合システムからViewを分離した話をご紹介しました。 同じような悩みを持っている方のお役に立てたら幸いです。
はじめに こんにちは。 ラク スの経費精算プロダクト「楽楽精算」のプロダクトマネージャー(PdM)組織で責任者をしております稲垣です。 楽楽精算では プロダクトマネジメント に関する専門組織を設けており、市場や顧客ニーズを迅速に製品に反映できるように努めています。 ※具体的な業務内容はPdMメンバーの記事もご参照ください tech-blog.rakus.co.jp PdMはビジネスと開発の架け橋となってプロダクトの価値を最大化するという役割上、必要とされるスキル範囲も広くなります。 下記に紹介する書籍のような業務やスキルが日々関わってきますので、ご参考になれば幸いです。 はじめに 実務に役立つプロダクトマネジメントおすすめ書籍10選 PdM全般 プロダクトマネジメントのすべて 事業戦略・IT開発・UXデザイン・マーケティングからチーム・組織運営まで ジョブ理論 イノベーションを予測可能にする消費のメカニズム INSPIRED 熱狂させる製品を生み出すプロダクトマネジメント All For SaaS SaaS立ち上げのすべて プロダクトマネージャーのしごと 第2版 ―1日目から使える実践ガイド 思考力 イシューからはじめよ――知的生産の「シンプルな本質」 解像度を上げる――曖昧な思考を明晰にする「深さ・広さ・構造・時間」の4視点と行動法 言語化・コミュニケーション力 「言葉にできる」は武器になる。 マーケティング マーケティング思考 業績を伸ばし続けるチームが本当にやっていること ユーザーの「心の声」を聴く技術 ~ユーザー調査に潜む50の落とし穴とその対策 おわりに 実務に役立つ プロダクトマネジメント おすすめ書籍10選 以下に書籍をご紹介していきます。カテゴリは以下の通りです。 PdM全般 思考力 言語化 ・コミュニケーション力 マーケティング PdM全般 プロダクトマネジメント のすべて 事業戦略・IT開発・UXデザイン・ マーケティング からチーム・組織運営まで 及川 卓也 (著), 曽根原 春樹 (著), 小城 久美子 (著) まさに プロダクトマネジメント の教科書的な本といえます。 ジョブ理論 イノベーション を予測可能にする消費のメ カニ ズム クレイトン M クリステンセン (著), タディ ホール (著), カレン ディロン (著), デイビッド S ダンカン (著), 依田 光江 (翻訳) 人がモノを買う行為の本質を理解することができます。 INSPIRED 熱狂させる製品を生み出す プロダクトマネジメント マーティ・ケーガン (著), 佐藤 真治 (監修), 関 満徳 (監修), . (その他), 神月 謙一 (翻訳) プロダクトマネジャーとは何かがわかり、実例も参考になる本です。 All For SaaS SaaS 立ち上げのすべて 宮田 善孝 (著) 事前/深掘り調査とプロトタイプ、開発、GoToMarket戦略、リリースに分けて、検討内容や注意点が丁寧に説明されています。 プロダクトマネージャーのしごと 第2版 ―1日目から使える実践ガイド Matt LeMay (著), 永瀬 美穂 (翻訳), 吉羽 龍太郎 (翻訳), 原田 騎郎 (翻訳), 高橋 一貴 (翻訳) PdMのコアスキルである、コミュニケーション、 組織力 、リサーチ、実行の4つのスキルについての習得法を学ぶことができます。 思考力 イシューからはじめよ――知的生産の「シンプルな本質」 安宅和人 (著) 問題解決、 ロジカルシンキング を使い課題の本質をつかむことを学ぶことができます。 解像度を上げる――曖昧な思考を明晰にする「深さ・広さ・構造・時間」の4視点と行動法 馬田隆明 (著) 物事の本質をつかみ、思考を深めるための方法を学ぶことができます。 言語化 ・コミュニケーション力 「言葉にできる」は武器になる。 梅田 悟司 (著) PdMに必要な 言語化 の重要性を学ぶことができます。 マーケティング マーケティング 思考 業績を伸ばし続けるチームが本当にやっていること 山口 義宏 (著) そもそも マーケティング とは何か、基礎から学びたい方におすすめです。 ユーザーの「心の声」を聴く技術 ~ユーザー調査に潜む50の落とし穴とその対策 奥泉 直子 (著) 具体的なインタビューの方法や、内容について紹介した書籍です。 おわりに 以上、当社のPdM組織で実際に推薦し、役立てている書籍をご紹介しました。 ラク スのPdMはビジネスサイド(営業、製品企画、カスタマーサクセス)、開発、デザインを巻き込んで顧客価値の最大化に努めます。そのため、思考力、 言語化 、コミュニケーションといったソフトスキルの書籍も取り上げました。 実務上は顧客調査に関連した SWOT 、3C分析、STP分析等のほか、顧客解像度を高めるためのインタビューなど直接顧客接点を持つことも多く、開発以外にもビジネスや顧客に関する背景知識は重要となります。 これらの知見をどのように ラク スの業務に活かしているのかは、私たちの登壇での トーク 内容もご覧ください。 logmi.jp 最後に、PdMの仕事にご興味をお持ちいただけましたら、カジュアル面談等も実施しておりますのでお気軽にお申込みください。 ラク スでのPdMの役割等もお話させていただきます。 career-recruit.rakus.co.jp career-recruit.rakus.co.jp
はじめに こんにちは。フロントエンド開発課に所属している新卒1年目のm_you_sanと申します。 3月6日にTypeScript5.4がリリースされました。 そこで、今回は個人的に気になった機能についてピックアップして紹介したいと思います。 はじめに 型の絞り込み NoInfer まとめ 型の絞り込み 関数 クロージャ 内の型の絞り込みの挙動が少し変わりました。 以下の getUrls では、まず始めにtypeof 演算子 を使用して、第一引数の url がstring型かどうかを確認しています。 string型であった場合、 URL の インスタンス を生成します。 その後、 map 関数内で URL のプロパティである searchParams を使用しています。 function getUrls ( url: string | URL , names: string [] ) { if (typeof url === "string" ) { url = new URL ( url ); } return names.map ( name => { url.searchParams. set( "name" , name ); return url.toString (); } ); } このとき従来の挙動の場合、 map 関数の クロージャ 内では、 if (typeof url === "string")... 内で絞り込んだ型情報が保持されず、 url がstringとURLのユニオン型になるため、 searchParams の部分でエラーが発生します。 そのため、 map 関数 クロージャ 内で再度、型情報を絞り込む必要がありました。 return names.map ( name => { // もう一度型情報を絞り込む if (typeof url === "string" ) return; url.searchParams. set( "name" , name ); return url.toString (); } ); しかし、5.4から クロージャ 内で型情報が保持されるようになったため、上記のようにわざわざもう一度型情報を絞り込む必要がなくなりました。 ただし、以下のようにネストされた関数内で、型情報を絞り込んだ変数が代入された場合は、型情報が保持されなくなります。 function printValueLater ( value: string | undefined ) { if ( value === undefined ) { value = "missing!" ; } setTimeout (() => { value = value ; } , 500 ); setTimeout (() => { // エラーが発生する console .log ( value.toUpperCase ()); } , 1000 ); } また、functionを使って関数宣言をした場合、ホイスティングができるため、型情報を保持することができません。 以下のコードの場合、 toUpperCaseFn は if (value === undefined)... より前に呼び出すことが可能なので、 value をstringに絞り込むことができません。 アロー関数の場合は、ホイスティングができないので、正常に型情報を絞り込むことが可能です。 function printValueLater ( value: string | undefined ) { // ここで呼び出すことが可能 // toUpperCaseFn(); if ( value === undefined ) { value = "missing!" ; } function toUpperCaseFn () { // エラーが発生する console .log ( value.toUpperCase ()); } // エラーは発生しない const toUpperCaseFn2 = () => console .log ( value.toUpperCase ()); } NoInfer NoInferは新しく追加されたユーティリティ型で、不要な 型推論 をブロックすることができます。 以下のコードでは、 colors の配列要素の一部が defaultColor の型になるようにしています。 しかし、配列の要素にないものを defaultColor に設定しても、 型推論 が効いてしまうため、型エラーが発生しません。 この場合、 defaultColor から ジェネリクス C を推論させないようにする必要があります。 function createStreetLight < C extends string >( colors: C [] , defaultColor?: C ) { // ... } createStreetLight ( [ "red" , "yellow" , "green" ] , "blue" ); createStreetLightの型情報 そこで、NoInferを使用すると 型推論 がブロックされ、 defaultColor が colors の配列要素の一部であることをチェックできるようになります。 function createStreetLight < C extends string >( colors: C [] , defaultColor?: NoInfer < C >) { // ... } // blueは配列の要素に存在しないため、エラーが発生 createStreetLight ( [ "red" , "yellow" , "green" ] , "blue" ); createStreetLightの型情報 まとめ 今回は、TypeScript5.4の新機能から個人的に気になったものを紹介しました。 クロージャ 内の型情報の保持は、今後開発を進める中で役立つ場面が出てくるのではないかと思いました。 5.4の機能について、更に詳しく知りたい方は、リリースノートをご覧ください。 参考資料 devblogs.microsoft.com
こんにちは。 株式会社 ラク スで先行技術検証をしたり、ビジネス部門向けに技術情報を提供する取り組みを行っている「技術推進課」という部署に所属している鈴木( @moomooya )です。 今回は毎年春先の社内ビアバッシュで新人向けに「一歩目の学習方法」として発表している話をしようと思います。 学習とは この記事の対象 学習に対する向き合い方 まず最初は 学習作戦その1「ちょい足し学習」 例)HTTPメソッドを扱ったとき 学習作戦その2「外から情報を仕入れる」 よくある情報源 技術書 技術同人誌 ウェブサイト 勉強会 SNS 飲み会 GitHub 脱初級者 手を動かす(検証と実践) 自由にできるサーバーを用意する 情報発信 無理に全部やらなくていい エンジニアにとっての勉強 学習とは [名](スル) 1 学問・技術などをまなびならうこと。「学習の手引」「学習会」 2 学校で系統的・計画的にまなぶこと。「英語を学習する」 3 人間も含めて動物が、生後に経験を通じて知識や環境に適応する態度・行動などを身につけていくこと。不安や嫌悪など好ましくないものの体得も含まれる。 4 人工知能 (AI)がデータを読み込み、基準や規則性を見つけ出すこと→ 機械学習   出典: デジタル大辞泉 ( 小学館 ) 誤解を恐れず、噛み砕いて言うとすれば「昨日よりもなにか一つうまくやれるようになること」です。 (4の語義は今っぽいですね) この記事の対象 若手エンジニアに向けたスライドが元ネタになっています。 想定としては今後のITエンジニアとして成長していくための長期的な観点で書いています。短期的な成果 1 につながるような考え方ではありません、というか短期的な成果は現場の仕事を覚えるという一点に収束するので特に触れません。 あと「脱初級」の定義って誰が定めてるんだよ、と思われるかもしれませんが、ターゲットとしている若手エンジニアに関しては「そんな定義気にせず知識と経験をため込む」段階なので定義とか気にしてる段階じゃないです。たぶん本記事の内容が「そんなの日常生活じゃね?」ってなってたら世間的に見ても技術的には多分もう初級じゃないです 2 。 また、経験ある人たちは学習習慣ついていると思うので勝手に高みを目指してください。 学習に対する向き合い方 おそらく学習についての記事を書くと「そんな知識いまさら使うのか?」「もっとこういう部分から学んだほうがいい」「効率悪そう」「タイムパフォーマンスがどうこう……」とかそういう話が出てくると思いますが 3 、長期的に考えたときに最初から 使いそうな知識だけ身につけることは不可能 です。無駄知識を持っていない凄腕エンジニア 4 なんて会ったことありません。 経験を積むことで、今後のキャリアのために必要な内容を取捨選択する、ということはあると思います。しかし最初は気にせず知識を取り込んでいくでいいと思います。いつどんな経験が役に立つかわかりませんしね 5 。 私も仕事としてITエンジニアをし始めたときは「仕事に使う知識を身につける」というスタンスでしたが、全然学習が捗りませんでした。しばらくしてから「仕事とか気にせずに気になったものや興味を持ったものは全部調べる」というスタンスに切り替えてからは無駄な知識も多いですが、学習が捗るようになりました。 まず最初は とはいえ若手エンジニアの皆様に置かれましては目の前の業務を遂行できるように頑張ってもらうのが最優先かな、と思っています。 一方で目の前の業務しかやらないと出来ることが増えないです。IT業界は規格化された部分は多くありますが、ローカルルール 6 などもたくさんあります。 なので、長期的なキャリアのためには業務だけではなく、職場以外を情報源とした学習も並行して行うことをおすすめします。 学習作戦その1「ちょい足し学習」 とはいえ「業務で忙しくて他のことができない」というのは容易に予想できる状況です 7 。 そんな方は業務でやっていることに ちょい足し する「ちょい足し学習」から始めてみましょう。 日常の業務ではわからないことが多く、調査に時間を使うことも多いと思います。また手順書が整備されていて、指示通りに手を動かすだけの整備された環境でも手順書の指示内容が「なぜ正しいのか」理解しきれていないこともあると思います。そんなときに日々の業務に必要な知識を裏付ける知識をちょい足しするのです。 例)HTTPメソッドを扱ったとき GET/POST、次いでPUT/DELETEあたりは触る機会が多いと思いますが、HTTPメソッドはこれだけではありません。 使わなかったけど仕様として定義されている他のメソッドを調べてみる リク エス トやレスポンスのデータ構成を調べてみる ヘッダに設定可能なパラメータを調べてみる テキスト以外のデータの扱いを調べてみる そもそも誰が仕様を定義しているのか など派生的に学べる内容はたくさんあります。 ちょい足し学習した後は必ず何かしらにアウトプットするようにしましょう。ブログでもなんでもいいです。検索して見つかるような場所に記録しておくと良いと思います。1年後には学習内容をすっかり忘れて調べ直しているはずです。そのときに自分が調べた内容の記事が見つかれば、すぐに知識を思い出すことが出来るでしょう。 学習作戦その2「外から情報を 仕入 れる」 興味の幅を広くして、いろいろな情報源に自然と触れる環境を作っておくと 頑張らなくて良くなる ので継続しやすいです。 基本的に触れる情報は多ければ多いほどよいです。ノイズが増えることを恐れるよりは、多い情報から必要なものを取捨選択する能力を鍛えることを考えたほうがいいです。 よくある情報源 技術書 体系的にまとまっているため新しい領域の知識を身につけるのにおすすめ。昔から読み続けられている、いわゆる古典と言われている技術書も普遍的な内容が多いため読んでおくと良いと思います。 古典技術書の名著については過去に記事を書いています。 tech-blog.rakus.co.jp お金に余裕があるなら O'reilly Online Learning (年額$499 8 )の利用も強くおすすめします。 情報学の学位持っていない方は 情報科学 の本を1冊くらい通読しておくと良いと思います。例えばこういうのとか。 https://amzn.asia/d/7Wbvfc3 amzn.asia ボリューム的に結構大変だと思うのでゆっくり読めばいいと思う。最初から読んでもいいけど、 スキルアップ が行き詰まった感を覚えたときに立ち戻ったりするのが良いかも。点と点を繋いでくれたり、応用力がついたり、概念を誤って理解することを減らしたりする助けになるかもしれないです。 技術同人誌 年々賑わってきている印象があります。 コミケ などでも扱われますが、技術書 オンリーイベント としては年2回行われている 技術書典 が国内最大のイベントです。 techbookfest.org 技術同人誌は商業誌で扱われないようなピンポイントなテーマでまとめられた本が期待できます 欲しいテーマで書かれたものが存在するかどうかは運次第なところもあるので、オンラインで指名買いするよりはオフラインでイベント会場を散策して気になる本を買うほうが楽しいと思います。 また情報のアウトプットの場としても有用です。だいたいしっかりした技術ブログ記事数本分の文量 9 があれば薄めの本が1冊作れるかと思います。 執筆時は Re:VIEW という 組版 システムが使いやすいです。 github.com ウェブサイト 基本的に一次情報である公式サイトを参考にするようにしましょう。公式サイトはほとんどが英語サイトとなりますが、なんとか読みましょう。 Google翻訳 を使ってもいいと思います。 他には企業ブログや個人ブログ、各種ニュースサイトなど、気になるものはチェックするようにしましょう。 RSSリーダー を活用してもいいし、ブックマークから直接訪問しても良いと思います。 はてなブックマーク のテク ノロ ジー カテゴリをウォッチするのも良いと思います 10 。 b.hatena.ne.jp 勉強会 コロナ禍を通して状況が色々変わってきていると思いますが、オフラインでの勉強会も有用です。オンラインでも参加しないよりはしたほうがいいのですが、オフラインでの強みは発表コンテンツの後の懇親会にあります。 似たような技術コンテンツに集まっている人たちは似たような課題にぶつかっている人も多く、いろいろな試行錯誤の情報を共有することができます。発表スライドには乗せにくい情報なども懇親会では議論できたりするので、個人的には可能ならオフラインでの勉強会への参加をおすすめしたいです。 コツとしては コンプライアンス 違反にならない範囲で自分が抱えている課題や、過去の泥臭い解決策なんかを開示し つつみん なで共有していく感じです。外向きのキラキラした話とは別に泥臭い実態の話が聞ければ値千金です 11 。 正直勉強会への参加は 地域格差 が大きいとは思いますが、現実として東京(やその他大きな都市)のエンジニアにとっては大きなアドバンテージなので活用してみてほしいです。 イベント探しは今や Connpass 一強だと思うので、Connpassで探しておけばいいと思います。 参加するテーマとしてはこれから取り組むような領域の勉強会に参加してコミュニティの雰囲気とか知るために行くのがおすすめです。 connpass.com SNS これもこの数年でだいぶ状況が変わってきました。 以前は「 Twitter アカウントはエンジニアの名刺 だから必ずアカウントを作りましょう」と言っていましたが正直現状の Twitter (現X)だとそこまで言い切れない……。 Blueskyが招待制を脱したり、Threadsも Facebook や Instagram からの導線を強化してユーザーが増えているようですが、 Twitter (現X)を置き換える程にはなっていないです。Misskeyはエンジニア向けって感じでもないですし、エンジニアがどこに腰を落ち付けることになるかは先数年様子を見ることになりそうです。個人的にはBlueskyあたりに移住するのがいいとは思っていますが……(所有 ドメイン で身分証明とか出来るし)。 現状ではまだ Twitter (現X)で発信しているエンジニアがまだ多いので、一旦は Twitter (現X)アカウントを作って気になるエンジニアを片っ端からフォローしましょう、という感じです。自動的に情報が流れ込んでくる状態を作れるので情報収集が楽になります。 他にも Youtube ではカンファレンスや勉強会の アーカイブ が公開されていたりするので、気になるチャンネルを登録しておくのも良いと思います。 もっと小規模なところで行くとVRChat内でもITエンジニア向けのコミュニティがあったりするので、自分がよく使っている SNS でエンジニアまたはコミュニティをフォローして、日常的に発信された情報が目に入る状態にしておくのが良いと思います。 飲み会 エンジニア同士が飲みに行けば、技術の話に花が咲くってものです。 普段一緒に働いているチームでもいいですが、普段仕事で接していない人と話すと新鮮な話が聞けて楽しいと思います。 大学時代の友人だったり、配属が違う同期であったり、前職の同僚であったり、趣味で知り合った人であったり、探すといろいろいるものです。 飲み会と書いてはいますが、酒を飲まなくても食事だけでも会話をする場が作れればいいと思います。 ただし、チーム外の人と話す際には機密事項には注意して話すようにしましょう。 社名とか製品名とか固有名詞をそのまま発言しないのは大人の嗜みです 12 。 GitHub GitHub も SNS だと言われそうですが、やはりこれは別枠で。 大量の ソースコード が読み放題です。新しめの フレームワーク とかで「 ディレクト リ構造どうしたらいい?」「この処理の実装セオリーはどんなコード?」と悩むことはないでしょうか。そんなときにも オープンソース は役立ちます。同じ フレームワーク で書かれた ソースコード を探して、いくつか読んでセオリーを見いだせます。 当然、利用している OSS ライブラリの挙動調査もできますし、気になる所があればプルリク投げて貢献してもいいです 13 。 ソースコード の修正だけではなく、ドキュメントの翻訳や修正も感謝されると思います。 脱初級者 手を動かす(検証と実践) やはり動くものを作ってみるのがもっとも学びになります。 色々聞いてわかった気になっても実際に実装していくとわからない部分や、うまくいかない部分が見えてきます。 そういった部分を一つずつ解消していくことが確実に力になっていくと思います 14 。 自由にできるサーバーを用意する 私はWebアプリケーションエンジニアなので、学習の際にはアプリケーションを動かすためのサーバーが欲しくなります。 昔はサーバーを1台用意するのは結構お金がかかったので大変だったのですが、今 15 だと3万円くらいから購入できる低価格ミニPCがあるのでこれをサーバーにするのが簡単な方法かと思います。3万円台で Intel N100(第7世代 Core i5 、第9世代 Core i3 くらいの性能 16 )だったり、2.5GbEポートが複数ついてたりするのでネットワークの勉強にも使えそうです。 物理マシンを用意する方法は色々融通が効くので私は好んでいます。 もう少し頑張ってみるとすると、Proxmox VEなどを使って仮想ホストサーバー(≒ プライベートクラウド )を構築してもいいかもしれません。大量に インスタンス を建てなければそれほどのスペックも必要ないはずです。上述の低価格ミニPCでも出来るし、2~3台購入して クラスタリング してもいいし。 www.proxmox.com もうちょっと安く……という方には VPS の利用が良いと思います。(上述の低価格ミニPCよりも)低スペックなサーバーになりますが、月額$4とか600円とかで国内に配置されているサーバーを利用することができます。 海外業者だとDigital Oceanとか Vultr とか、国内業者だと さくらインターネット とかConoHa VPS が有名かと思います。 ちなみに VPS の利用はウェブサイトを公開する場合にも便利です。実害が出るかどうかは別として、ウェブサイトを公開するともれなくクラック対象として攻撃にさらされます。万が一侵入されたときにも 自宅サーバ ーではなく、 VPS (や後述の パブリッククラウド など)であれば被害を限定することができるので多少安心できます。 もっと安く、できれば無料で……という方には大手 パブリッククラウド の無料枠を用いるとか、フロントエンドだけを試すなら静的 ホスティング サイト(NetlifyとかVercelとか)が無料枠内でも結構使えます。 個人的には最初から パブリッククラウド を使うのは、ハマりどころが多い印象があるので学習のハードルが上がると感じています。とはいえ今は利用者も多くなり、出回っている情報も増えました。直接仕事につながりやすいスキルにもなるので選択肢に含むのはアリでしょう。 www.digitalocean.com vps.sakura.ad.jp www.conoha.jp www.netlify.com vercel.com 情報発信 学習したら発信することで更に定着させましょう。 「手を動かす」でも書きましたが、学んだ内容を文章に書き起こしてみると、自分の理解が不足している部分がわかってきます。 不足を埋めるように調べて、調べた内容が正しいか試してみて、文章を補足していくということを繰り返すと確かな知識が身についていきます。 発信する場はどこでもいいと思います。多くの人の目に触れる場所の方が承認欲求が満たされてモチベーションを維持しやすいというのはありますが、逆にプレッシャーに感じてしまう人はパブリックな場所であればどこでもいいと思います。 例えば私の身の回りだと…… テックブログに記事を書く 社内ビアバッシュでLTする 個人ブログとかQiita、Zenn的なところに記事を書く 社外勉強会で発表(LT含む)する みたいな方法が身近な方法です。 ちなみに発信を続けていくと「社外勉強会で登壇依頼される」みたいなパターンもあるかもしれません、ありがたい話ですね。 記事を書くのか、人前で発表するのかは個人の好みもあると思うので好きな方法を取ればよいと思います。 ただ社外勉強会に参加するときはLTでもいいので登壇すると、懇親会での話のタネができて コミュ力 が低くても交流しやすいのでおすすめです。 ちなみにLTについては手前味噌ですがこちらの記事もどうぞ。 qiita.com 無理に全部やらなくていい 本記事に書いた内容を「すべてやらなきゃ」となると多分続かないです、大変すぎる……。 ただ、興味を覚えたものについてはどれかやっている状態を続けるといいと思います。「ここ最近は技術書読みがマイブーム」とか「無償にコードを書きたい/読みたい」とか「勉強会めぐりしたい気分」とか。「ITポエム書きたい」でもいいかもしれない 17 。 そんな生活を数年続けていたらいつの間にか初級者とは見られなくなっていくと思います。『一万時間の法則』の一万時間にはあまり意味がないらしいけど、日常生活に組み込まれるレベルで努力を続けた人は一流になっていくというのはあるかと思います 18 。 エンジニアにとっての勉強 社内のエンジニアと話していてかっこいいセリフがあったのでこれを引用して締めとします。 「勉強とは意識してない。ただの日常生活の一環です」 とはいえ短期的に業界内の認知度向上というか個人 ブランディング みたいな方法はあるにはあります。たとえば特定の フレームワーク などの技術要素に徹底的に特化して学習することで、特定領域の第一人者になる方法があります。ただこの業界は特定の技術はいつか必ず廃れるので、長期的に食べていくためにはあまり特化しすぎるのも考えものかな、と思っています。もっとも第一人者になると人脈なども自然と形成されるので、そこから知識を横に広げていくというは全然ありでしょうけれどね。 ↩ ビジネスという意味ではまた別ですが。 ↩ どうせ はてブ コメントとかにもそういう言う事書かれると思う。 ↩ むしろ膨大な「無駄知識」が凄腕エンジニアを凄腕たらしめているとすら思えます。 ↩ 電子工作(記憶装置を取り付けないと電源オフで状態が破棄されるのが当たり前の世界)と、マイクロサービスやコンテナ化の文脈で頻出するステートレスの感覚が似ていたりとか。 ↩ 自社でしか通じない言葉や、過去のしがらみなどに基づく固有のやり方、予算や製品特性などの制限上セオリーから外れた最適解など。こういうのは無くせるに越したことはないけれど「仕事(≒ビジネス)が出来るエンジニア」になるためには適切に接していく必要があると思います。 ↩ そうでないなら恵まれた環境にあると言えます。十分な学習時間を確保しましょう。 ↩ 個人利用であればO'reilly Online Learningの年会費は ACM 会員になることで合計$174( ACM 会員費$99 + 優待金額$75)で利用することもできます。 ↩ 本記事くらいの文量で3記事分くらいあれば40ページ前後の薄い本が出来ると思います。 ↩ 私は はてな アカウント自体は はてなダイアリー 提供当初の頃に作って使っていましたが、 はてなブックマーク は過去のBANまつりの時期に利用し始めたせいで、初めてブックマークしたタイミングでBANされてしまって以来使えません。 個人的には はてなブックマーク (の運営)は好きではないです。 ↩ 世の中で流行ってるとかモダンとか言われている技術について話していて、表向きは ブランディング などの理由から便利な面をアピールしている場合でも、突っ込んで聞いていくと泥臭い苦労の上で成り立ってたりすることは多々あります。このあたりのエピソードを知っていくと聞きかじりの浅い知識を脱することができるようになってきます。 ↩ 外食してると結構他の席のお客さんから仕事の話が聞こえてくることがあります。たまに「それ機密情報じゃない?」みたいな会話があって他人ながらひやひやしたりも……。 ↩ ちなみに私は初プルリクを投げたら、直前にプロジェクトが凍結されていて無駄に終わったという悲しい過去を持っています。 ↩ 人にもよりますが、うまくいかないことにぶつかるとイライラします。すごくイライラします。正しく設定/実装しているはずなのに動かない、ということはザラです。それでもそれを解決したときの達成感により繰り返してしまうような人がエンジニア向きなんだろうな、と思っています。ちなみにうまくいかない理由はだいたい自分のミスのせいです。 ↩ 少し前だと鼻毛鯖とか安鯖なんて呼ばれていた低価格サーバーがありましたが今はなくなりました。 ↩ ちなみに我が家のサーバーはサーバー用CPUの Xeon を使っていますが、第2世代の Xeon E3-1280という古いCPUなので性能的には Intel N100と同等です。Webアプリケーションの学習用としては特に不便していません。古い分、電力消費が Intel N100(6W)と比べて大きい(95W)ですが。 ↩ Qiitaとかの「技術記事を投稿するプラットフォーム」とされている場所に投稿してると反感買うかもしれないので注意。自分のブログとか SNS なら誰も文句言わないはず……たぶん。 ↩ 色んな人を見ているとどう見ても「努力だけで到達できるように思えないレベル」の人はいると思いますが、そのへんは適性+努力の「超一流」なのでまた別枠かな、と。 ↩
ラク ス ベトナム 責任者の寺田です! 2014年より、 ラク ス ベトナム は、 ラク スの開発子会社として共に SaaS 開発を進めています。 ラク スでは、今後グローバルな開発の重要性が更に増大すると考えており、今回のブログでは、そんな ラク スの日本ー ベトナム 間のグローバル開発の様子と今後の展望を簡単にお伝えしたいと思います。 ラク スのグローバル開発は、日本と ベトナム がお互いにワンチームである意識を強く持ち、開発に取り組んでいる点が特徴です。 その上で、 ベトナム チームには、より重要な役割を担う事が期待されています。そのため、ぜひ、これから入社される方と一緒に、開発領域のさらなる拡大や成長を加速させていきたいです! 海外での開発業務の実施や、海外組織との開発コミュニケーションに興味を持たれている方にとって、この記事が参考となれば幸いです。 ラクスベトナムについて ミッション・ビジョン・バリュー 開発体制と開発プロセス 体制例 コミュニケーション 今後のグローバル開発への想い ラク ス ベトナム について ラク ス ベトナム (以下RV)は、 ラク スが提供する SaaS 開発に特化した ベトナム 、 ホーチミン の開発拠点です。(現在、 ホーチミン には2つの拠点があります) 現在、RVでは ラク スの5サービス(楽楽精算、楽楽明細、楽楽販売、メールディーラー、配配メール)の担当開発チームが立ち上がり、事業成長を支えており、 ラク スの製品開発を進める上で、なくてはならない開発パートナーとなっています。 相互理解・協力体制を重視しており、近年は低退職率(年間5%以下)と共にメンバーが一丸となってチーム成長に取り組み、着実に組織を拡大しています。 ミッション・ビジョン・バリュー RVのミッションは「高技術と高品質で、最高の SaaS サービスを提供することでエンドユーザ、働く仲間に最高のHappyを届ける」です。 そのために「 ベトナム で クラウド ( SaaS )領域でナンバー1の技術、品質を持ち、最高のサービスを提供して、最高にHappyな会社になる」というビジョンを掲げています。 ミッションのビジョンの実現のために、現時点では「Quality First」、「Fast Resulting」、「Deep Thinking」、「Challange」、「Active Communication」、「One for All&All for One」という6つのバリューを常日頃の業務で大事にしつつ、開発に取り組んでいます。 開発体制と 開発プロセス RVは、寄せられている期待に応える形で、年々体制を拡大し続けています。(現在の社員数は、60名ほどです) それぞれのサービスについて、PM、PL、開発メンバー、QAが在籍する様々な規模(5〜20名ほど)の Java / PHP の開発チームがあります。 複数チームが連動したり、QAの専門チームを置いている開発チームもあります。 RVは、主に基本設計、詳細設計、実装、品質管理などの工程を担っており、日本と密接に協力しながら開発を進めています。 体制例 国内エンジニア…PM 国内ブリッジSEチーム(以下、BrSE)…PMO、BrSE RVのエンジニア…PM、PL、PG、QA 通常は、日本国内の開発チーム内で開発全体のPMを アサイ ンし、BrSEがRVとの開発コミュニケーション支援を行います。 RVでは、設計・実装・ 単体テスト ・第 三者 テストを行い、成果物をBrSEが受け入れる形で進めています。 詳細な業務の流れは、下記の通りです。 ① 国内PMから案件提案 ② 国内BrSEチームで案件の要求整理・RVへ連携 ③ RVで調査・設計の提案 ④⑤ BrSE・国内PMの調査・設計内容レビュー ⑥ RVで実装・ 単体テスト 項目の作成 ⑦⑧ BrSE・国内PMのコード・テスト項目レビュー ⑨ RVで 単体テスト ・第 三者 テストの実施 ⑩ BrSE・国内PMでの納品物受入 コミュニケーション 日本側メンバー、 ベトナム 側メンバー共に、ワンチームとして相互理解・協力体制を大切にしています。 開発では、資料・会議ともに、技術翻訳・通訳の専門チームのメンバーが日本語と ベトナム語 を相互に翻訳・通訳してくれています。 また、彼らは、日本人出張者や駐在者の意図を汲んだコミュニケーションもサポートしてくれています。 日本側でも ベトナム人 ブリッジSEが活躍しており、進捗フォローや分かりにくい要求事項を常日頃からRVメンバーと認識合わせし、思い違いなどがなくスムーズな開発が出来る様に努めています。 ベトナム 側の成長課題に、日本側が一緒に取り組んでいる所も ラク スのグローバル開発の特徴かと思います。 また、日本側メンバーと ベトナム 側メンバーがお互いを知り、尊重し合うというマインドも重視しています。 例えば、日本側と ベトナム 側のメンバーが定期的に出張を実施して、仕事を一緒にしつつ、1on1や懇親会でコミュニケーションを取っています。 それにより、お互いの課題を知り、課題の解決の為に協力するという文化が育まれていますね。 今後のグローバル開発への想い 今後、 ラク スの事業拡大と組織成長に伴い、日本と ベトナム がより一体となったグローバル開発を進めていこうとしています。 将来的には、日本と ベトナム 間での連携を深化させた上で、まだ実現出来ていない ベトナム 側での製品バージョンリリースも実施していきます。 その実現のために取り組んでいる事柄は多いですが、直近では、RVの開発では以下の2つのテーマを重視して取り組んでいます。 RVでの技術課題解決 :チーム毎に得意技術分野を持ち、サービスの新機能要求に対する技術ソリューションを、 ベトナム 側で検討・決定していく。 チームでの内部品質担保 :性能、保守性、信頼性をチーム内で担保して、クオリティの高い設計・実装をしていく。 また、RVメンバーの スキルアップ の為に、例えば、以下の様な施策にも取り組んでいます。 Github Copilotなどの開発ツール/開発技術の積極的な活用 技術スキル、ソフトスキル向上の為の組織的なト レーニン グ 開発作業だけでなく、 スキルアップ 時間も計画的に確保し、その時間を活用した定期的なチーム毎の技術 セミ ナーや改善活動 日本と ベトナム の相互出張によるペアワークを中心にした設計・実装スキルの向上 RVでは、これらの取り組みを マイルストーン を設けて仕組み化し、 PDCA を回していき、開発力を更に高めていこうとしています。 日本側も ベトナム 側と共に成長していく事で、製品を進化させていくメンバーを強化し、グローバル開発として一体となって製品をスケールしようとしています。 ラク スのグローバル開発の現状と今後の展望について簡単にお伝えしましたが、 「面白そうだな」「自分のスキルが丁度活きるフェーズだな」と思われた方、ぜひ一緒に働きましょう!