社内のタスク管理ツールをTracからBacklogに移行した話

この記事は 弥生 Advent Calendar 2021 の17日目の記事です。

弥生の川本です。開発本部でQAを担当しています。
昨年の 弥生 Advent Calendar 2020 でも17日目を担当したので、今年もゲン担ぎで同じ日を選択しました!テーマも昨年同様PowerBIよもやま話にしようかと直前まで考えていました。

が、弥生はここ最近、以前にも増して新しい技術やツールの導入が盛んでして。私もBacklog移行プロジェクトに参画して新しいタスク管理ツール導入に取り組んだので、今年はそのよもやま話を書くことにました。
(※Backlogはタスク管理機能を持ったプロジェクト管理ツールと記載した方が適切ですが、本記事内では便宜上タスク管理ツールと記載しています)

ちなみに、PowerBIと類似したサービスでAWSのQuickSightというサービスがありますが、そちらについて1日目にgarusanさんがQuickSightでかんたんに視覚化を寄稿されているので、BIツールに興味がある方はそちらもチェックして頂ければ幸いです。


~目次~

タスク管理ツールとは

読んで字の如くタスクを管理するツールです!

これだけだとヒンシュクを買うこと間違いなしなので、私なりの考えを述べさせて頂くと、「プロジェクトを遂行するために割り振られた担当業務の進捗や状況を見える化し、コントロールするためのツール」だと考えます。 (参考までにWikiの「タスク管理」の説明は以下リンクです)

タスク管理 - Wikipedia

一般的にはタスク管理は担当者一人ひとりの裁量に任されている要素も多く、「ちゃんと予定した期日までに計画通りに完了してくれれば良い」と考える方もいらっしゃるでしょう。
しかし、皆さんもこれまでに手痛い経験をしたことがあるように、全てのタスクが計画通りに進むなんてことはそうそうありません。
様々な事情(見積工数とのギャップ、その他の割り込み作業、解決に時間を要する課題に遭遇、等々…)によって、タスクは当初思い描いていた理想とはまったく異なる状況になることも多いですよね。

そのような状況をいち早くキャッチアップするための仕組みを提供してくれるのがタスク管理ツールであると思います。
とは言え、当然のことながら、タスク管理ツールを導入すればそれで万事OK!とはいきません。
ツールはあくまでツール。目的達成のための手段の一つでしかなく、結局はどのようにそのツールを運用するのかが重要です。

なぜBacklogに移行するのか

さて、上記の言い回しだと疑問に思われた方もいるでしょう。

タイトルにも記載した通り、弥生では「Trac」というタスク管理ツールをすでに運用しているのです。
しかも十数年に渡って活用してきた歴史があるので、弥生の開発においてTracの利用は「当たり前」という価値観が定着しています。

そんな「当たり前」を壊して、あえて別のツールに移行するのはなぜかと言うと、「プロジェクトを遂行するために割り振られた担当業務の進捗や状況を見える化し、コントロールする」ことがTracでは難しいケースが出てきたからです。

少し話題が逸れますが、弥生のプロダクトは技術の循環サイクルによって進化してきました。 以前はいわゆるデスクトップアプリケーション単体で独立していたものが、クラウドアプリケーションの台頭によって連携することが当たり前な時代になってきました。

そうなると当然ながらプロダクトを作るプロジェクトも単体で独立したものから、複数のプロジェクトが関わりあって一つの大きなサービスを作り上げるという形に変化していきます。 そしてTracの構成だと「複数のプロジェクトから割り振られた担当業務を確認しにくい」ため、いろいろと不都合が生じ始めたのです。

他にも細かい理由はいろいろありますが「複数のプロジェクトの横断的なタスク管理を自分で行えるようにする」、これが新ツールに移行した最も大きな理由です。

苦労したこと

新しいツールを導入するから皆使ってねー!で済めば話はかんたんですが、さすがにそういうわけにもいきませんね。 あらためて運用ルールを定めたり、困ったときのFAQを準備したり、いろいろやることがある中で、私がいちばん苦労したのはTracのデータをBacklogに移行するためのツール作成でした。

Backlogのヘルプセンターには、RedmineからBacklog、JiraからBacklogに移行するツールは公開されていたのですが、残念ながらTracからBacklogに移行するツールはない模様。
そんなわけで移行ツールの自作を決意し、担当として手を挙げました。
ただ正直言って私の見通しは甘かったですね・・・。Office製品のVBAマクロ作成の経験はありましたが、外部のAPIを使う際のイロハをわかっておらず何度も躓きました。 いくつか躓き事例を挙げると、

  1. Backlogは設定する項目の値を内部的にIDで持っていて、しかもプロジェクトごとに値が異なる。Excelのセル代入のように単に登録したい値(文字列)を渡せば良いというものではない
    ・「そんなの当たり前じゃん!」と思われる方もいらっしゃるでしょう。でも私は関数リファレンスを確認するまで勘違いしていました。
    ・「まだ慌てる時間じゃない」と気持ちを落ち着けて、登録したい値の内部IDをプロジェクトごとに照合する関数を用意する方針に切り替えました。

  2. 課題登録時にカスタム属性を指定するとエラーになる
    ・関数リファレンスにサンプルコードがないため、正しいリクエストパラメータの書き方がわからず検索しまくりました。
    ・蓋を開けてみれば難しいことはなく、リクエストパラメータに「&customField_{カスタム属性自体の内部ID}=登録したい値」を加えるだけでした。

  3. Tracのチケットの担当者と、Backlogの課題の担当者をどうやって紐づけるか
    ・それぞれのアカウント名が異なるので紐づけテーブルを別で用意することを考えていました。
    ・が、ある日Backlogの関数リファレンスを確認していたら、メールアドレスを取得できることに気づきました。天啓がおり、メールアドレスを照合することで無事解決。

  4. TracのWikiformat形式を、BacklogのMarkdown形式にどのように更新するか
    ・試行錯誤しましたが、「複数個の表」がある場合の変換ロジックを実現できませんでした。。。
    ・諦めて表の置き換えについては別のExcelアドインを用意しました。WikiFormat形式の表を一度Excelの表に置換し、Excelの表をMarkdownの表に置換するアドインで、ユーザー自身に更新を依頼しました。

いま思い返すとただの知識不足や思い込みもありますが、躓いた分だけ担当する前よりスキルや知識が向上したので、挑戦した甲斐があったなーと思っています。
そして、出来ることが増えてきたので、さらに欲が出てきました。

これから実現していきたいこと

一言でいえば、「プロジェクト管理ツールとタスク管理ツールの連携による業務効率化」を実現したいと考えています。

弥生ではプロジェクト管理ツールとして、Microsoft Project(通称MSP)を利用しています。このツールではいわゆるWBSを組み立ててプロジェクト全体の進捗を見える化します。
したがって、プロジェクト管理ツールとタスク管理ツールの関係性は、言わば親子関係みたいなもので、プロジェクト管理ツール上の最小単位であるタスクを、タスク管理ツールでもっと詳細なサブタスクに細分化して管理していく、といった運用が基本です。
つまり、プロジェクト管理ツール(MSP)で計画したタスクを、タスク管理ツール(Backlog)に登録していくことになるわけですね。

f:id:takuya_windsurf:20211213174523p:plain
プロジェクト管理ツールとタスク管理ツールの関係(イメージ図)

現行のMSPとTracの運用では、MSPで計画したタスクを主に手作業でTracに登録していました。
MSPを確認しながら、タスクのタイトルを記載して、担当者・開始日・終了日などの項目を埋め、1件1件Tracに登録していくのです。
また、もしもMSPの方でスケジュールを再計画した場合は、当然ながらタスク管理ツールの日付も見直す必要が出てきます。

このタスク管理ツールに登録する作業や日付を見直す作業は、TracからBacklogに移行しても残念ながら無くなりません。
そして、この作業には相応の時間が必要なので、以前からどうにかして効率化できないかなーと思っていたのです。

まだ検討段階ではありますが、移行ツール作成で得たノウハウを活用することで、プロジェクト管理ツール(MSP)から直接Backlogの課題を登録することや、再計画したプロジェクト管理ツール(MSP)の日付でBacklogの課題を自動更新する、といった業務効率化を実現できるのではないかと考えてワクワクしている今日この頃です。

お知らせ

弥生では一緒に働く仲間を募集しています!
チャレンジしたいことがあれば応援してくれる環境があるので、興味のある方はお気軽にお問い合わせください!

herp.careers

個人作業好きなエンジニアもハマるチーム開発手法

はじめに

こちらは弥生アドベントカレンダー2021の16日目の記事です。

こんにちは。新卒1年目エンジニアの田邊慎史と申します。
SMARTという、銀行の明細や紙の領収書などの様々な「取引データ」を「会計データ」に変換する機能を開発しているチームに所属しています。

入社してからまだ8ヶ月ですが、以下のように過ごしてきました。

2021年4月:新卒として弥生に入社。  
2021年6月:開発本部システム開発部に配属。導入教育・開発研修を受ける。  
2021年9月:SMARTプロジェクト業務を開始。  


さて突然ですが、皆さんは個人作業とチーム作業のどちらが好きですか?

人それぞれだと思いますが、私は個人作業の方が好きで、黙々と一人で没頭するのがとても好きなタイプです。
しかし、開発本部配属直後の開発研修において、チーム全員での作業(モブプログラミング)をすることになりました。
最初は正直あまり乗り気ではありませんでした。しかし、実際にやってみたら奥が深く、楽しみながら学ぶことができました。そんなモブプログラミングの面白さについてお伝えしたいと思います。

研修の内容

モブプログラミングの話に入る前に、少しだけ研修の説明をします。開発研修は以下の3つのフェーズに分かれていました。

f:id:s_tanabe:20211202172325p:plain
研修内容

今回はチーム開発に焦点を当ててお伝えします。10営業日にわたり、他の新卒2人を含めた3人チームで、モブプログラミングによるECサイト改修を行いました。既に完成している、最低限の機能をもつECサイトを改修するというチーム開発でした。

モブプログラミングとは?

モブプログラミングとは、複数人で1つの画面を共有し、同時に1つの作業を行う開発手法です。

f:id:s_tanabe:20211202172308p:plain
モブプログラミング
とはいえ、全員で一斉に実装を書いていくわけではありません。以下の役割に分かれます。

  • タイピスト:1人。キーボードを持ってコードを書く人。
  • モブ:他の全員(今回のチーム開発では2人)。画面を見ているがキーボードは持たない。タイピストに指示を出す人。

モブが「このコントローラーにこのメソッドを追加してください」や「このメソッドの引数を修正してください」などの指示を出します。タイピストはその指示を受けてコーディングを進めます。

このロールを3人で15分交代で行いました。

やってみて感じたメリット

モブプログラミングを初めて知ったという方は、「こんな方法、非効率じゃないの?」と思った方もいるかもしれません。ちなみに私はそう思いました。 最初に書いた通り、私は一人で黙々と作業するのが好きなタイプなので「全員でやるのはどうなんだろう......?」というのが第一印象でした。

しかし、そんなタイプの私でも、実際にやってみるといくつかのメリットに気づきました。

知識習得・定着がしやすい

常に一緒に作業しているので、分担作業に比べ、チーム内での質問も多くなります。質問はする側、される側両方にメリットがあります。質問する側は疑問に感じたことをすぐに聞くことができます。質問される側は、質問に答えることで理解が深まり、自分の理解不足に気づくこともできます。

特に「質問される側」という点がポイントです。通常の研修であれば、受講生が質問されることはあまりありません。しかし、質問されることでアウトプットの機会を得ることができ、理解を深めることができます。

私も、「理解したつもりになっていたが質問されると答えられない」という場面から、改めて公式のドキュメントを確認して理解しなおしたり、自分の勘違いに気づいたりすることがよくありました。逆に、他の人のコーディングのやり方も見ることができるので、「他の人はこうやって考えるのか」「こういう実装の方法があるのか」などと勉強することもできました。

情報共有のコストが低い

分担作業を行っていると、事前の打ち合わせで合意したはずなのに、完成したものを見せたら相手と認識がズレていた、ということはありませんか?どうしても分担する場合は情報共有を入念に行わないと認識がズレることがあると思います。

しかし、モブプログラミングでは、同じ作業を皆で行うので、そのような認識のズレが起こりづらいです。もし起こったとしても、その場ですぐに修正することができます。そのようなコミュニケーションコストがかかりにくい点は大きなメリットになります。

実際に作業をしていて、UI上どこにボタンを配置するか、またあるメソッドをどこに配置するかなどで意見が分かれることがありました。そのような際に、言葉だけで自分の想定を伝えようとしても伝わりにくかったり、間違って伝わったりしてしまうことがあります。しかし、モブプログラミングであれば、全員で同じ画面を見ているため、実際に実装してみて見比べるなどができます。そのようなコミュニケーションの齟齬を防ぎやすいことも含めて、モブプログラミングにはコミュニケーションコストを減らすメリットがあると感じました。

品質の高いコードを書ける

常に複数人でコーディングするので、間違いに気づきやすいです。いわば、常にレビューしあいながらコードを書いているという状態です。

実際に作業していると「そのメソッドの引数の型がおかしい」「その条件分岐だと正確に分岐できない」などと、指摘がチーム内で出ることでミスを減らすことができました。さらなるメリットとして、他人から指摘されることで、「自分はこういうミスをしやすいんだな」と気づけることもあります。

難しかったこと

そんなモブプログラミングですが、もちろん良いことばかりではありません。良い部分だけ抜き出してお伝えしてもフェアではないので、難しいと感じたこと、どう対処すればよいと思ったかについて以下でお伝えします。

詰まりやすい

これは初心者の研修特有の現象かもしれませんが、分からないことに詰まってしまうことが頻繁に発生していました。

通常のモブプログラミングでは、集団でコーディングすることでみんなの知識を総動員させることができるので、問題を解決しやすいというメリットが挙げられることが多いです。

しかし、我々新卒3人のチームでは、それぞれ持っている知識が広くはないため、分からないことを全員で解決させることが難しかったです。基本的に全員持っている知識に差がほぼ無いので、詰まったときに誰かが解決できるということは少なかったという印象でした。

もちろん今回は研修だったので、詰まったときにどう調べて解決するかという点も含めて勉強になりましたが、実際の業務では、技術面をリードできる人がいたり、知識のプールが異なる人と一緒に実施したりすると良い形になるのではないかと感じました。

意思疎通ができないと困る

常に一緒に作業する以上、コミュニケーションを取りながらの作業になります。意思疎通の機会の多さは、上記のように質問しあえるという大きなメリットにつながります。しかし、逆に言えば、コミュニケーションがうまくいかないとモブプログラミングの利点を活かせません。

今回の新卒チームは4月から一緒に研修を受けてきたので、すでにコミュニケーションを取りやすい関係性が構築できていました。そのため、モブプログラミングを進める上で困ることは殆どありませんでした。しかし、そのような関係性が無い状態で行う場合は、指摘しづらい、質問しづらいなどの原因で、モブプログラミングが進めにくくなる可能性があります。

業務でモブプログラミングをやる場合、関係性ができているチームメンバー同士では特に問題ないと思います。ただ、これまで関わりがあまりなかったメンバー同士でやる場合は、アイスブレイクなどを通してコミュニケーションを取りやすい関係性を構築することも重要だと思います。

まとめ

モブプログラミングの良かったところ、難しかったところについて、私の体験で感じたことを紹介してきました。

自分の知識を深めることができ、さらに他人の持つ知識も得ることができます。また、煩雑なコミュニケーションコストを低減でき、そしてバグの少ないコードを書ける、そんなモブプログラミングの面白さが皆さんに伝われば嬉しいです。

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

herp.careers

確定申告時期の苦労と、その時期にやっていること

この記事は弥生 Advent Calendar 2021 の15日目エントリーです。
弥生会計のエンジニアをしている竹山です。

今年も残すところ半月となり、2か月後には確定申告期間が始まります。
開発は繁忙期の最中ということで、その乗り越え方についてご紹介します。
(2021年5月13日(木)に開催された、もくテク「確定申告ソフトの開発舞台裏」でも発表しました)
もくテク「確定申告ソフトの開発舞台裏」を開催しました - 弥生開発者ブログ

会計ソフト開発の一年

弥生会計は、毎年秋ごろに新バージョンをリリース(今年は弥生会計 22)し、1月から始まる確定申告期間に合わせ「所得税確定申告モジュール」をリリースしています。

f:id:hiroaki_takeyama:20211214141847p:plain

大変なところ

メジャーバージョンと確定申告モジュールのリリース前に忙しい時期が生じますが、
特に確定申告モジュールの開発は、様々な要因があって、特に忙しくなります。

  • 所得税の確定申告期間はあらかじめ決まっているため、申告期間の開始に間に合うようリリースする必要があります。
  • 一方で、確定申告書などの様式、確定申告の手引き、e-Taxの仕様などが公開される時期もある程度決まっています。これらが公開されないと、開発を始めることができません。
  • したがって、様式や手引きの公開から確定申告期間までの限られた時間で、開発を行う必要があります。

f:id:hiroaki_takeyama:20211214142030p:plain

  • 改正の分量によっても対応の難易度が左右されます。昨年は様式や計算方法の変更が多く、対応が大変でした。帳票や手引きは、すべての情報が一気に公開されるわけではないため、帳票のイメージなど、断片的な情報から対応内容を予測し、期限に間に合うよう工夫しています。例えば昨年の様式変更では、扶養親族の記載欄に丸印を付ける条件が分からない状態からスタートしましたが、様々な情報源から推測して先行実装しました。

f:id:hiroaki_takeyama:20211213092927p:plain

  • また、技術面ではなく表現方法で悩む個所もあります。
    下図は昨年作成した「所得税確定申告に関するお知らせ」です。
    昨年は青色申告特別控除額の変更がありましたが、条件によって設定すべき金額が異なるため、 「過大申告・過少申告を生じさせない」ためにお知らせ表示することとしました。
    このような分かりやすさに関わる部分は、ユーザー対応を行うコールセンターとも連携して、表現方法を検討しています。

f:id:hiroaki_takeyama:20211214111737p:plain

どんなふうに乗り越えているか

年によって大小はありますが、毎年確定申告モジュールをリリースできているのは、以下の理由があります。

法令確認

日ごろから法令などの情報収集を行い、修正の必要性を早く把握できるようにしています。

  • 社内で法令調査を行い、共有会をしています。
  • チーム内では、法令改正情報をエンジニアが当番制でチェックしています。
  • e-Taxの仕様を確認したり、弥生の他製品ともで齟齬がないかを確認しています。

開発プロセス

手戻りを最小限にすることを意識した開発プロセス(U字プロセス)を採用しています。設計書、テストスクリプト、実装のレビューを徹底的にすることで、設計書やコードの状態が常に整備されているので、過去の資料の不整合が少なく、今回の対応に集中できます。
会計ソフトの場合、計算式の端数処理や桁数、0の場合に0と表示するかなど、細かな指定が残されていることがとても大事です。対応の際はこれらの指定を一つ一つを吟味して修正します。

やりがい

限られた時間の中で、必要とされる機能を正確に、かつ最大限わかりやすい形で提供するところにやりがいを感じます。日常の準備や改善が繁忙期に結果として表れるので、毎年新しい気付きを得ることができます。

会計ソフト開発全般については、昨年の記事をご参照ください。
tech.yayoi-kk.co.jp

まとめ

これまで継続して、毎年の期限に合わせてソフトウェアをリリースできていることは大変なことで、毎年プレッシャーもありますが、上記のような取り組みが重なって、安定して機能を提供することができていると思います。
今年分も使いやすい確定申告機能を提供できるよう、鋭意開発を進めていきます。

お知らせ

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

お客様サポートの裏側の話

この記事は弥生 Advent Calendar 2021 の14日目の記事です

こんにちは。弥生の後藤です。
SMARTという、銀行の明細や紙の領収書などの様々な「取引データ」を「会計データ」に変換する機能を開発しているチームでエンジニアをしています。

メインの仕事としてはSMARTの機能追加や改修をしているのですが、その他にもう1つ、お客様からのお問い合わせに関する調査を担当しています。
今日は、この調査担当にまつわる仕事についてお話しさせていただこうと思います。

どんなことをしているのか

弥生のコールセンターにお客様から製品についてお問い合わせのあった案件に関して、 コールセンターのご案内だけでは解決が難しい案件について調査を行っています。

調査担当者は各製品チームに数名存在し、SMARTチームには担当者が現在3人います。

メインの仕事はお客様からのSMARTに関するお問い合わせに関する調査ですが、その他に

  • 他チームや他社からの調査依頼対応
  • 他部署からのデータ抽出依頼対応
  • 本番環境のエラーログの定期的な監視

なども行っています。

調査の仕事は、お客様をなるべくお待たせすることのないよう、迅速に対応する必要があります。
一方でSMARTの開発業務も遅延することなく進めていかなければならないのが、調査担当の難しいところです。

工夫していること

SMARTの調査担当者とテクニカルリーダとで共有会を行っています。
目的は、調査対応の質の向上と効率化です。

共有会では、各メンバーが調査の過程で得た知見や効率の良い調査方法などを共有したり、解決できていない課題について相談したりしています。

共有会を始める前は、過去に類似事象がないお問い合わせの調査を行ったときに、その調査の過程で得た知見が調査担当者の中で閉じられてしまっていました。
共有をしあうことで、次に類似の事象が来た時に調査の勘所がわかるため、何も知らない状態に比べて効率よく調査ができるようになりました。

また、よくあるお問い合わせと調査内容をWikiにまとめて、新しくメンバーが入ってきたときに参考にできるようにしたり、 定期的に発生するデータ抽出依頼に対して、データ抽出の作業の一部を自動化するツールを用意したりしています。

調査担当になってよかったこと

時々事象が複雑で調査が難航することもあるのですが、原因が判明して事象が解決した瞬間はとてもうれしいです。

また、調査担当になる前と比べて、SMARTの様々な機能について理解が深まりました。
調査では、お客様が行った操作のヒアリング内容をもとに、ログの内容やデータベースのデータの状態を確認しつつ、原因を突き止めていきます。
その際、仕様を正確に把握していないと原因を突き止めることができません。
当初は自分が開発で担当したことのない機能については仕様の理解が浅かったのですが、調査担当として経験を積んでいくことで SMARTの1つ1つの機能や、機能と機能のつながりについて理解が深まっていきました。

おわりに

調査担当になって、お客様からのお問い合わせに触れる機会が格段に多くなりました。
この経験も踏まえて、お客様にとってより良い製品について今後さらに考えていきたいと思います。

お知らせ

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

Jetpack Navigationを使った画面遷移でFragmentとステータスバーのThemeを制御する話し

弥生モバイルチームのtijinsです。

MisoaアプリNavigationコンポーネントを導入してActivityとFragmentが複雑に絡み合った状況を改善しようと思ったのですが、アプリのデザインがActivity毎のThemeで定義されていて、単純にNavigationを入れると見た目が変わってしまう問題に行き当たりました。 ThemeをFragment毎にすれば同じ見た目を実現できるはずなのですが、ThemeもStyleも全くワカランって感じで1ヶ月近く悩んでいたので、1ヶ月悩んだ結果を紹介します。

この記事の概要

このブログでは、以下の内容を説明していきます。

  • FragmentにThemeを適用する方法
  • ステータスバーの色をFragmentのThemeで制御する方法
  • ステータスバーの文字・アイコン色をFragmentのThemeで制御する方法

f:id:tijins:20211206142128g:plain

ThemeとStyle

背景色や文字サイズなど、アプリの見た目を一括して設定できる機能です。
デフォルトのプロジェクト構成ではres/theme.xmlに保存されています。
theme.xmlで定義したThemeをAndroidManifest.xml<application theme="{theme}"><activity theme="{theme}">に指定する事で、アプリ全体やActivity毎の見た目を設定可能です。
ただし、Fragment毎に指定する機能は提供されていませんでした。

Themeの指定

themeはAndroidManifest.xmlで指定しました。
<application>に指定するとアプリ全体の背景色や文字サイズに適用されます。
<activity>に指定するとActivityの背景色や文字サイズに適用されます。
Android5.0以上ではレイアウトXMLのViewGroupにも指定可能です。
<LinearLayout theme="{theme}"><ConstraintLayout theme="{theme}">のように指定するとViewGroup内が適用範囲になります。

Styleの指定

StyleはThemeの適用範囲を1つのViewに限定したものです。
定義はThemeと同じでtheme.xml<style>で行います。
レイアウトXMLで<TextView style="{style}">のように指定しします。
ViewGroupに適用しても、Themeと異なり内側の要素に継承されません。

FragmentにThemeを適用する

FragmentのThemeをxmlで指定する機能は提供されていない為コードから指定します。
Theme(Style)をコンストラクタで指定可能なThemedFragmentを作成しました。
onCreateView()でLayoutInflaterを差し替えています。

// テーマの指定が可能なFragment
abstract class ThemedFragment(@LayoutRes val layoutRes: Int, @StyleRes val styleRes: Int) : Fragment(layoutRes) {

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        // Themeを適用したContextを作る
        val themedContext = ContextThemeWrapper(context, styleRes)
        // Themeを適用したLayoutInflaterを作る
        val themedInflater = inflater.cloneInContext(themedContext)
        // レイアウトの生成に使用するinflaterを差し替える
        return super.onCreateView(themedInflater, container, savedInstanceState)
    }
}

// Fragment毎のレイアウトとThemeを指定する
class SampleFragment : ThemedFragment(R.layout.fragment_first, R.style.Theme_StyleSample_Red)
{
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        //  Fragment固有の処理
    }
}

※サンプルコードはandroidxのFragmentを継承しています。

ステータスバーの色をFragmentのThemeで指定する

FragmentにThemeを適用する事例はすぐに見つかったのですが、ステータスバーの色が変わらない事に気づきました。
LayoutInspectorで調べると、Fragmentの描画範囲にステータスバーが含まれていませんでした。

f:id:tijins:20211206112233p:plain
fitSystemWindow="false"の時

試行錯誤の結果、以下でステータスバーの色を変更できる事がわかりました。
システムが描画するステータスバーの色を透明にする事で、ディスプレイ全体に広がったFragmentの背景色が、ステータスバーの色として表示されるという仕組みです。

  1. Fragmentの描画範囲をディスプレイ全体まで広げる
  2. ActivityのThemeで、ステータスバーの色(android:statusBarColor)を、透明(@android:color/transparent)にする
  3. FragmentのThemeで、背景色(colorPrimaryDark)を、表示したいステータスバーの色にする
    f:id:tijins:20211206134733p:plain
    ステータスバーの背景色を設定する

※サンプルは、下記の組み合わせで確認しています。

ライブラリ パッケージ バージョン
AndroidX androidx.core:core-ktx 1.7.0
マテリアルデザイン com.google.android.material:material 1.4.0

Fragmentの描画範囲をディスプレイ全体に広げる

Activityのルート要素をFragmentContainerViewにします

Activityのレイアウト

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.fragment.app.FragmentContainerView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/nav_host"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:name="androidx.navigation.fragment.NavHostFragment"
    app:defaultNavHost="true"
    tools:context=".MainActivity"
    app:navGraph="@navigation/nav_graph"/>

Fragmentのレイアウト

Fragmentのルート要素をDrawerLayoutにしてfitSystemWindow="true"を指定します。
(ルート要素がConstraintLayoutやLinearLayoutだとステータスバーの背景色が変わりません)

fragment_first.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".FirstFragment"
    android:fitsSystemWindows="true">

</androidx.drawerlayout.widget.DrawerLayout>

f:id:tijins:20211206112231p:plain
fisSystemWindow="true"の時

Activityのテーマで、ステータスバーの色を透明にする

theme.xml
android:statusBarColor@android:color/transparentを指定します

    <style name="Theme.StyleSample" parent="Theme.MaterialComponents.Light.NoActionBar">
        <item name="android:statusBarColor">@android:color/transparent</item>
    </style>

Fragmentのテーマでステータスバーの背景色を指定する

colorPrimaryDarkでステータスバーの背景色を指定します

    <style name="Theme.StyleSample.Red">
        <!-- ActionBarの色-->
        <item name="colorPrimary">@color/red</item>
        <!-- ステータスバーの色  -->
        <item name="colorPrimaryDark">@color/red_status_bar</item>
    </style>

ステータスバーの文字を黒色にする

ステータスバーの背景色は変更できたのですが、今度はandroid:windowLightStatusBar="true"が反映されない事に気づきました。
android:windowLightStatusBar="true"はステータスバーの背景色が明るい色のときに使用するもので、ステータスバー上の文字やアイコンが黒色になります。

こちらもxmlでは指定できない為、Fragmentが遷移した際のイベントで、コードから動的に変更する事にしました。

Style値の取得

xmlで指定された値(true/false)をコードから取得するUtilityを作成しました。

object StyleUtil {
    fun getWindowLightStatusBar(context: Context, @StyleRes styleRes: Int?): Boolean {
        val themedContext = if (styleRes != null) {
            ContextThemeWrapper(context, styleRes)
        } else {
            context
        }
        val typedArray = themedContext.theme.obtainStyledAttributes(
                intArrayOf(android.R.attr.windowLightStatusBar)
        )
        val isLight = typedArray.getBoolean(0, false)
        typedArray.recycle()
        return isLight
    }

context.theme.obtainStyledAttributes()を使用すると、themeに指定されているstyleの値を取得可能です。

windowLightStatusBarの動的な変更

childFragmentManager.addOnBackStackChangedListener{}のイベントでFragmentに指定されたstyleを取得して、ステータスバー文字色を変更します。
findNavController().addOnDestinationChangedListener{}のイベントを使用したくなるところですが、DestinationChangedイベントが発生した時点では、まだ遷移先のFragmentが生成されていない為、スタイルを取得できません。

class MainActivity : AppCompatActivity(R.layout.activity_main) {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val navHost = supportFragmentManager.findFragmentById(R.id.nav_host)!!
        navHost.childFragmentManager.addOnBackStackChangedListener {
            val currentFragment = navHost.childFragmentManager.fragments.firstOrNull()
            val isLight = StyleUtil.getWindowLightStatusBar(
                    this,
                    (currentFragment as? ThemedFragment)?.styleRes
            )
            setLightStatusBar(isLight)
        }
    }

    private fun setLightStatusBar(isLight: Boolean) {
        if (isLight) {
            window.decorView.systemUiVisibility =
                    window.decorView.systemUiVisibility or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
        } else {
            window.decorView.systemUiVisibility =
                    window.decorView.systemUiVisibility and View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.inv()
        }
    }
}

サンプルコード

https://github.com/tijins/ThemedFragment

まとめ

この記事は弥生アドベントカレンダー2021の13日目です。
ActivityとFragmentが複雑に絡み合ったアプリをリファクタリングにしてスッキリした新年を迎えたいですね。

お知らせ

弥生のモバイルチームでは、Android、iOSのエンジニアを募集中です!! herp.careers herp.careers

技術勉強会の企画を立てよう!(初心者編)

この記事は弥生 Advent Calendar 2021の9日目エントリーです。

<登場人物>
 もくにゃん木曜日にテクノロジーを語ろうとすると現れるねこ
 あいざわ:リモートワーク中によくPCの接続が切れるエンジニア


<2021/10/14(木)>

 あ~もくテクが今月(10月)も無事に終わった~
 もくテク?
 毎月木曜日に開催している弥生主催の技術イベントだよ~

mokuteku.connpass.com

 隙宣伝にゃん
 それはさておき…

 再来月(12月)のもくテクの企画が決まらない!

 来月(11月)は決まってるのかにゃん?
 11月は大丈夫!いつも大体こんなサイクルで企画運営しているのです

 にゃるほど
 だから12月の企画を検討してるんだけど、オンラインで再開から早半年、ネタ切れしました
 もうちょっと頑張るにゃん
 毎月開催 意外大変也
 今まではどんな風に決めてたにゃん
 運営メンバーで集まって決めてたんだけど…毎月ともなるとマンネリ化してきて…

 仕方ない…もくにゃんが企画のコツを伝授するにゃん
 やった~!

 ネタがないって言っても、社内の話は日々色々聞いてるにゃんね?
 色んなチームが日々施策を打ちながら開発して、それを社員総会や全体会で共有してるからね
 それをストックして企画にすればいいにゃん

例:

 たしかに
 日々の積み重ねが大事にゃん
 でも今は何も残してないからストックがない…
 そういう時は社内で面白いネタを持っていそうな方に頼ったり、社内で募集するにゃん

例:

※CTL=Chief Technical Lead
参考記事:10年先の未来に向けて──弥生の開発プロジェクトを成功に導く“CTL”の役割|弥生株式会社

 た、頼れる~~!!
 あとは頑張るにゃん
 はい!

.
.
.
<1週間後>

 もくにゃん~!12月の企画が決まったよ~!
 よかったにゃん
 よ~しこの調子で先の企画もガンガン決めてくぞ~!
にゃ~ん


宣伝

① 弥生はもくテク運営の他にも自主的に参加できる社内活動があり、どこも活発に活動しています。
社内活動が好きな人は覗いてみてください!
herp.careers

② 記事の中でもくにゃんとあいざわが企画したイベントは、12/16(木)に開催します。
初学者~ベテランまで幅広い登壇者がAWSの話をします。
興味がある方は参加してみてください!
mokuteku.connpass.com

AWS Route53の加重ルーティング機能で本番インフラを無停止・段階的にECS環境に移行する

弥生 Advent Calendar 2021 6日目の記事です。

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

クラウド請求書作成ソフトであるMisocaのインフラはサービス稼動開始からつい最近までAWS EC2を使ってアプリケーションを運用していましたが、この度Dockerコンテナ管理サービスであるECSに移行し、この移行作業がほぼ完了致しました 🎉

ECS移行に際しては長い時間をかけて準備や検証を行っており、開発者ブログで紹介したいネタがいくつかあるのですが、今回は実際にEC2で稼動するappサーバを無停止且つ段階的にECSに移行した方法について紹介したいと思います。

ECS移行前の状態と作業の概要

移行前のMisocaアプリケーションのインフラは複数台のEC2インスタンス上で稼動しており、各EC2インスタンスへのリクエスト振り分けはALBを使って制御していました。ドメインとALBの紐付けはRoute53のエイリアスレコードを利用しています。

移行後はEC2インスタンスが複数のECSタスクに、ALBもEC2用からECS用の新規ALBリソースに置き換わります。この移行作業の準備としてMisocaアプリケーションのコンテナ化やECSサービス・タスク定義の作成、ECS用のALBの用意を事前に行いました。

移行作業は以下の点を重視し、これらが実現できる方法について調べました。

  • サービスの停止時間を作らずに移行作業ができる
  • 一気にECS環境に切り替えるのではなく、カナリアリリースのように段階的にECS環境を本番に投入して負荷状況等を監視したい
  • 段階的に投入する事でECSもしっかり本番環境でサービスが提供できているという安心感を得たい 1
  • 万が一ECS起因の障害が発生したとしても影響を最小限にしつつ素早くロールバックできるようにしたい

こちらについてAWS Japan ソリューションアーキテクトの方に技術相談をしたところ、Route53の加重ルーティング (Weighted routing) 機能について教えていただきました。 2

Route53加重ルーティング機能について

正確な機能紹介についてはAWSドキュメントを読む事が最も良いので、ここではざっくりとした紹介をします。

Route53の加重ルーティング機能は、同一レコード名で登録し、重み (Weight) を各レコードで設定する事でその重みに応じた割合でリクエストを振り分けることができます。振り分け方法はラウンドロビン方式です。また、片方の重みを0にする事で設定したレコードへリクエストが振り分けられなくなり、もう片方のレコードに全てのリクエストが振り分けられるようになります。

例えば、 hoge.example.com への振り分けを EC2:ECS=30:70 としたい場合は以下のように重みを設定しそれぞれレコード登録します。

レコード名 ルーティングポリシー 重み ルーティング先
hoge.example.com 加重 30 EC2用のALBドメイン
hoge.example.com 加重 70 ECS用のALBドメイン

この重みを変更する事でリクエストの振り分ける割合を柔軟に設定が可能になります。

f:id:mizukmb:20211118152712p:plain

無停止・段階的に移行するための作戦

行う事はシンプルで、加重ルーティングで設定する各レコードの重みを編集して少しずつ EC2:ECS=100:0 な状態から EC2:ECS=0:100 にすれば作業は完了します。

MisocaチームではインフラをTerraformで管理しているので、Route53レコードの設定や重みの変更はTerraformコードを修正し、プルリクエストの作成とコードレビューを経て terraform apply を実行し適用するという流れでした。

重みの変更によるアプリケーションの停止時間は発生しないので、この値が間違っていないか十分注意していれば簡単に無停止・段階的な移行が可能になります。

実際の移行作業ですが、最初は EC2:ECS=95:5 の割合から始めて、1〜2日様子を見てECS側の重みを増やして EC2:ECS=70:30 にする。さらに様子を見てECS側の重みを増やして…といった作業を繰り返していき、約1週間程かけて EC2:ECS=0:100 な状態に移行し、サービスの稼動に影響が無い事を確認できました。

おわりに

MisocaのアプリケーションインフラのEC2からECSへの移行にRoute53の加重ルーティング機能を活用した話を書きました。

ECS移行に関する知見についてはまだまだあるので、できる限り開発者ブログで紹介したいと考えています。

弥生 Advent Calendar 2021 では弥生のエンジニアが様々な内容で記事を公開しますのでぜひ他の記事もチェックしてみてください!

採用

積極採用活動中です!!

herp.careers

付録:最初に考えてた作戦内容と却下理由

ALBのリスナールールではリクエストを転送するターゲットグループの重み付けができるため、最初はALBのレイヤで無停止・段階的な移行を進めるつもりでいました。

しかしながら、ECS環境のデプロイはCodeDeployのBlue/Greenデプロイメント機能を使っている関係からこの案は技術的に不可能である事がわかりました。理由ですが、CodeDeploy Blue/Greenデプロイメント機能を実行するとCodeDeploy内部でリスナールールに紐付くターゲットグループをBlueからGreenに付け替えるといった事を行っており、実質リスナールールの転送先はECSに固定されてしまうためです。

CodeDeploy Blue/Greenデプロイメント機能を使わなければこの案で進められますが、これのためにBlue/Greenデプロイを止めるのは割に合わないと判断したため却下となりました。


  1. もちろんステージング環境で検証していますが、やはり本番となると怖いものは怖いので安心感が欲しいのです

  2. 弥生ではAWS Japan ソリューションアーキテクトの方と定例会を行っており、AWSに関する技術相談がカジュアルにできる場が設けられています