TECH PLAY

ニフティ株式会社

ニフティ株式会社 の技術ブログ

487

この記事は、 ニフティグループ Advent Calendar 2022 13日目の記事です。 はじめに 最近ChatGPTにハマっている柴田です。普段はFlutterの開発を個人的に楽しんでいます。今回は、FlutterでHero Animationsを実装してみます。 Hero とは FlutterのHero Animationsとは、アプリらしいシームレスなアニメーションのことで、Androidで言うshared element transitions、iOSでいうSwiftUI2.0で追加されたmatchedGeometryEffectのようなものです。 遷移先の画面に遷移元と共通または似ている要素がある場合にHero Animationsを使用することでシームレスに画面遷移することができます。 Flutterでは、標準で用意されているMaterial LibraryのHero Widgetを使うことで簡単に実装することができます。 詳細はこちら https://docs.flutter.dev/development/ui/animations/hero-animations 実装する カードをタップすると詳細が表示されるUIを実装します。 Heroを使用しない実装 Heroを使う前のコードと動作例です。 Card( child: InkWell( splashColor: Colors.blue.withAlpha(30), onTap: () { Navigator.push( context, MaterialPageRoute( builder: (context) => DescriptionScreen(cardContent: cardContent), ), ); }, child: Column( children: [ Container( height: 150, decoration: BoxDecoration( borderRadius: const BorderRadius.all(Radius.circular(5)), image: DecorationImage( fit: BoxFit.cover, image: Image.asset(cardContent.imageAssets).image, ), ), ), ListTile( title:Text( cardContent.title, style: Theme.of(context).textTheme.titleMedium, ), ), ], ), ), ); Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Image.asset(cardContent.imageAssets), Padding( padding: const EdgeInsets.all(30), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( cardContent.title, style: Theme.of(context).textTheme.displayMedium, ), const SizedBox(height: 20), Text( cardContent.description, style: Theme.of(context).textTheme.bodyLarge, ), ], ), ), ], ), Heroを使用した実装 このコードにHero Animationsを実装していきます。実装と言ってもImageやTextをHero Widgetでラップするだけです。 Hero Widgetが同一のTagが設定されているWidget間でいい感じにアニメーションを計算してくれます。 Hero Widgetを使って実装したコードと動作例です。 Card( child: InkWell( splashColor: Colors.blue.withAlpha(30), onTap: () { Navigator.push( context, MaterialPageRoute( builder: (context) => DescriptionScreen(cardContent: cardContent), ), ); }, child: Column( children: [ Hero( tag: cardContent.imageAssets, child: Container( height: 150, decoration: BoxDecoration( borderRadius: const BorderRadius.all(Radius.circular(5)), image: DecorationImage( fit: BoxFit.cover, image: Image.asset(cardContent.imageAssets).image, )), ), ), ListTile( title: Hero( tag: cardContent.title, child: Text( cardContent.title, style: Theme.of(context).textTheme.titleMedium, ), ), ), ], ), ), ); Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Hero( tag: cardContent.imageAssets, child: Image.asset(cardContent.imageAssets), ), Padding( padding: const EdgeInsets.all(30), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Hero( tag: cardContent.title, child: Text( cardContent.title, style: Theme.of(context).textTheme.displayMedium, ), ), const SizedBox(height: 20), Text( cardContent.description, style: Theme.of(context).textTheme.bodyLarge, ), ], ), ), ], ), 注意 HeroでラップするTextにはstyleを付ける必要があります。 Styleを指定していないと、下の図のようにHero Animationsの処理が上手く行われず、アニメーション時に文字がはみ出してしまいます。 今回実装した全体のコードはこちらで公開しています https://github.com/ShibataRyusei/flutter-hero-demo 余談 今回の動作例はiOSですが、もちろんAndroid、Web、macOSでも動作しました。(Windows, Linuxは未検証) 一度の実装で複数のOSに書き出せるのは、何度体験しても感動します。 終わりに 今回は、FlutterでHero Animationsを実装してみました。ImageやTextなどのWidgetをHero Widgetでラップするだけでアプリらしいアニメーションが簡単に実装できました。このようなWidgetsが標準で用意されている点からFlutterのDX(Developer Experience)の高さを感じ、ストレスフリーに開発ができました。 みなさんもFlutterで快適な開発体験を!! 明日(14日目)は、 ike-chan さんです。お楽しみに! We are hiring! ニフティでは、さまざまなプロダクトへ挑戦するエンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトよりお気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering
アバター
この記事は、 ニフティグループ Advent Calendar 2022 11日目の記事です。 はじめに 会員システムグループ N1!Machine Learning Product Engineerの中村です。 ニフティでは11/22にNIFTY Tech Dayというイベントを開催しました。 この時に、ニフティニュースにおける深層自然言語処理によるニュース記事要約について発表をさせていただきましたが、この時に行った転移学習(ファインチューニング)について技術的な解説をしていきます。 モデルの転移学習について 本記事の大まかな実装は sonoisa さんの記事を参考に構築しています。 https://qiita.com/sonoisa/items/a9af64ff641f0bbfed44 言語モデルの転移学習 現在の言語モデルは非常に大規模であり、全く学習されていない状態(スクラッチ)から改めて学習させることは現実的ではありません。そこで考案されたのが転移学習という手法です。 転移学習とは、既に学習された大規模な言語モデルを元に、新しいタスクについての学習を行う手法です。この方法は、新しいタスクの学習データが少ない場合に特に有効です。また、既に学習されたモデルの学習済みの知識を引き継ぐことで、学習がスムーズに進むため、学習時間の短縮が期待されます。 プロンプト学習 https://arxiv.org/abs/1910.10683 今回のニュース記事要約ではT5というモデルの学習を行いますが、この学習にはプロンプトを用いて学習を行います。現在の深層学習モデルは非常に大規模であり、1つのモデルで複数のタスクを実行可能です。プロンプトと呼ばれる接頭辞を頭に付けて学習を行うことで、文章に対して何を行いたいかを指定し、それに合わせたタスクを実行するように学習します。 実装と学習の実行 ここではニフティニュースにおける記事データと要約データを用いて、T5モデルの転移学習を行います。 データセットの準備 ノーマライズ処理 まず、neologdの正規化処理を改変した処理を用いて、正規化処理を定義します # https://github.com/neologd/mecab-ipadic-neologd/wiki/Regexp.ja から引用・一部改変 from __future__ import unicode_literals import re import unicodedata def unicode_normalize(cls, s): pt = re.compile('([{}]+)'.format(cls)) def norm(c): return unicodedata.normalize('NFKC', c) if pt.match(c) else c s = ''.join(norm(x) for x in re.split(pt, s)) s = re.sub('-', '-', s) return s def remove_extra_spaces(s): s = re.sub('[  ]+', ' ', s) blocks = ''.join(('\u4E00-\u9FFF', # CJK UNIFIED IDEOGRAPHS '\u3040-\u309F', # HIRAGANA '\u30A0-\u30FF', # KATAKANA '\u3000-\u303F', # CJK SYMBOLS AND PUNCTUATION '\uFF00-\uFFEF' # HALFWIDTH AND FULLWIDTH FORMS )) basic_latin = '\u0000-\u007F' def remove_space_between(cls1, cls2, s): p = re.compile('([{}]) ([{}])'.format(cls1, cls2)) while p.search(s): s = p.sub(r'\1\2', s) return s s = remove_space_between(blocks, blocks, s) s = remove_space_between(blocks, basic_latin, s) s = remove_space_between(basic_latin, blocks, s) return s def normalize_neologd(s): s = s.strip() s = unicode_normalize('0-9A-Za-z。-゚', s) def maketrans(f, t): return {ord(x): ord(y) for x, y in zip(f, t)} s = re.sub('[˗֊‐‑‒–⁃⁻₋−]+', '-', s) # normalize hyphens s = re.sub('[﹣-ー—―─━ー]+', 'ー', s) # normalize choonpus s = re.sub('[~∼∾〜 ~]+', '〜', s) # normalize tildes (modified by Isao Sonobe) s = s.translate( maketrans('!"#$%&\'()*+,-./:;<=>?@[¥]^_`{|}~。、・「」', '!”#$%&’()*+,-./:;<=>?@[¥]^_`{|}〜。、・「」')) s = remove_extra_spaces(s) s = unicode_normalize('!”#$%&’()*+,-./:;<>?@[¥]^_`{|}〜', s) # keep =,・,「,」 s = re.sub('[’]', '\'', s) s = re.sub('[”]', '"', s) return s そのほかにタブ文字やタグの除去、エスケープシーケンスの復号化などを行い、データセットを整備します。 参考元の処理ではlower処理やスペースの削除などが含まれていますが、例えば「iPhone」などの商品名や、空白によって意図的に区切られている部分が消滅するなどの現象が起こったため、本記事ではその部分の処理を行わないように実装しています。 ニフティニュースでは短文と長文の2種類のタイトルの他、3行要約も作成しており、そのデータにそれぞれプロンプトを付与し学習させます。 import re import numpy as np import pickle from tqdm import tqdm tag_regex = re.compile(r"<[^>]*?>") def normalize_text(text): text = text.replace("\t", " ") text = normalize_neologd(text) text = tag_regex.sub("", text) text = text.replace(""", "\"").replace("&", "&").replace("<", "<").replace(">", ">").replace(" ", " ") return text all_data = [] count = 0 for index, data in news_data.iterrows(): if data['body'] is None or data['body'] is np.nan or not data['body']: continue normalized_body = normalize_text(data['body']) all_data.append({"text": "keyword: " + normalized_body,"response": normalize_text(data['keyword_str_1']),}) all_data.append({"text": "keyword: " + normalized_body,"response": normalize_text(data['keyword_str_2']),}) all_data.append({"text": "keyword: " + normalized_body,"response": normalize_text(data['keyword_str_3']),}) all_data.append({"text": "topics_title: " + normalized_body,"response": normalize_text(data['topics_article_title']),}) all_data.append({"text": "title: " + normalized_body,"response": normalize_text(data['title']),}) if data['long_title'] is not None and data['long_title'] is not np.nan: all_data.append({"text": "long_title: " + normalized_body,"response": normalize_text(data['long_title']),}) if data['summary_1'] is not None and data['summary_1'] is not np.nan: all_data.append({"text": "summary_1: " + normalized_body,"response": normalize_text(data['summary_1']),}) if data['summary_2'] is not None and data['summary_2'] is not np.nan: all_data.append({"text": "summary_2: " + normalized_body,"response": normalize_text(data['summary_2']),}) if data['summary_3'] is not None and data['summary_3'] is not np.nan: all_data.append({"text": "summary_3: " + normalized_body,"response": normalize_text(data['summary_3']),}) プロンプトも含めた本文であるtextと、それに対応する応答であるresponseを、all_dataという配列に含めた状態になりました。 データセットの分割 データをtrain/validation/testの3つに分割します。 import random from tqdm import tqdm random.seed(1234) random.shuffle(all_data) def to_line(data): text = data["text"] response = data["response"] assert len(text) > 0 and len(response) > 0 return f"{text}\t{response}\n" data_size = len(all_data) train_ratio, val_ratio, test_ratio = 0.95, 0.03, 0.02 with open(f"data/train.tsv", "w", encoding="utf-8") as f_train, \ open(f"data/val.tsv", "w", encoding="utf-8") as f_val, \ open(f"data/test.tsv", "w", encoding="utf-8") as f_test: for i, data in tqdm(enumerate(all_data)): line = to_line(data) if i < train_ratio * data_size: f_train.write(line) elif i < (train_ratio + val_ratio) * data_size: f_val.write(line) else: f_test.write(line) 確認してみると、ランダムにデータが分割されたことがわかります。 学習の実行 モデルの定義などは参考元と同じため、ここでは割愛します。 https://qiita.com/sonoisa/items/a9af64ff641f0bbfed44 事前学習モデルには megagonlabs/t5-base-japanese-web を使用します。 A100(80GB)時の設定について Google ColaboratoryなどでGPUにA100を使う場合、プリインストールされているtorchではCUDAが対応していないというエラーが起きます。その場合には、以下のコマンドで対応するtorchなどをインストールします。 !pip install -qU transformers[ja] pytorch_lightning sentencepiece torch==1.10.0+cu111 torchvision==0.11.1+cu111 torchaudio torchtext -f https://download.pytorch.org/whl/torch_stable.html その他ハイパーパラメータ ハイパーパラメータについては以下のように設定します。 モデルのチェックポイント周りの定義を行っておくと、もしも途中で学習が終了する(PCが止まる、Google Colaboratoryのセッションが切れてしまう)自体に陥っても、学習を途中から始めることが可能なため設定しておくことをおすすめします。 (どうしても時間がかかってしまうような、このような大規模モデルの学習では非常に便利だと感じました) # 学習に用いるハイパーパラメータを設定する args_dict.update({ "max_input_length": 1024, # 入力文の最大トークン数 "max_target_length": 64, # 出力文の最大トークン数 "train_batch_size": 8, "eval_batch_size": 8, "num_train_epochs": 2, }) args = argparse.Namespace(**args_dict) train_params = dict( accumulate_grad_batches=args.gradient_accumulation_steps, gpus=args.n_gpu, max_epochs=args.num_train_epochs, precision= 16 if args.fp_16 else 32, amp_backend='apex', amp_level=args.opt_level, gradient_clip_val=args.max_grad_norm, default_root_dir=f"{MODEL_SAVE_DIR}/checkpoint", ) 以下を実行して、転移学習を行います。 # 転移学習の実行 model = T5FineTuner(args) trainer = pl.Trainer(**train_params) trainer.fit(model) # 最終エポックのモデルを保存 model.tokenizer.save_pretrained(MODEL_DIR) model.model.save_pretrained(MODEL_DIR) 推論処理 以下のコードを実行することで推論処理を行います。 article_body = "本文" MAX_SOURCE_LENGTH = 1024 # 入力される記事本文の最大トークン数 MAX_TARGET_LENGTH = 64 # 生成される出力の最大トークン数 import re import pickle from tqdm import tqdm tag_regex = re.compile(r"<[^>]*?>") def normalize_text(text): text = text.replace("\t", " ") text = normalize_neologd(text) text = tag_regex.sub("", text) return text def preprocess_body(text): return normalize_text(text.replace("\n", " ")) # 推論モード設定 trained_model.eval() # 前処理とトークナイズを行う preprocessed_body = preprocess_body(article_body) inputs = ["title: " + preprocessed_body, "long_title: " + preprocessed_body, "topics_title: " + preprocessed_body, "summary_1: " + preprocessed_body, "summary_2: " + preprocessed_body, "summary_3: " + preprocessed_body] batch = tokenizer.batch_encode_plus( inputs, max_length=MAX_SOURCE_LENGTH, truncation=True, padding="longest", return_tensors="pt") input_ids = batch['input_ids'] input_mask = batch['attention_mask'] if USE_GPU: input_ids = input_ids.cuda() input_mask = input_mask.cuda() # 生成処理を行う outputs = trained_model.generate( input_ids=input_ids, attention_mask=input_mask, max_length=MAX_TARGET_LENGTH, temperature=1.0, # 生成にランダム性を入れる温度パラメータ num_beams=10, # ビームサーチの探索幅 diversity_penalty=1.0, # 生成結果の多様性を生み出すためのペナルティ num_beam_groups=10, # ビームサーチのグループ数 num_return_sequences=1, # 生成する文の数 repetition_penalty=1.5, # 同じ文の繰り返し(モード崩壊)へのペナルティ ) # 生成されたトークン列を文字列に変換する generated = [tokenizer.decode(ids, skip_special_tokens=True, clean_up_tokenization_spaces=False) for ids in outputs] # 生成された文字列を表示する for i, generated_str in enumerate(generated): if i == 0: print(f"title: {generated_str}") elif i == 1: print(f"long_title: {generated_str}") elif i == 2: print(f"topics_title: {generated_str}") else: print(f"summary {i-2}: {generated_str}") # 前処理とトークナイズを行う の部分でプロンプトを与えつつ、トークナイズを行います。 # 生成処理を行う の生成処理ではハイパーパラメータを変更することで、処理時間や精度のトレードオフ関係を調整します。 実際の推論 実際の推論は以下のようになります。(NIFTY Tech Day 2022より) 全てにおいて成功するわけではなく、以下のように失敗する例もあります。 おわりに 今回はNIFTY Tech Day 2022では話せなかった細かい転移学習の手法などについて書いてきました。 深層学習周りの自然言語の歴史や、クラウド上のアーキテクチャに関してもNIFTY Tech Day 2022でお話したので、 興味がある方はぜひ御覧ください。 最近だとChatGPTのような流暢な対話型のAI技術も登場し、いよいよ自然言語処理は人間に近い存在になりつつありますね。 実際に深層自然言語処理による要約生成などに挑戦してどのような苦労があったのか?についてもまたどこかの機会に発表したいと思います。 We are hiring! ニフティでは、さまざまなプロダクトへ挑戦するエンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトよりお気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering この記事は、 ニフティグループ Advent Calendar 2022 11日目の記事です。 明日は ニフティライフスタイル のsaikeiさんです。次回もお楽しみに!
アバター
この記事は、 ニフティグループ Advent Calendar 2022  10日目の記事です。 はじめに こんにちは!インフラシステムグループの仲上と申します。 今回は、Slackでインタラクティブなリマインダーアプリを作成したので、紹介したいと思います。 背景 突然ですが、皆さんはSlackを使っていますか?Slackには優秀なリマインダー機能があり、繰り返し設定や日付時間指定、リマインド相手など細かいところまで設定することができます。 しかし、こちらの機能はコマンドの構文を覚えるのが大変です。特に繰り返し系のコマンドは少し複雑なので、覚えるまで苦労します。(英語ができれば自然と書けると思いますが、筆者は英語が全くできません!) また、忙しいタスクの中で、コマンドを打ってリマインダーをセットするのは大変ですよね。 そこで、Slackのショートカットから呼び出せるインタラクティブなアプリを作ってみました。 インタラクティブとは 「 対話 」または「 双方向 」といった 意味 で、ユーザーがパソコンの画面を見ながら、対話をするような 形式 で 操作 する 形態 を指す。 https://kotobank.jp/word/%E3%82%A4%E3%83%B3%E3%82%BF%E3%83%A9%E3%82%AF%E3%83%86%E3%82%A3%E3%83%96-901 ここでは、modal(Slackのショートカットをクリックしたときに表示されるポップアップのこと)への入力内容に反応して自動で入力項目が書き換わっていることから「インタラクティブ」と呼ばせていただきました。 成果物 アプリには以下の機能があります 単発のリマインドセット 繰り返しのリマインドセット 毎日、毎週、毎月、毎年から選択 投稿先指定 構成 Slackは公式ライブラリとして、 Slack bolt を公開しています。こちらのライブラリがとても便利なので、今回はNode.js + Slack bolt の構成でAWSのlambdaにデプロイしました。 デプロイには、Serverless Frameworkを用いています。 modal(ショートカットをクリックしたときに表示される画面)は Slack Block Kit を用いて作成しました。 費用については、リクエストに応じて課金されるので、個人で使用する分にはほとんどかかりません。(1月当たり数百円程度) 作り方 コードはすべて こちらのリポジトリ に公開しています。 準備物 Node.js(自分のローカルは v16.17.0) AWSアカウント AWS CLI のインストールとプロファイル設定 手順 1.Slackアプリの設定 2.環境構築系 3.Serverless Frameworkの整備 4.Lambda関数の準備 5.環境変数の設定 6.デプロイ 7.イベント通知先のURLを設定 より詳細な手順に関しては、以前別の社員が こちらの記事 で紹介しているので、今回はコードとコマンドのみ紹介させていただきます。 1.Slackアプリの設定 display_information: name: reminder_maker features: bot_user: display_name: reminder_maker always_online: false shortcuts: - name: リマインダーセット type: global callback_id: socket-mode-shortcut description: リマインドコマンド生成 oauth_config: scopes: bot: - channels:history - chat:write - reminders:write - reminders:read - commands settings: event_subscriptions: request_url: https://example.com # イベント送信先。後で変えるので一旦適当 bot_events: - message.channels interactivity: is_enabled: true request_url: https://example.com # イベント送信先。後で変えるので一旦適当 org_deploy_enabled: false socket_mode_enabled: false token_rotation_enabled: false 2.環境構築系 Serverless Framework の設定 $ npm init (npm initの設定はすべて何も入力せずEnterでOK) $ npm i -g serverless # Serverless Frameworkインストール ライブラリの準備 $ npm install @slack/bolt 3.Serverless Frameworkの整備 service: serverless-bolt-js frameworkVersion: '3' provider: name: aws runtime: nodejs14.x region: ap-northeast-1 environment: SLACK_SIGNING_SECRET: ${env:SLACK_SIGNING_SECRET} SLACK_BOT_TOKEN: ${env:SLACK_BOT_TOKEN} functions: slack: handler: app.handler events: - http: path: slack/events method: post plugins: - serverless-offline 4.Lambda関数の準備 今回コードが長くなってしまったので、ピックアップして紹介します。 全文はGitHubのリポジトリを見てください。 この辺りはNode.js + Lambda でインタラクティブアプリを作るための初期化です。 // Initialize your custom receiver const awsLambdaReceiver = new AwsLambdaReceiver({ signingSecret: process.env.SLACK_SIGNING_SECRET, }); // Initializes your app with your bot token and the AWS Lambda ready receiver const app = new App({ token: process.env.SLACK_BOT_TOKEN, usr_token: process.env.SLACK_USER_TOKEN, receiver: awsLambdaReceiver, }); ショートカットが押された時の動作を定義しています。 app.shortcut('socket-mode-shortcut', async ({ shortcut, ack, context, logger }) => { ~ } 送信先の選択肢が変更された時のmodal更新を行っています。 Slack boltでは、app.actionにactionIDを渡してあげることでボタンが押された時の動作を定義することができます。 app.action('actionId-3', async ({ ack, context, logger, body, payload }) => { ~ } 送信ボタンが押された時の制御をしています。 app.view('modal-id', async ({ ack, body, view, client, logger, payload, context }) => { ~ } 最後にLambda関数のイベント処理を書いて完成です。 module.exports.handler = async (event, context, callback) => { const handler = await awsLambdaReceiver.start(); return handler(event, context, callback); }; 5.環境変数の設定 $ export SLACK_SIGNING_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx $ export SLACK_BOT_TOKEN=xoxb-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 6.デプロイ $ sls deploy 7.イベント通知先のURLを設定 デプロイ後に表示されたURLをここに書きます。 動作確認 すべての設定が完了すると、ショートカットが選択できるようになっているはずです。 ここをクリックすると、最初に紹介したmodalが表示され、リマインドが登録できると思います。 まとめ 今回は、Slackのリマインド設定を楽にするために、インタラクティブなアプリを作成してみました。 Slack boltに初めて触ってみましたが、イベントの制御が非常に優秀でとても使いやすかったです。また、今回のように動きを持ったアプリのほうが、使っていて楽しいなと感じました。 普段コードをあまり書かないため、今回書いたコードはとても長くなってしまいました。modalのブロック部分はほかのjsonファイルに保存して、適宜呼び出すような形にしたほうが良いかもしれません。 また、現状はリマインドの登録機能のみ実装されているので、削除機能やリスト表示機能なども追加したいと考えています。 皆さんもぜひ使ってみてください! 次回予告 明日は「 @ibukinakamura 」さんの「Google Colaboratoryで自然言語処理モデルT5をチューニングする」です!お楽しみに! 参考 https://slack.dev/bolt-js/ja-jp/tutorial/getting-started https://slack.dev/bolt-js/ja-jp/deployments/aws-lambda https://api.slack.com/block-kit 【初心者でもできる!】SlackのTimesチャンネルの開設を通知しよう! We are hiring! ニフティでは、さまざまなプロダクトへ挑戦するエンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトよりお気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering
アバター
NIFTY Tech Talkは、ニフティ株式会社の社員が主催するトークイベントです。 本イベントでは、ニフティグループの社員が業務を通じて学んだことを発信しています! 第8回目のテーマは「ニフティのデータ分析を語る会」です。 ニフティのデータ分析部隊のメンバーから実際の業務で培われたノウハウについて、LTと質疑応答を交えたディスカッション形式で語っていただきます。 概要 日程:12月20日(火)12:00〜13:00 配信方法:YouTube Live 視聴環境:インターネット接続が可能なPC/スマートフォン 参加方法 YouTube Liveにて配信いたします。 connpass にて登録をお願いいたします。 YouTube LiveのURLは決定後、connpass内の参加者への情報欄に記載いたします。 こんな方におすすめ データ分析系の業務に関わるエンジニア ニフティの現場で使われているデータ基盤やデータ分析の技術について興味がある方 ニフティの技術や風土に興味がある方 タイムテーブル 時間 コンテンツ 12:00 – 12:05 オープニング+会社紹介 12:05 – 12:10 登壇者紹介 12:10 – 12:25 データ基盤について 12:25 – 12:40 Tableau、TableauServerの社内の活用事例 12:40 – 12:55 ChatGPTで賑わう自然言語処理技術、ニフティのデータサイエンスについて 12:55 – 13:00 まとめ+クロージング テーマ ニフティでデータ分析・データ基盤の業務に携わっている方々に登壇していただき、LT形式で発表していただきます。 データ基盤について Tableau、TableauServerの社内の活用事例 ChatGPTで賑わう自然言語処理技術、ニフティのデータサイエンスについて 登壇者プロフィール 松下 俊平(ファシリテータ) 管理会計・データ分析チームで購買データやお客様の声のデータ分析をしています。 分析でよく使うのはSQL、Tableauです。 黒羽 孝夫(登壇者) 継続的にデータ活用・分析を実現するために、データ基盤を続けているニフティN1!データアーキテクト。 いかに楽しく働くかってことを考えてます。 打矢 光(登壇者) 『データエンジニアの方に整えて貰ったデータ基盤』と『ビジネス的なドメイン知識』・『分析技術』を掛け合わせて、 データ分析起点での、①社内各所からの分析相談 ②問題提起や解決提案 などを行っています。 瀬川 雄太(登壇者) ニフティN1!データサイエンティストの瀬川です。 データセンター管理やクラウド開発を学んだと思ったら、気づけばコンテナ開発や機械学習をしていたエンジニアの端くれです。 今は経営管理部門に異動してビジネスを学び、データを活かして主力商品のマーケティング推進に携わっています。 ニフティグループでは一緒に働く仲間を募集中です 新卒採用、キャリア採用を実施しています。ぜひ リクルートサイト をご覧ください。 ニフティエンジニアが業務で学んだことやイベント情報を エンジニアブログ にて発信しています! ニフティエンジニアのTwitterアカウントを作りました NIFTY Tech Talkのことや、ニフティのエンジニアの活動を発信していきます。 Tweets by NIFTYDevelopers 「NIFTY Tech Day 2022」を開催しました 技術イベント「NIFTY Tech Day 2022」のアーカイブはこちら NIFTY Tech Day 2022 アンチハラスメントポリシー 私たちは下記のような事柄に関わらずすべての参加者にとって安全で歓迎されるような場を作ることに努めます。 社会的あるいは法的な性、性自認、性表現(外見の性)、性指向 年齢、障がい、容姿、体格 人種、民族、宗教(無宗教を含む) 技術の選択 そして下記のようなハラスメント行為をいかなる形であっても決して許容しません。 不適切な画像、動画、録音の再生(性的な画像など) 発表や他のイベントに対する妨害行為 これらに限らない性的嫌がらせ 登壇者、主催スタッフもこのポリシーの対象となります。 ハラスメント行為をやめるように指示された場合、直ちに従うことが求められます。ルールを守らない参加者は、主催者の判断により、退場処分や今後のイベントに聴講者、登壇者、スタッフとして関わることを禁止します。 もしハラスメントを受けていると感じたり、他の誰かがハラスメントされていることに気がついた場合、または他に何かお困りのことがあれば、すぐにご連絡ください。 ※本文章はKotlinFest Code of Conductとして公開された文章( https://github.com/KotlinFest/KotlinFest2018/blob/master/CODE-OF-CONDUCT.md )を元に派生しています。 ※本文章はCreative Commons Zero ライセンス( https://creativecommons.org/publicdomain/zero/1.0/ ) で公開されています。
アバター
この記事は、 ニフティグループ Advent Calendar 2022  8日目の記事です。 はじめに こんにちは!会員システムグループの渡邊です。普段はニフティトップページの開発運用を担当しています。 今回はニフティトップページでも採用しているNext.jsを使ったサイトの表示速度を改善する方法について紹介します。 実行環境 MacBook Pro(M1、2021) macOS v12.6.1 Next.js v13.0.6 next/image 画像ファイルはウェブページ全体のバイト数の半分を占めると言われるほど、ページを読み込むときに負荷がかかる部分です。 こういった問題を解決するために画像サイズを縮小させるTinyPNGなどのサイトを使ったり、軽量な画像フォーマットに変換するという作業を行うことがあると思います。 Next.jsでは、next/imageと呼ばれるコンポーネントを呼び出すことで、軽量な画像フォーマットに自動で変換する仕組みを備えています。 使い方は単純で以下のようにコンポーネントを呼び出し、必要なプロパティを渡してあげるだけです。 以下の例では、1920×1080の画像を読み込み500×250リサイズしています。 import Image from 'next/image' import styles from '../styles/Home.module.css' export default function Home() { return ( <div className={styles.container}> <h1>画像テスト</h1> <div>next/image</div> <Image src="/tree.jpg" alt="tree" width={500} height={250} /> <div>imgタグ</div> <img src="/tree.jpg" alt="tree" width={500} height={250} /> </div> ) } 仕組みとしてはImageコンポーネントで読み込んだ画像をGoogleが開発している次世代画像フォーマットのwebpに変換します。 テスト用にnext/imageとimgタグを使って2枚の画像を表示してみると以下のようになります。特に劣化は感じることはなく、サイズを見ると1/10以下に削減できています。 https://nextjs.org/docs/api-reference/next/image next/dynamic 次にES2020から追加された新機能の動的インポートをNext.jsでも行えるようにする機能です。 トグルやモーダルなどの初回ロード時では不要な処理をユーザーが操作したときに都度読み込みを行うようにするものです。 一部の処理を動的インポートにすることで初回ロード時に読み込む量を減らすことができます。 静的インポートと動的インポートの書き方の違いは以下のようになります。 https://nextjs.org/docs/advanced-features/dynamic-import // static import import List from './list' import List2 from './list2' // dynamic import import dynamic from 'next/dynamic'; const List = dynamic(() => import('./list')) const List2 = dynamic(() => import('./list2')) next/script 外部スクリプト読み込みは、パフォーマンスに大きな影響を与える原因の一つです。 外部スクリプトはこちらから手を入れることができず、そのまま読み込むしかないため、パフォーマンス改善の中では一番苦しめられる部分だと個人的には思います。 こういった問題を改善するために登場したのがnext/scriptです。 next/scriptは、外部スクリプトの実行順序を制御することができ、strategyというプロパティを使用することで自動的に優先順位をつけることができます。 書き方は以下のようにScriptコンポーネントにsrcとstrategyプロパティを渡してあげるだけです。 <Script src="<https://example.com/script.js>" strategy="beforeInteractive" /> strategyには3つのプロパティが用意されています。 beforeInteractive ページが表示される前に読み込みが開始する バンドルされたJavaScriptを読み込むより前に実行することができる 最優先で読み込まれるので、実行速度が求められる広告スクリプトなどで有用 afterInteractive プロパティを何も指定していないと選ばれるもの ページが表示された直後に読み込みが開始される タグマネージャーやGAなどのスクリプトに有用 lazyOnload ページが表示されてアイドル状態になった後に読み込みが開始される すぐに読み込む必要がなく、ページが完全に表示されてから使われるようなチャットボットやソーシャルメディアウィジェットなどで有用 以上のことから beforeInteractive > afterInteractive > lazyOnloadの順番でスクリプトが読み込まれるということになります。 https://nextjs.org/docs/api-reference/next/script バンドルサイズを確認する 最後にバンドルされたファイルに含まれる各パッケージの容量を可視化するツールを使って、アプリケーションのどこの読み込みに時間がかかっているか可視化してみます。 webpack-bundle-analyzerをインストールします。 yarn add -D @next/bundle-analyzer next.config.jsに以下の設定を追加します。 const withBundleAnalyzer = require('@next/bundle-analyzer')({ enabled: process.env.ANALYZE === 'true', }); module.exports = withBundleAnalyzer({}); 最後にコマンドを実行します。 ANALYZE=true yarn build 今回はライブラリによる読み込みサイズを見るために日付ライブラリを複数導入し、同じ処理を実行してみました。 日付ライブラリでメジャーなday.js、date-fns、luxonを入れて検証しました。 3つ比較してみましたが、luxonが圧倒的に重い処理をしていることがひと目で分かります。同じような処理をしているライブラリでもここまでの差が生まれるので、慎重に選ばないといけないことがわかります。 加えてコンポーネント単位での確認もできるので、どの機能に原因があるかの特定が容易にできるのも良い点です。 まとめ 以上がニフティトップで採用しているパフォーマンス改善の一部になります。どれもNext.jsを採用しているサイトならすぐに実施できることなので、積極的に活用することをおすすめします。 パフォーマンス改善において大事なのは可視化できることなので、原因を自動で特定し、数値化するLightHouse ciの導入もおすすめです。 We are hiring! ニフティでは、さまざまなプロダクトへ挑戦するエンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトよりお気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering 明日は、 nakanowai さんの「AWS re:Invent 2022で発表されたこと」のまとめ記事です。 お楽しみに!
アバター
この記事は、 ニフティグループ Advent Calendar 2022 5日目の記事です。 はじめに 会員システムグループ SREチームの浅見( @rubihiko )です。 インシデント管理、リスク分析の重要性の高まりから色々調べていたところ、以下の記事の記事をみて良いと思ったので試してみました。 その SLO は現実的ですか?SRE 的なリスク分析手法 Googleでは 稼働準備レビュー(PRR) 時に リスクを優先順位付けして明確化 することを推奨しているようで、ニフティでも Production Readiness Checklist を作って運用し始めているので、取り入れていきたいなと思っています。 用語の説明 平均検出時間(MTTD:Mean Time To Detect) 問題が発生し、修復する人(やシステム)が検知するまでの平均時間です 平均修復時間(MTTR:Mean Time To Repair) 人(やシステム)が問題を検知して、それを修復するためにかかった平均時間です 平均障害間隔(MTBF:Mean Time Between Failures) 障害を復旧させサービスが使えるようになってから、次の障害が発生するまでの平均時間です MTTD と MTTR と MTBFの関係 その SLO は現実的ですか?SRE 的なリスク分析手法 年間の損失 以下の数式でリスクに対しての年間の損失が導けます (MTTD + MTTR) * (365.25 / MTBF) * percent of affected users MTTD(分): 5分 MTTR(分): 120分 MTBF(日): 365日 (5 + 120) * (365.25 / 365) * 100% = 125分 年間で約125分停止するリスクがあるとわかります リスク分析 MTTD, MTTR, MTBFの関係がわかりました。 これにユーザーインパクトを加えると損失を導くことができました。 この計算式を使い、リスク分析へと進んでいきます。 今回は、サンプル事例とデータを使って見ていきたいと思います。 記事の中に、リスク分析のためのテンプレートがあるためこちらをコピーして使います。 このテンプレートはいくつかのシートで構成されており、リスクの洗い出し、要素の洗い出し、リスク許容の判断をすることが出来ます。 CRE Risk Analysis Template – Public ※ ここでは、テンプレートに書いてある用語を以下のよう置き換えて考えています ETTD = MTTD ETTR = MTTR ETTF = MTBF ETTD Estimated Time To Detection – how long it would take to detect and notify a human (or robot) that the risk has occurred; aka MTTD. ETTR Estimated Time To Resolution – how long it would take to fix the incident once the human (or robot) has been notified; aka MTTR. ETTF Estimated Time To Failure – estimated frequency between instances of this risk; aka MTBF. リスクカタログ リスクの洗い出しを行います。 過去にあった障害をベースに考えるのがよいと思います。 リスクカタログ このリスクカタログにある、 「新規機能リリースの影響で一部機能が利用不可」 の項目の場合の詳細としては、「リリースをしたが、ある一定数のユーザーに対してエラーとなっており、それに気がついたのは1日後だった。気がついた後10分で切り戻しを行い解消した。この障害は60日毎に発生してた。」としたものです。 つまり表に入力する値としては以下のようになります。 一定数のユーザーに対してエラー: impact 2% 気がついたのは1日後: MTTD 1440(min) 気がついた後10分で切り戻しを行い解消: MTTR 10(min) この障害は60日毎に発生してた: MTBF 60(days) 損失の計算のとおり、年間における障害の時間が算出されます。 年間の平均障害数: incidents/year 6.09(回) 年間の平均障害時間: bad mins/year 177(min) リスクファクター リスク全体に影響を及ぼすような要素を洗い出します リスクカタログで挙げたリストに対して、影響を与える要素を追加することになります。 追加する観点は、MTTD, MTTR, MTBFどれを使っても構いません。 リスクファクター 例えば、 「運用過負荷のため、障害検知時間(MTTD)がインシデント毎に+30分増加」 などは、通常の業務が忙しすぎたりしてアラートに気が付かない、他に優先度が高いタスクがありアラートを受け取れない、などが課題としてあり、全てのリスクに対して、MTTRが+30分増加するというものです。 また、 「運用手順書が不足しているため、障害復旧時間(MTTR)がインシデント毎に+10分増加」 はアラートに気が付き対処を開始したが、復旧のための運用手順書が不足しているので、通常よりMTTRが+10分増加してしまうというものです。 このように、全ての障害に影響を与える要素について、リスク(bad mins/year)として追加しているわけです。 リスクスタックランク これまで洗い出したリスクのカタログ・要素を並び替えます このテンプレートを使えば以下のよう自動で並び替えをしてくれます。 ここからは、SLOに対して、どの程度リスクを許容できるか確認するフェーズになります。 リスクスタックランク SLO 99.5% なら全てのリスクを許容してもエラー予算は余りますが・・・ SLO 99.5%の可用性は、年間43.8時間の停止を許容する目標となりますが、ユーザーに見せているサービスとしては低いので良くないように思えます。 社内で利用しているツールや実験的なサービスならありかもしれません。 リスクスタックランク: SLO 99.5% 99.9%, 99.99% を求める場合は当然許容できるリスクが減ります SLO 99.9%, 99.99%の場合はどうなるか見てみます。 SLOが厳しくなるほど許容できるリスクが少なくなっているのが下の図から分かると思います。 リスクを許容しすぎるとエラー予算がなくなってしまい、SLOを維持できなくなってしまいます。 これは、ユーザーへの信頼性低下へと繋がります。 ここでは、下から適当にリスク許容としていますが、実際にはSLOに基づいた適切なリスク許容をする必要があります。 このようにリスクを数値化したことで、データドリブンのアプローチが可能になったのはすごいことだと思います。 リスクスタックランク: SLO 99.99% リスクの許容 分析したリスクは、プロダクト開発にも影響する大きい要素です。 ビジネスサイド(PO)とエンジニアサイドで議論して決める必要があります。 リスクを許容した上で施策を進めるのか、それとも時間をとって改善するのか。 このようなデータがあることでプロジェクトの進行判断の材料になります。 まとめ リスクを数値化したことで潜在的なリスクを明るみにして、優先度付けできました。 テンプレが整っているので、Production Readiness Checklistにも容易に組み込めるのでとても良い考えだと思います。 リスクを可視化するには MTTD, MTTR, MTBF, ユーザーインパクトを使う MTTD: アラートが上がってから検知するまでの平均時間 MTTR: アラートを受け取ってから対応を完了するまでの平均時間 MTBF: サービスが正常になった点から次に障害発生するまでの平均時間 MTTD, MTTR をできるだけ短くすることが信頼性向上につながる MTBF をできるだけ長い期間にすることは信頼性向上につながる リスクを分析すること リスクやリスクを増加させる要素を洗い出し、数値化し、分析する リスクを評価し許容を検討する ユーザーインパクト、ビジネスインパクト、現実的な問題など話し合って決める SLOに応じてリスクは許容できることを理解する SLO 99.99%: ほぼ許容出来ない SLO 99.90%: 一部許容できる SLO 99.50%: ほとんど許容できる 明日は、 supreme3854 さんの tkinterで残業代計算 です。 お楽しみに! We are hiring! ニフティでは、さまざまなプロダクトへ挑戦するエンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトよりお気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering
アバター
この記事は、 ニフティグループ Advent Calendar 2022 4日目の記事です。 はじめに こんにちは。ニフティ 会員システムグループ シニアエンジニアの伊達です。 AWS上で稼働するアプリケーションの開発をするにあたってIaC(Infrastructure as Code)を実践することは一般的になっています。ただ、そのツールにはいくつか候補があるでしょう。ニフティではTerraformを使うことが多くCDKは今のところ少数派です。 今回はCDKを使うにあたってのちょっとしたTipsを共有します(特に設定値に関するものをいくつか用意しました)。とはいえ、まだまだCDK初級者ですので、 @NIFTYDevelopers へ読者諸賢のTipsも教えていただけると嬉しいです。 なお、伊達はTerraformを通らずにCloudFormationとCDKを使い始めたため、それTerraformでも普通にできるよというものがあると思いますが目を瞑っていただけますと幸いです。 また、本記事はCDK v2を前提とし、CDKのコードの言語はTypeScriptを使っています。 CDKとは AWS CDKの特徴は既存のプログラミング言語を使ってAWS上でシステムを構築できる点です。 2022年12月現在ではTypeScript、Java、Python、C#、Go言語で記述ができます。開発者はアプリケーションのコードを書くのと同じようにIDEの恩恵を受けながらAWSのリソースのプロビジョニングをすることができます。 この記事ではCDKそのものの解説などはしません。詳しくはAWSの公式ページやGitHubを参照ください。 オープンソースの開発フレームワーク – AWS クラウド開発キット – Amazon Web Services https://github.com/aws/aws-cdk Tipsその1 Contextで設定値を与える CDKにはContextという仕組みがあり、CDKのStackなどにkey-value形式のデータを渡すことができます。cdk.jsonのcontext内がデフォルト値となります。 例えば cdk initしたばかりのcdk.jsonは以下のようになっています。 { "app": "npx ts-node --prefer-ts-exts bin/tmp.ts", "watch": { "include": [ "**" ], "exclude": [ "README.md", "cdk*.json", "**/*.d.ts", "**/*.js", "tsconfig.json", "package*.json", "yarn.lock", "node_modules", "test" ] }, "context": { "@aws-cdk/aws-lambda:recognizeLayerVersion": true, "@aws-cdk/core:checkSecretUsage": true, "@aws-cdk/core:target-partitions": [ "aws", "aws-cn" ], "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true, "@aws-cdk/aws-iam:minimizePolicies": true, "@aws-cdk/core:validateSnapshotRemovalPolicy": true, "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true, "@aws-cdk/aws-s3:createDefaultLoggingPolicy": true, "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true, "@aws-cdk/aws-apigateway:disableCloudWatchRole": true, "@aws-cdk/core:enablePartitionLiterals": true, "@aws-cdk/aws-events:eventsTargetQueueSameAccount": true, "@aws-cdk/aws-iam:standardizedServicePrincipals": true, "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true } } ここに値を追加することでStack内などで参照することができます。例えば、既存のVPCがあり、それを参照したいという場合には以下のように cdk.jsonに記述します。 ... "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true, "vpc_id": "vpc-xxxxxxxxxxxxxxxx" } コード内では以下のようにして参照します。 import * as cdk from 'aws-cdk-lib'; import { Construct } from 'constructs'; export class ExampleAppStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); // vpcIdの値は"vpc-xxxxxxxxxxxxxxxx" const vpcId = scope.node.tryGetContext('vpc_id'); ... } } Tipsその2 環境を分ける まず、devlepment、staging、productionなど稼働環境を複数持つ場合には、AWSアカウント自体をわけることをおすすめします。同じアカウント内に複数の環境を作るとリソースの重複などを避ける手間があることと、誤った環境にデプロイするなどのオペミスが起きやすくなります。 その上で環境ごとに設定を分けるには、cdk.json内に”stage”といったキーでデータを追加します。 ... "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true, "stage": "development" } これは以下のように参照できます。 import * as cdk from 'aws-cdk-lib'; import { Construct } from 'constructs'; export class ExampleAppStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); // stageの値は"development" const stage = scope.node.tryGetContext('stage'); ... } } 例えばグローバルでユニークな必要があるドメイン名やS3のバケット名を以下のようにしたいとします。development環境にはprefixをつけるパターンです。 環境 ドメイン名 S3バケット名 development dev- app.example.com dev- nifty-engineering-example-bucket production app.example.com nifty-engineering-example-bucket cdk.json内ではstage、domain、bucket_nameを設定します(stageがdevelopmentでほかがproduction用の値なのが不格好ではありますが……)。 ... "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true, "stage": "development", "domain": "app.example.com", "bucket_name": "nifty-engineering-example-bucket" } CDKのコードではstageを参照してドメイン名やバケット名を組み立てます。 import * as cdk from 'aws-cdk-lib'; import { Construct } from 'constructs'; import * as route53 from 'aws-cdk-lib/aws-route53'; import * as s3 from 'aws-cdk-lib/aws-s3'; export class ExampleAppStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); const stage = scope.node.tryGetContext('stage'); let domainPrefix = ''; if (stage == 'development') { domainPrefix = 'dev-'; } const domain = [domainPrefix, scope.node.tryGetContext('domain')].join(''); // 既存のRoute53ホストゾーンを参照 const hostedZone = route53.HostedZone.fromLookup(this, 'ExampleAppHostedZone', { domainName: domain, }); const bucketName = [domainPrefix, scope.node.tryGetContext('bucket_name')].join(''); const bucket = new s3.Bucket(this, 'ExampleAppBucket', { bucketName: bucketName, }); } } Contextはcdkコマンド実行時に上書き指定ができます。以下のようにすることで、コード内で参照される値を変えることができるため、環境ごとに異なる設定でデプロイができます。 $ cdk synth # 指定がないときにはcdk.jsonの値なので stage: "development" $ cdk synth --context stage=development # stage: "development" $ cdk synth -c stage=production # stage: "production" Tipsその3 さらに環境ごとの設定をする 先ほどの書き方の場合には、 if (stage == 'development') { としてましたので、developmentではないときにはproductionという扱いでした。prefixをロジックで追加できるのは良いですが、cdkコマンド実行時にスペルミスすると惨事になりそうです。また、他システムのAPI Keyなど環境ごとに値が全く異なるものもあるでしょう。 以下のように環境ごとの設定をcdk.jsonに記載します。 ... "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true, "stage": "development", "development": { "domain": "app.example.com", "bucket_name": "nifty-engineering-example-bucket", "foo_system_api_key": "ABCDEF012345" }, "production": { "domain": "dev-app.example.com", "bucket_name": "dev-nifty-engineering-example-bucket", "foo_system_api_key": "XYZABC789012" } } その2で記載したコードは以下のようになります。こちらのほうがだいぶスッキリしますね。 import * as cdk from 'aws-cdk-lib'; import { Construct } from 'constructs'; import * as route53 from 'aws-cdk-lib/aws-route53'; import * as s3 from 'aws-cdk-lib/aws-s3'; export class ExampleAppStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); const stage = scope.node.tryGetContext('stage'); const settings = scope.node.tryGetContext(stage); const hostedZone = route53.HostedZone.fromLookup(this, 'ExampleAppHostedZone', { domainName: settings.domain, }); const bucket = new s3.Bucket(this, 'ExampleAppBucket', { bucketName: settings.bucket_name, }); } } Tipsその4 リソースにタグを設定する コスト分析のためにコスト配分タグを使っていると思います。 CDKではStack内のリソースにまとめてタグを設定できます。 以下のようにタグを付けたいとします。 タグ名 値 application example application system example system 例によってcdk.jsonに以下のように書きます。 ... "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true, "application": "example application", "system": "example system" } 以下のようにしてタグを設定できます。scopeはStackでもConstructでも指定できます。 Tags.of(scope).add(key, value) ExampleAppStack内のリソースすべてに同じタグを設定するには以下のように書きます。 import * as cdk from 'aws-cdk-lib'; import { Construct } from 'constructs'; export class ExampleAppStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); cdk.Tags.of(scope).add('application', scope.node.tryGetContext('application')); cdk.Tags.of(scope).add('system', scope.node.tryGetContext('system')); ... } } Tipsその5 リソースにタグを設定する#2 常日頃から活発に開発をしているアプリケーションであれば良いですが、中には一度リリースした後にはほとんど触らないようなものもあります。1年後に手を入れることになり「ドキュメントやレポジトリはどこだっけ……」と調べて回るようなことになりがちです。 タグで各リソースにドキュメントやレポジトリのURLをつけておくと便利です。 タグ名 値 document https://notion.so/barcorporation/xxxxxxxxxx repository https://github.com/barcorporation/example-application タグのキーと値が上記の場合には、cdk.jsonには以下のように記述します。 ... "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true, "document": "https://notion.so/barcorporation/xxxxxxxxxx", "repository": "https://github.com/barcorporation/example-application", } そして、CDKのStackのコードに以下のように書けば、Stack内の各リソースにドキュメントとレポジトリのURLがタグ付けされ、AWS管理コンソールから調査を始めたときにドキュメントに辿り着けるようになります。 import * as cdk from 'aws-cdk-lib'; import { Construct } from 'constructs'; export class ExampleAppStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); cdk.Tags.of(scope).add('document', scope.node.tryGetContext('document')); cdk.Tags.of(scope).add('repository', scope.node.tryGetContext('repository')); } } We are hiring! ニフティでは、さまざまなプロダクトへ挑戦するエンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトよりお気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering 明日は、 @rubihiko さんのSREでのリスク検討に関する記事です。 お楽しみに!
アバター
はじめに この記事は、 ニフティグループ Advent Calendar 2022 7日目の記事です。 どうも!IWSです!普段はお客様が初めて@niftyをご利用になる際の無料ID会員登録システムや、サービスをご利用になる際のログインシステムの開発・運用を担当しています。 そして、それとは別にオンライン会議イベントサポートチーム(通称:オンサポチーム)としても活動をさせていただいています。その活動の一環として、ニフティで毎月行われているTechイベント「NIFTY Tech Talk」の配信サポートもしています。 この記事ではオンサポチームのお仕事、とりわけNIFTY Tech Talkの舞台裏のようなものを書いていこうかなと思います! NIFTY Tech Talk NIFTY Tech Talk はテーマに対してLTやディスカッションの形式で話すニフティのテックイベントです。過去にはスクラムマスター対談やコスト管理術、レガシー脱却など様々なテーマで開催されました。 最初はZoomで NIFTY Tech Talkは、元々は配信にZoomを使用していました。#1から#3までですね。 ZoomにはミーティングをそのままYouTubeに配信する機能があります。当初はこの機能を使い登壇者の方にZoomに集まって話してもらいYouTubeに配信するという方式でやっていました。 しかし、Zoomではワイプとスライドのレイアウトが難しいという問題がありました。 たとえば、下の画像は NIFTY Tech Talk #1 の配信画面の切り抜きです。登壇者の方のカメラワイプがスライドに重なってしまい、QRコードが何のものなのか分からなくなっています。せっかく作ったスライドが隠れてしまうのはもったいないですね…… ワイプでスライドの大事なところが隠れてしまった StreamYard!! 「もっときれいなレイアウトで配信ができないものか」 と、そこに彗星のごとく現れたのが StreamYard !! StreamYardはYouTubeやTwitchなどのPFへの配信ができるライブサービスです。画面の装飾やレイアウト変更、BGMを再生したりコメントを拾ったり……などなど配信をサポートする様々な機能があります。 中でも特にありがたいのがバックヤードの機能です。名前の通り配信の舞台裏といった機能で事前に必要なものをバックヤードに集めておくことで配信への出し入れをスムーズに行えるようになります。 ここがバックヤード! 例えば、スライドを途中で別のものに交代するような場合、事前にバックヤードに集めておくことでワンクリックでスライドを切り替えることができます。バックヤードにはそれ以外にも画面共有や動画などいろいろなものを準備しておけるので様々な状況に対応できると思います。 レイアウトはどうなった? 問題だったレイアウト部分は StreamYard では8種類から選ぶことができます。中でも使い勝手が良かったのはこのレイアウトですね。スライドを大きく見せつつワイプもしっかり出す一番オーソドックスなものではないでしょうか。 NIFTY Tech Talk #5 ちなみに、画面の装飾もStreamYardで行っています。右上にあるニフティとニフティライフスタイルのロゴマークは自作した画像をオーバレイという機能を使って出したものです。簡単なものならこれだけで出せます。 ただ、大きさや位置など細かい調整はStreamYard側ではできないので作るときに気をつけないといけないというのはありました。このロゴマークも実は16:9の透過画像で作成しています。 StreamYard側での調整ができないので「いざ作ってみたらスライドに被ってしまった……」といった事があるのは少し悲しいところ。かゆいところに手が届かない…… お主にはこの共催権限を与えようぞ…… StreamYardの権限には すべての操作ができる「管理者」 配信の開始などの一部操作以外のことができる「共催」 必要最低限の権限だけを持った「ゲスト」 の3パターンがあります。 基本的に登壇者はゲスト権限があればこと足りるのですが、自分の番になったときにスライドを配信に追加する。ということはできない(バックヤードから配信への追加は共催以上の権限が必要)ため、そこの管理だけは管理者がやる必要があります。 第4回では登壇者にゲスト権限を与えて管理者の人力でスライド管理を行っていました。ただ、いくら数人分だけとはいえ、タイムテーブルとにらめっこしながらスライドを上げ下げするのはかなり大変だったため、第5回以降では登壇者の方に共催の権限を付けるようにしています。 共催であればバックヤードから自分やスライドをいつでも配信に追加することができるため、自分の番になったら各自で追加し話してもらう、といったスタイルです。登壇者の方の誤操作でレイアウトが変わったり違うものが追加されたりといった危険性はありますがそこは飲んでいます。タイムテーブルとにらめっこするよりは……という感じですね。 最後に いろいろ話してきましたが、シンプルでスライドの切り替えがしやすく使いやすい、というのがStreamYardの1番のいいところではないでしょうか。NIFTY Tech Talkのように毎回登壇者が変わるイベントではそのたびに使い方を説明するのも大変なのでシンプルで使いやすいというのはそれだけで助かります。 実際にStreamYardで登壇してくれた方からは「スライドをシームレスに切り替えられるのはめちゃ使いやすかった」、「事前にバックヤードで画面共有が出来ていたので、画面共有時のあたふたが無くてよかった」などの声をもらっており、StreamYardを使い始めてよかったのではないかと思っています。 同時に「(共催権限が付いていて)誰でもいじれちゃうので、誤操作怖いなみたいな気持ちになりました!」というような声ももらいました。このあたりは課題としてうまく解決していきたいですね! さて、結局StreamYardのお話がメインになってしまいましたがこの記事はここで終わりにしようかなと思います。 第7回開催まできたNIFTY Tech Talkですが、これからもオンサポメンバーとして、 第8回、第9回……と良いものができるよう新しいやり方や面白いやり方をまだまだ模索して頑張っていきます! ぜひ NIFTY Developers のチャンネル登録や高評価のほどよろしくおねがいします! YouTube : https://www.youtube.com/channel/UCKn__vHAD4yqAzeOUzb_0sQ Twitter : https://twitter.com/NIFTYDevelopers We are hiring! ニフティでは、さまざまなプロダクトへ挑戦するエンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトよりお気軽にご連絡ください! ニフティ株式会社採用情報 NIFTY Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering 明日は、 @D_W さんのNext.jsに関する記事です。 お楽しみに!
アバター
この記事は、 ニフティグループ Advent Calendar 2022 3日目の記事です。 はじめに こんにちは。ニフティ会員システムグループのたけろいどです。 今回は業務改善の一環としてGoogle拡張機能を作ってみました。そのとき躓いたポイントを書いていきます。 背景 みなさんはGoogle拡張機能を作ったことはありますか? 低コストで行える業務改善の手段の一つとしてGoogle拡張機能はとてもよい選択肢です。 私も面倒だなと感じていた作業があったのでGoogle拡張機能をつかって作業の簡略化を図りました。そこで得た知見をみなさんに共有出来たらと思います。 今回はフレームワークにSvelteを使用しています。Svelteについては過去に記事で触れています。 そちら をご覧ください。 また、躓きポイントはサービス固有のものではなくGoogle拡張機能やSvelteの全般的なものを紹介するつもりです。誰かの助けになれば幸いです。 躓き SvelteとCRXJSの相性が悪い 当初 CRXJS を使用して開発環境を整えようとしました。CRXJSとはGoogle拡張機能開発時のVite環境でHMRが可能になるなど開発のサポートをしてくれるライブラリです。 開発体験があがるためモチベの維持や素早いデプロイなど様々なメリットなどあったのですが、インストール時Svelteと依存関係でコンフリクトが起こりました。 ドキュメント にスタートガイドもなく、Svelteのバージョンを3.49.0からいくつか下げるなど行ったのですがうまくいきませんでした。 時間を無駄に浪費していたため、HMRの導入をあきらめ気合でビルドする方向で舵取りしました。 悔しい躓きの一つです ClipBoard APIを動かすのは大変 ブラウザ上でコピペを行うには2種類の方法があります。それがClipBoard APIとexecCommandです。execCommandはすでに非推奨となっておりClipBoard APIを使うのが通常です。しかし拡張機能上ではClipBoard APIを動かすことは困難です。Permission関係なのかGoogle拡張機能の対応が遅れているのか、これだという原因は不明です。 回避方法はありますが こちら の方法少々コストが高く、スマートな方法ではありませんでした。 今回は個人で使うもの&他のGoogle拡張機能のコードでもexecCommandが使用されていたため、execCommandで実装しました。 execCommandやClipBoard APIの仕様や使い方はMDNを参考にしました。 コンポーネントの再レンダリングについて SvelteはRefなどの変数に更新があればコンポーネントを自動的かつ部分的に再レンダリングしてくれます。 この際即時に再レンダリングするわけではなく、ほかに更新するものがないか一旦確認してから再レンダリングします。 つまり再レンダリング後の値を参照するにはすこしだけ待たなければいけません。そこで tick() が役に立ちます。詳細は参考に置いておきます。 値が参照できないなどの問題があった場合は tick() を使うことも検討してみましょう。 まとめ 以上3点の躓きでした。 SvelteとGoogle拡張機能の文献や記事はまだ少なく手探りな状態ですが、知的好奇心をくすぐられて楽しかったです。 いくつか妥協はしましたが、無事Google拡張機能も完成し目的も達成できたのでよしとします。 ありがとうございました。 参考 クリップボードとのやりとり tickについて We are hiring! ニフティでは、さまざまなプロダクトへ挑戦するエンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトよりお気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering この記事は、 ニフティグループ Advent Calendar 2022 3日目の記事です。 次回もおたのしみに!
アバター
概要 この記事は、 ニフティグループ Advent Calendar 2022 2日目の記事です。 こんにちは。ニフティ トップページチームの添野、山田、佐々木、宮本、碇川、渡辺です。今回は共同執筆記事です。 ニフティで開催した障害対応ロールプレイングについてご紹介します。 障害対応ロールプレイングとは、「実際にシステムに障害が起こったことを想定し、擬似的な障害を起こすことで、実際の障害対応をスムーズに行えるようにしよう」というコンセプトでニフティで実施しているイベントです。 詳細内容 担当 開発サイド 障害対応:碇川 補佐兼議事録:佐々木 運営:添野、山田、宮本 企画サイド 関係者への障害告知:渡辺 ロールプレイングで扱った障害 今回の障害対応ロールプレイングで扱った障害について軽くご紹介します。 ニフティトップページでは、ログインしたユーザーのメールやニフティポイントなどの情報を表示しています。各サービスを回らなくても、ニフティトップページにログインするだけで手軽に確認することができてしまうなんて便利ですね! しかし、そんな便利な機能が使えなくなると、ユーザーの方々に大変ご迷惑をおかけしてしまいます。できる限り問題が発生しないよう心がけ、ユニットテストなども実施して日々対策していますが、残念ながら障害とはいつか思わぬ原因で発生してしまうもの。そしていざ障害が発生したとき、あたふたしてしまうだけでは問題です。 ということで 、今回のロールプレイングでは、ユーザーの皆様がよく使う新着メール通数の表示機能に障害を起こすことに決めました。重要な機能にあえて障害を起こすことで、いざというときスムーズに対応できるよう経験を積むことができます。 余談ですが、障害対応ロールプレイングのために、本番環境や開発環境からも完全に独立したロールプレイング専用の環境も用意しました。これなら憂いなく壊し放題です。もちろん、本番と同じアラートやサービス監視用ダッシュボードも完備しています。これなら完璧ですね。 障害!新着メール通数が表示されない! ターゲットが決まったところで、次にどのように障害を発生させるかについてです。ニフティトップページで利用しているAPIは、どれもうっかり外部に公開しようものなら、第三者からの不正アクセスされ放題になってしまいます。これを防ぐため、複数の方法を組み合わせAPIへのアクセスを限定しています。 そして、認証もその仕組みの一つです。つまり、”うっかり運用ミスで認証情報を誤ったものに書き換えた”りすれば、それだけでAPIへのリクエストが失敗するようになり、障害が発生してしまいます。人間のうっかりミスで発生する障害……気をつけてはいても起こってしまいそうです。 障害の原因としてはあっさりしたものですが、もちろん実際の障害対応者にはいろいろと考えることがあります。単にニフティトップページ上の表示では、新着メール通数が表示されないだけです。APIからデータを取得していることを理解していても、どこで問題が発生しているのかはすぐにはわかりません。認証以外でも、たとえば以下のようないろいろな原因を考えることができます。 リファクタリングした際にAPIのリクエスト形式が変わってしまった リクエスト先を開発環境のAPIにしてしまった ニフティトップページ開発チームでは管理していないAPI側での障害 クラウドプラットフォーム自体の障害 etc. アラートの種類や吐き出されるログ、監視用ダッシュボード、その他ドキュメントなども用いて、障害の原因を特定していきます。 ロールプレイングの流れ 当日は、事前に打ち合わせをしてお客様問い合わせから障害が発覚したというシナリオでスタートしました。 お問合せとほぼ同時にアラートも発生させています。ですが、実際の障害だと流石にアラートと同時にお問合せまでは発生しないため、もう少し実際の障害と近いような状況を設定する余地があったかもしれません。この辺りはよりリアルなロールプレイをするための反省点でした。 障害の発生を検知した時点で、開発チームが対応に当たります。ロールプレイでは事前に障害対応者と補佐兼議事録役は決めていたので、初動はスムーズです。   障害対応で行う作業について、議事録役がSlackに記録していく しかし、ここでちょっとした問題が発生します。今回想定しているのは「メール通数が表示されない」という障害であり、データの大元を辿ればそれはトップ開発チームの管轄外のAPIです。そのため、もちろん原因としては大元のAPIに何かが発生したということも考えることができます。もちろん原因がトップページ側にある可能性もあり(今回はそのパターンです)、調査は行っていきますが並行してAPIを管理しているチームに問い合わせを行うのは自然なことだと思います。 が、そのことを完全にロールプレイングの運営側は失念してました……。運営3人いたのに、完全に、素で忘れていました。 そこで、急遽運営が一人外部のチーム役として、対応するようにしました。ロールプレイのシナリオを考える段階で、関係者としてどんな人が出てくるか?という考慮が漏れていると、せっかくの障害対応ロールプレイングもどんどん実際の障害と乖離してしまうため、注意が必要です。 こうして想定から漏れていた外部のチームとのやりとりも行いつつ、障害を解消しようと対応に当たっていただきました。単純な障害の解消以外にも、ユーザーの方に向けたお知らせのための正確な情報収集や、企画サイドとのリアルタイムな意思疎通など、なかなか普段の業務では行わない作業も多いです。 また、実際に障害調査を行う過程を経ることで、どのようなドキュメントが不足しているかについても浮き彫りになります。さらにドキュメントとして用意していても、いざというときまだシステムに不慣れなメンバーでもすぐに参照できる場所にあるか?といったこともわかります。とりあえず必要だからとドキュメントを用意しても、その存在自体が共有できていなかったり、わかりやすい場所になく探すために時間がかかってしまっては宝の持ち腐れになってしまいます。普段のシステム運用時から障害発生時の備えはしていますが、その備えが適切かという点について確認することができるのが良いところです。 ロールプレイの終了後には、運営から発生させた障害原因についての詳しい解説と、障害の特定方法についての解説を行いました。今回のロールプレイを通し見つかった問題点を改善しつつ、より安定したシステム運用を行えるようにしていけるようにしていこうと思います。 所感 ロールプレイの参加者と運営から所感を書いていただいています。 参加者目線 佐々木 今回は補佐兼議事録役として参加しました。 ロールプレイングでは一部サービスが動かないというケースの想定でしたが、ドメイン知識やシステム構成の知識が背景として求められるなと実感しました。すでにあるドキュメントやシステムを理解していないと直ぐに答えにはたどり着けないものもあるので、すぐに情報を得られるように普段からチームで整理しておくことも対策になると感じました。 最終解決までは至らなかったですが、今回のように役割分担を決めておくと実際の障害対応時には動きやすくなると思うので活かしていきたいと思います。 渡辺 企画側の立場で障害時の対応を把握するために開発と一緒に参加しました。 対応するエンジニアがどれだけシステムを把握しているかで初動が変わってくるのがよくわかりました。 全員で同じ知識を持つというのも、難しいことではありますが、みなさんに力を付けていただき、企画側はお客様への周知や関係者への情報提供に努め、不測の事態が起こっても障害に対処できる対応力をつけてゆければと思います。 定期的にこのような機会をもって、企画・開発メンバー全員で対応できるようにしようと思います。 碇川 今回私がメインで障害の対応を行ったのですが、自分が持っている情報だけではうまく解決できない点もあり、補佐役の佐々木さんには頼りきりになってしまったことが反省点です。この障害対応ロールプレイングでどのような立ち回りをすればよいかを理解することができたんじゃないかと思います。 今回得た知見や反省点を活かし、実際の障害時はメンバーとしてうまく立ち回っていきたいです。 運営目線 添野 今回は起こりうるシナリオを想定した上で、開催をしましたが、準備を進める中でも不足している情報がちらほらあり、見直す良いきっかけになりました。またロールプレイングの最中では、参加者の動き方を見て、障害への向き合い方が人それぞれだということを改めて感じました。実際の障害時や次回ロールプレイングを開催する際には今回得た知見を活かしていきたいと感じました。 宮本 普段はいかに安定してシステムを運用するかを考えていますが、「ロールプレイングとしてどんな障害を起こそうか?」というのを考えるのは正直結構楽しかったです。他にもいくつか対象とする障害の案が出ていたので、今後も色々試していきたいと思います。ぜひこの記事を読んでいる方々も、どんな障害が起こりそうか考えてみてください。 一方で、実施してはみたいものの、なかなかロールプレイングとして起こし辛い障害(クラウド自体の障害等)もあります。そうした障害にも対応していけるよう、日々対策は怠らないようにしていきたいです。 山田 ロールプレイング実施に当たっては「こう対応するだろうな」という想定シナリオを用意していましたが、実際はその通りに進まない部分が多く苦労しました。運営側は障害前後の状況などを現実に起こりうる形で作っていく必要があり、そのためには普段の運用をより広く理解していなければならないため、大変ですが学びが多かったとも思っています。 本番に近い形での障害を起こす、という部分でまだ再現が難しいものも多いため、カオスエンジニアリングツールなども取り入れて改善していきたいと思います。 まとめ 今回はニフティで開催した障害対応ロールプレイングについて紹介しました。 このように実際の障害対応を意識したチーム体制で動くことで、本番の対応でもスムーズに動ける対応力が身につきますし、「障害に強いシステムへの改善」という目線でも気づきを得るきっかけにつながると思います。 We are hiring! ニフティでは、さまざまなプロダクトへ挑戦するエンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトよりお気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering 明日は、 @takenokoroid さんのSvelteに関する記事です。 お楽しみに!
アバター
はじめに この記事は、 ニフティグループ Advent Calendar 2022 1日目の記事です。 急に冷え込んだと思ったら、なんともう12月です!年末になると1年を振り返りたくなりますね。 スクラムでは、一定期間(スプリント)ごとにふりかえりを行うことをレトロスペクティブと呼びます。 このレトロスペクティブですが、私たちのチームでは、メンバーが交代でファシリテートをし、毎回いろいろなやり方を試してきました。 以前レトロスペクティブのおすすめを何点か紹介した記事をかきましたが、 レトロスペクティブのおすすめアクティビティ 今回はいろいろなケース別に、良かったふりかえり手法を紹介していきたいと思います。 リモートでも話しやすい場づくり リモートワークも増え、オンラインだとミーティングが盛り上がりにくい、ファシリテーターや一部の人ばかりが話してしまうという話をよく聞きます。 ながら作業で聞いている人や、ゲスト気分で参加している人に、このふりかえりの場に意識を向けてもらうための取り組みをしましょう。 一言だけ話す 最近買ったものは何?とか、無人島に持って行くなら?とか、そういった気軽な質問をします。 アイスブレイクとして一人だけでしゃべってしまう人もいますが、できる限り全員になにか答えてもらうことがお勧めです。 こういったアクションはメンバーが打ち解けることを目的とされやすいですが、実際、MTGの数分で打ち解けることはなかなか難しいです。(何回もアイスブレイクや雑談を積み重ねて打ち解けていくものかと思います) ふりかえりの始まりとしては「なんでもいいから本題に入る前に声出しをしてもらえればOK」くらいの気持ちで取り組んでみましょう。なんなら、あいさつだけでもかまいません。 簡単に体を動かす 参加人数が多い時や、あまり雑談が浮かばないときは体を動かすこともお勧めです。 「みなさんカメラをONにしてください。椅子から立ち上がって、大きく伸びをしましょう」 と言うだけでOKです。 一度作業から離れて、このふりかえりの場に意識を向けてもらえればじゅうぶんに「場がつくれた」と言えると思います。 十分に打ち解けて親しいメンバーばかりだとしても、一度この場に意識を向けてもらうという意味で、なにか話すなり体を動かすなりということから始めることをおすすめします。 さまざまなふりかえり手法 いくつかのふりかえりアクティビティを、40-50分ほどで収まるようなやり方になるよう紹介しています。 チームメンバーを知る ふりかえりというよりは1時間かけられるアイスブレイク手法となります。 価値観ポーカー https://wevox.io/valuescard 詳しいルールは上記サイトをご確認ください。 メンバーがどんな価値観を大事にしているかがわかります。 手持ちの価値観で、優先度が高くない価値観カードをどんどん捨てて行く必要があるので やたらインパクトのある絵面になり、盛り上がりやすいです。 できたばかりのチームではもちろん、もう長く過ごしているメンバーでも、こういうタイプだと思っていたことと、本人が大切にしてる価値観にどれだけギャップがあるかを埋めることができます。 地元トーク 過去に住んでいたところ周辺のおすすめポイントや、自分がそこで当時どう過ごしていたかをGoogle mapのストリートビューをつかって持ち回りで紹介していきます。 地方特有のチェーン店や、気になる不思議な建物、ここの桜が綺麗など、がんばって話を振らなくても話題が尽きにくいです。 自分の過去について話すことは、相手に対して自己開示をしやすくするひとつの足がかりになると思います。 チームの状態を客観視する アジャイルの車輪 SCRUMMASTER THE BOOKのエクササイズを参考にしています。 書籍では「個人個人がどのくらいアジャイルに適応できているか」という検査や他者との比較を目的として行うことになっていましたが、今回レトロスペクティブに取り入れるにあたって 「チームがどのような状態にあるか確認する」 と言う目的にし、みんなで話し合いながら書いてみるというスタイルでやってみました。 上の図では、青色が現在、黄色が半年後になっていたい姿です。 特に意識的にやっていきたい項目に丸をつけています。 ポイント グラフの目盛り表現にはそこまで厳密さを求めない。今(または半年後)どの状態にあるかを話し合うことが、このアクティビティの主題。 すべての項目を半年後に伸ばす必要があるわけではない。半年では伸ばせない項目が何かを知ることも大事 あまり何回もするふりかえりではなく、半年ごとに1回するくらいでOK 熱気球 気球を自分達のチームとみなして、 気球(チーム)にとって上昇気流・追い風になったこと 気球(チーム)にとって荷物・足を引っ張る要因だったこと 気球(チーム)にとって、将来的に嵐・混乱させる要因になりえること を書いていきます。上昇気流と荷物は現在の状況を示し、嵐は将来の予測を示します。 ポイント なにか大きめのリリースに向かっている「途中」に行うと良い 「嵐」を避けて晴れた日に向かうために、できることはなにかを考える 「荷物」が重すぎて動けなくなった時にどう対応するかも考える 人ではなく物事にフォーカスして振り返る ×私の作業が遅くて、みんなの足を引っ張ってしまった ○不慣れなタスクに時間がかかってしまった ふりかえり手法を毎回変えるとチームはどうなるか いくつかふりかえり手法を紹介しましたが「なぜ毎回レトロスペクティブの方法を変えるのか」と聞かれることがあります。 マンネリ防止という観点もありますが、さまざまな視点で振り返るほうがチームの成長につながると思います。 レトロスペクティブはチームの定期検診だと考えています。 定期検診で体重だけを測り続けたとしたら、気づける病気はかなり限られてしまいますよね。 それと同じように、チームの長所や問題も、同じふりかえり方=同じアングルから見ていても限られた情報しか得ることができません。誰しも考え方や視点にはクセがでてしまうものです。 自分の感情にフォーカスしてみる、他人の不安にフォーカスしてみる、チームを客観視してみるというように、あらゆる角度からふりかえって行くことで、チームの本当に良いところや、根深い問題が見つけやすくなります。 また、物事をよりよくするためには、各自が自主的に工夫を凝らすことも必要です。 私たちのチームではスクラムイベントのファシリテーターを持ち回りにしています。 レトロスペクティブは前回と同じふりかえり手法にはしない、と決めておくと「今回のふりかえりはどんな手法がいいかな」「このふりかえりはチームに合いそうだな」というように、各々がいまのチームの状態を見つめるきっかけにもなります。 チームの関係性をよりよくしていったり、問題を早く解決するためには、今までにやっていなかったなにかをしたほうがいいケースが多いです。慣れ親しんだ対処方法を繰り返しても、一定ラインから成長は鈍化します。 ふりかえり手法を毎回変えたときのメリット・デメリット メリット チームの問題が大きくなる前に早くみつけやすい チームの成長をずっと続けることができる 飽きない デメリット あまりうまくいかないふりかえりが発生することもある 準備に少し時間がかかる ふりかえり手法を変えない時のメリット・デメリット メリット 準備の時間が短い 慣れ親しんだ方法なので戸惑わない デメリット マンネリ化しやすい 観点に偏りが出てしまって、新たな問題に気づきにくい 一定ライン以上は成長できない チームの問題に早期に気づき成長を加速するためにも、いろいろなふりかえり手法を試してみることは効果的な取り組みだと思います。 レトロスペクティブという定期検診を多角的に行って、チームの健康と成長を保ち続けましょう。 明日は、 tatsuya-miyamoto さんの ニフティトップページの運用メンバーで障害対応ロールプレイングをやってみたよ! です。 お楽しみに! We are hiring! ニフティでは、さまざまなプロダクトへ挑戦するエンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトよりお気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering
アバター
有志によるスクラム本がでました! N1! 制度でスクラムエバンジェリストを担当している西野です。 9/11にオフライン開催された技術書典13にて、ニフティ有志で執筆したスクラム本の頒布を行いました! スクラムマスターの話を中心に、いろいろなチームのスクラムとの戦いの記録が読めます。 以下からDLできるので、まだ読んでいない方はまずDLしてからこの記事を読んでください! ニフティのスクラム:ニフティ – 技術書典マーケット 発刊記念イベントしてみた! 喜ばしいことに想像以上のDL数があったので、この本を出すにあたってのバックグラウンドトークイベントを 「ニフティのスクラム」執筆者が語る! 〜スクラムぶっちゃけ話〜 と題して行いました。 ↓アーカイブ動画はこちらから↓ 「ニフティのスクラム」執筆者が語る! 〜スクラムぶっちゃけ話〜 – youtube 「ニフティのスクラム」執筆者が語る! 〜スクラムぶっちゃけ話〜のサマリー 登壇者 スピーカー:西野 香織(N1!スクラムエバンジェリスト・マイニフティチーム スクラムマスター) スピーカー:畑谷 啓志(オプションサービスチーム スクラムマスター) スピーカー: 小浦 由佳(社内プラットフォーム チーム スクラムマスター) ファシリテーター: 高田 渉(入会システムチーム スクラムマスター) 登壇者LT ニフティのスクラム」をなぜ出したかと概要紹介 2つのスクラムチームを合体させるとどうなるか? 「ニフティのスクラム」p41 同タイトルの概要紹介 超属人化組織の情シスでスクラム 「ニフティのスクラム」p32 同タイトル記事の概要紹介 スクラム一問一答 〜NIFTY Tech Talk編〜 「ニフティのスクラム」でもスクラム一問一答というコーナーがあるのですが、今回、3人のスクラムマスター同士で本には載せられなかったスクラムに関する疑問について答えてみました。 過去に行った社内スクラムマスターの情報共有会や、お互いに聞いてみたかった内容を質問として取り上げています。 システムを熟知している人の負担が大きくなりがちだけどどうしてる? システムを熟知している人ばかりに仕事の負担が大きくならないよう、属人化解消をどう進めているか 小浦 チームメンバーができる程度の粒度までタスクを分解する 誰でもとれるようなSBIには頭に★をつけて見やすくしている 業務共有やマニュアル化を徹底する もともと、それぞれのメンバーが専任的にプロダクトを担当していたので、自分の担当ではなかったプロダクトのタスクを取るよう工夫している 違う業務をどのくらいの割合担当したかの検査 畑谷 モブプロを実施して属人化を解消している タイトルにあるぶっちゃけ話的にいうと、もともと属人化が進んでいた なんでもモブプロにするわけではなく、コアの部分だけを対象としてモブプロ モブプロだとコードを書く効率自体は上がるわけではないが、レビュー時間が短縮されるため、トータルでかかる工数はそこまで変わらない 西野 システムを熟知している人の理解度を100%だとして、全員が30%くらいまで理解できていれば、理解度100%の人が1人しかいないことは問題ではない タスクを分解したときに、このタスクについてサポートする人をタスクに明記しておく 畑谷さんのチームと同じくモブプロも導入している システムを熟知している人が最初ナビゲーターをやるシーンが多いとおもうが、みんなが慣れてきたら、その人はナビゲーターもドライバーもなるべくせずに見守るようにする 熟知している人がどこまで離れても大丈夫かを少しずつ測る 急ぎの仕事があると「システムを熟知している人にやってもらおう」となりがちなので、属人化解消期間にはそういう仕事をなるべく入れないような調整も必要 よかったレトロスペクティブある? ファシリテーターである高田から挙げた質問。KPTをよく使うが、ほかの良かったレトロスペクティブを知りたい。 畑谷 アジャイルレトロスペクティブズ に載っている内容が素晴らしいため、ここから実践している 特にタイムラインという振り返りを、データ収集のフェーズでよくやっている スプリントの中でやってきたことを付箋に貼って見える化する 特に(スプリントをまたぐような)大きな開発で、開発着手からリリースまでを振り返るときに、このイベントでテンションが上がった・下がったということを共有できる そこから得たデータで、KPTを考えるといった次のアクションにつなげることができる 西野 レトロスペクティブは毎回変えるようにしている レトロスペクティブについてのqiita記事を書いたのでよかったら読んでほしい https://qiita.com/kanishionori/items/9900c6a8d1e7a4e9dda6 障害がおきたときはフィッシュボーンをよく使う 起きた障害を魚の頭として、そこから魚の骨のように、その障害を起こした要因としてこんなものがあったかも…というのを書いていく レトロスペクティブに限ったことではないが、デイリースクラム以外のあらゆるスクラムイベントでファイブフィンガーをおすすめしている 5本指で自分の満足度を表現すする。とても満足なら5(手をパーにする)、まったく満足していないなら0(手をグーにする) 畑谷さんが挙げていたタイムラインだが、タイムラインを書く時にどんなイベントが起きたかを抽出するのが記憶ベースだと大変。どうやっているか 確かに大変。初めてやったときは、事前にタイムラインを書かせてほしいというような要望が出たりした(畑谷) タイムラインを自分もよくつかっているが、思い出すのが大変なのはすごくわかる(小浦) 小浦 象、死んだ魚、嘔吐 象:見ないふりをしている問題 死んだ魚:早くごめんなさいをしたほうがいい悩みの種 嘔吐:ぶっちゃけ話 チームの中で表出してない課題や、メンバーの率直な気持ちが聞ける 今のプロダクトオーナーにどうやって権限を持たせることができたか? プロダクトオーナーに権限がないと、プロダクトの方向性を見失ってしまうこともあるが、スクラムマスターとしてどのようにプロダクトオーナーの権限を持たせたか 西野 とても難しい問題 PO本人も、どんな権限があるかわかってない事が多い まずはそのチームで決められることを把握する もしPOが決定を渋ることがあれば、それを誰に確認しなきゃいけないかを聞くことで、権限をもっていないことがわかる 権限がない状態がなぜ起きているかを知る 周囲から(そのスクラムチームが)不安に思われていることが要因となって、権限が無くなってしまっていることが多いので、その不安を潰すようなアプローチをかける 例えばリリースを何度も遅延させてしまっていて、なにかするたびに上席にレビューを求められている状態だとしたら、リリースを間に合わせるようにして信頼を取り戻すとか 組織全体がスクラムを理解した状態であれば、POに権限を持たせることは難しくないはず 組織に対するスクラム支援もスクラムマスターの責務のひとつ。POに権限があるとこういうメリットがあると説明し、共感してもらうことはスクラムマスターの頑張りどころ (組織から)スクラムチームが信頼される・信頼できる状態にあることは大事 畑谷 これという答えは出せない 実際POを立てるときにとったアプローチとして、権限をPOに持ってこられるよう上のほうで話し合ってもらった 開発の上長経由で、POの上長にアプローチしてもらう 上のほうで話し合ってもらうときに、スクラムをする(POに権限を持たせる)メリットの説明をするには、結果が出ていないと難しい スクラムをやっている当人はメリットを実感しやすいが、ステークホルダーだとなかなか実感は難しい面もあるかも(西野) 最初から(ステークホルダーを含めた)全員が完全に納得してスクラムを始めるのは難しいかも(西野) 小浦 まだまだ道の途中という感じがある 自分のチームの場合はPOがマネージャーなので、権限がある状態 あるべき状態としては、POとマネージャーは違う人の方がいいと思う。いずれはそうしたい PBI管理を、POから開発者に移管して運用している ルール上はPOしかPBIを動かせないが、スクラムチームで合意がとれているなら、POの権限を開発者に委譲してもいいと思う(西野) POが「開発者はここまでやっていい」と権限を下ろせていたり、POが多忙であったりするならば、(合意をとったうえで)開発者が動かしてもいいという話も聞いたことがある(西野) POに権限をもたせて、さらにスクラムチームで細かい権限を分担していくかたちもありだと思う(西野) PBIを動かすことそのものはあまり問題がないが、プロダクトの行く末を決めるような力がまだ弱いので、そこへの注力や周囲への説明をがんばりたい(小浦) スクラムやっててよかったエピソード・一番しんどかったエピソード 畑谷 よかった:リリースの期間の短縮 スクラムやる前は「2-3ヵ月かかります」とざっくり見積もっていたことが、1ヶ月でできるようになった 集中して取り組むと無駄なくリリースできることが実感できた しんどかった:優先度決め ひとつのチームで、対象ユーザーは同じだがいろいろなプロダクトを扱っている どちらの優先度がより高いかを、プロダクト同士で比較・判断することが難しい (プロダクトが違う中で優先度を決められる)真のPOは誰だという悩みもある 西野 よかった:チームメンバーのモチベーション向上 スクラム導入後のフィードバックとしてもらった意見 リリース回数が増えたことで、特にエンジニアのモチベーションが向上した 今までは自分しかできなかった仕事がほかの人もできるようになったことで、障害対応のときに絶対に自分がいないといけないという不安が拭え、心理的安全性が確保された しんどかった:スクラムマスターがひとりしかいない 今は社内でスクラムマスター共有会というイベントがあるので相談先があるが、自分がスクラムを始めた頃はスクラムマスターが各チームに点在している状態だった スクラムマスターの振る舞いについて悩んでも相談できる先がなかなか無い スクラムマスターが凹んでいるとチームにも悪影響が出てしまうため、スクラムマスターである自身のモチベーションコントロールが難しい 小浦 よかった:属人化解消 スプリントごとに誰がどんなチケットを消化したかを検査している。特に(属人化解消の)成果が出たスプリントは、全員で喜び合っている 日々の会話のなかで、メンバーから「いつもと違うタスクができて嬉しい」「スクラムやってよかった」というコメントがでてくるとニンマリしてしまう しんどかった:スクラムマスターの役割はどこまでなのかわからない ひとりで気張って、いつの間にかお母さんみたいになってしまって、勝手にしんどくなってしまっている チームのリーダーも兼任しているので、スクラムマスターでもあり、リーダーでもあり、お母さんでもあり……という状況に陥って、凹んでしまう women developers summitでその話を西野さんがするそうなので、楽しみ 参考リンク)登壇内容こちら: スクラムマスターが「チームのお母さん」にならないための方法 #devsumi まとめ 同じ会社のスクラムマスター同士とはいえ、チームが置かれている環境や目指すゴールも異なるスピーカー3名でしたが、スクラムについての悩みや課題・喜びは話し合ってみて非常に共感できるものが多かったです。 似たような課題でも解決のアプローチが違うことも知れたので、お互いのスクラムチームの良いところを吸収して、自分のチームの課題解決に還元していきたいと思います。 現場にどのように権限を持たせていくかはもっと掘りがいがあるテーマだと思ったので、また機会あれば スクラムマスター対談はまたやってみたいと思っているので、スクラムに関する質問などあれば、ぜひTwitterでこの記事を引用して質問をお寄せいただければと思います。(関係者一同、ウォッチしております!) We are hiring! ニフティでは、さまざまなプロダクトへ挑戦するエンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトよりお気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering
アバター
ニフティ株式会社でマネージャーをしている元安です。 今回はマネージャーとして業務する上で気を付けていることについて経験談を交えながら書かせていただこうと思います。気を付けていることはいくつもあるのですがいくつも書くと長くなるので今回はまず1つ。 マネージャーだから意識しなければならないものではなくどのような立場でも気を付けたい内容ですが、私自身はマネージャーになり、より一層意識しています。それでは早速紹介していきます。 何があったか マネージャーやリーダーは様々な関連部門や関係者から相談を受けることが多い立場です。私も社内外の関係者から様々な相談を受けることがありますが、マネージャーになってから暫くはその相談事に対する対応を自分一人で抱えてしまうことが多々ありました。 自分自身で業務を抱えてしまう理由としては 自分でやった方が早い チームメンバーが忙しそうだから更に業務を振るのが申し訳ない といったものがありました。 前者はある業務に対して熟練してきた人にはありがちで、自分が詳しい領域なので他メンバーに説明してやってもらうより自分がやった方が早いという思いから業務を抱えてしまう「あるある」な話です。 後者は人事異動により多忙なチームのマネージャーになった際に芽生えた意識でした。新たに赴任したマネージャー兼チームリーダーとしてチームビルドをする中でチームメンバーには注力プロダクトの開発業務や新たな技術習得などに力を注いでほしいという思いがあり、同時にメンバーは皆多くの案件対応を抱えている状況がありました。そしてメンバーの負荷を上げないために自分が一人で抱える業務が増えていきました。 どうなったか 結果として以下のような問題が発生します。 自身が一人で業務をいくつも抱えているため手が回らず進まない業務がでてくる 関連部門からの依頼案件の内容や温度感がメンバーに正確に伝わらず、チーム全体で課題や現在の状況を認識できない 新たな経験を積むことで得られるチームメンバーの成長機会を奪ってしまう そのような状況で短期的に私が取った手段は依頼された案件対応を回すことを第一に考え「自分が更に頑張る」というものでした。残業して自分の業務時間を増やし業務が滞らないよう対応しました。結果として何とか業務は回り、ある開発プロジェクトでは社内的にはよい結果を残すことができました。 しかし、このやり方で良い結果が出せても上に挙げた問題点はほぼ解決されていませんし、この成果はチームとして出した成果ではありません。そして自分自身もどんどん疲弊してしまい、このやり方は限界を迎えます。 私はマネージャーのミッションの大きなものに 「チームとして」成果を上げること チームメンバーを育成すること があると思っていますが、上記のような状態ではとてもそのミッションは達成できません。 どう変えていったか そこで改めて「自分がやろう」のスタンスをやめチームメンバーに頼る範囲を増やしていきました。動きの活発なプロダクトについてプロダクト毎に取りまとめを行ってもらうメンバーを決め一緒に業務に入ってもらい業務をシェア + 権限移譲しました。一緒に業務に入ってもらう際に意識してお話ししたのは以下の点です。 現在の状況 今自分が抱えている業務の状況 率直に、今大変で困っているから助けてほしい 期待のすり合わせ どんなことをやってほしいか、逆に私にフォローしてほしいところはどんなところか? 権限移譲 + 責任の範囲を明確化 業務の中でどの部分をお任せするか そして一緒に業務に入ってもらい段々とお任せする範囲を増やしていき、最終的にサブチームのリーダーとして動いてもらうようになりました。いざお願いする前は「忙しいけどお願いして大丈夫かな?負荷が上がってしまい大変じゃないかな?」など考えすぎていましたが、結果としてお任せしたメンバーは業務を通じて成長し社内でもより信頼される存在になっていきました(もちろん忙しい中頑張ってくれて大変だったとは思います)し、私が業務を抱えすぎてボトルネックになることもなくなったのでチームの動きのスピード感も増していきました。 まとめ マネージャーの役割はチームの力を最大限に発揮し成果を上げることです。変に遠慮や気遣いをして自分一人で頑張ろうとしても会社、チーム、チームメンバー、自分自身、誰のためにもなりません。 弊社のマネージャーとチームメンバーは上司、部下という関係性でもありますが、大前提として同じチームで働く仲間です。仲間を信頼し互いに助け合って成果を最大化していけるようチームワークを発揮した働き方をしたいですね。 We are hiring! ニフティでは、さまざまなプロダクトへ挑戦するエンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトよりお気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering
アバター
NIFTY Tech Talkは、ニフティ株式会社の社員が主催するトークイベントです。 本イベントではニフティグループの社員が業務を通じて学んだことを発信しています! 第7回目のテーマは、自社イベントの開催の仕方 〜 NIFTY Tech Day 2022 制作秘話 〜 と題しまして、 先日開催した、NIFTY Tech Day 2022 を裏から支えたメンバーの苦労話や自社開催で得たノウハウについて語っていただきます。 NIFTY Tech Day 2022 のアーカイブ動画はこちら 動画プレイリスト 概要 日程:12月06日(火)12:00〜13:00 配信方法:YouTube Live 視聴環境:インターネット接続が可能なPC/スマートフォン 参加方法 YouTube Liveにて配信いたします。 connpass にて登録をお願いいたします。 YouTube LiveのURLは決定後、connpass内の参加者への情報欄に記載いたします。 こんな方におすすめ ニフティの自分たちでやろうという風土が気になる方 自社イベント開催に興味がある方 配信について興味がある方 タイムテーブル 時間 コンテンツ 12:05 – 12:10 オープニング+会社紹介 12:10 – 12:15 自己紹介 12:15 – 12:25 NIFTY Tech Day 2022 開催概要 12:25 – 12:35 LT: NIFTY Tech Day とシャイな社員 12:35 – 12:45 LT: 「詳しそう」と映像作成担当に任命されたので、撮影から編集まで内製することにしました 12:45 – 12:55 LT: 技術者交流会を開催するまで 12:55-13:00 クロージング テーマ NIFTY Tech Dayの運営の各セクションのリーダーからLT形式で発表を行います。 LT: NIFTY Tech Day とシャイな社員 初めて技術イベント開催するけど、シャイな社員は登壇に消極的!そんなニフティでのセッション完成までのお話。 LT: 「詳しそう」と映像作成担当に任命されたので、撮影から編集まで内製することにしました IT企業で映像制作未経験のエンジニアが映像を内製するはなし LT: 技術者交流会を開催するまで Tech Day 2022にて開催した技術者交流会を開催するまでの苦労話や、どういう流れでそうなったかなどの紆余曲折を語ります! 登壇者プロフィール 田中 星佳(登壇者) ニフティ株式会社 基幹システムグループ サービスインフラチーム NifMoなどの入会系システムの開発・運用を担当しています。 NIFTY Tech Day 2022では、セッションの選定や登壇者との調整を担当しました。 川端 航平(登壇者) ) ニフティ株式会社 会員システムグループ 第一開発チーム 駆け出しSREです。 NIFTY Tech Day 2022では映像の撮影~編集を行う機材班のリーダーでした。 筑木 信成(登壇者) ニフティ株式会社 会員システムグループ 第二開発チーム 技術イベントの企画やキャリア採用を担当しています。 NIFTY Tech Day 2022では技術者交流会・ニフティ分析などの休憩時間コンテンツを担当しました。 伊達 乾(登壇者) ニフティ株式会社 会員システムグループ 第1開発チーム シニアエンジニア @niftyトップページとニフティポイントクラブの開発運用をしているチームのリーダー。 NIFTY Tech Day 2022では運営の統括をしてました。 多田 圭佑(ファシリテータ) ニフティ株式会社 基幹システムグループ 入会システムチーム ニフティの基幹業務システムの開発・運用を担当しています。 NIFTY Tech Day 2022では社内外の広報活動とサイトやバナーなどクリエイティブ制作の調整を担当していました。 ニフティグループでは一緒に働く仲間を募集中です 新卒採用、キャリア採用を実施しています。ぜひ リクルートサイト をご覧ください。 ニフティエンジニアが業務で学んだことやイベント情報を エンジニアブログ にて発信しています! ニフティエンジニアのTwitterアカウントを作りました NIFTY Tech Talkのことや、ニフティのエンジニアの活動を発信していきます。 Tweets by NIFTYDevelopers アンチハラスメントポリシー 私たちは下記のような事柄に関わらずすべての参加者にとって安全で歓迎されるような場を作ることに努めます。 社会的あるいは法的な性、性自認、性表現(外見の性)、性指向 年齢、障がい、容姿、体格 人種、民族、宗教(無宗教を含む) 技術の選択 そして下記のようなハラスメント行為をいかなる形であっても決して許容しません。 不適切な画像、動画、録音の再生(性的な画像など) 発表や他のイベントに対する妨害行為 これらに限らない性的嫌がらせ 登壇者、主催スタッフもこのポリシーの対象となります。 ハラスメント行為をやめるように指示された場合、直ちに従うことが求められます。ルールを守らない参加者は、主催者の判断により、退場処分や今後のイベントに聴講者、登壇者、スタッフとして関わることを禁止します。 もしハラスメントを受けていると感じたり、他の誰かがハラスメントされていることに気がついた場合、または他に何かお困りのことがあれば、すぐにご連絡ください。 ※本文章はKotlinFest Code of Conductとして公開された文章( https://github.com/KotlinFest/KotlinFest2018/blob/master/CODE-OF-CONDUCT.md )を元に派生しています。 ※本文章はCreative Commons Zero ライセンス( https://creativecommons.org/publicdomain/zero/1.0/ ) で公開されています。
アバター
2022年11月2日に  スクラムマスターが「チームのお母さん」にならないための方法  というタイトルで  women developers summit  に登壇させていただきました。 外部イベントでは初めての登壇だったのですが「わかりやすかった」「スクラムをやっていなくても参考になった」という意見もいただけて、登壇してよかったです。 この記事では、登壇で話した内容と、その補足をしていきたいと思います。 スクラムマスターが「チームのお母さん」にならないための方法 自己紹介 ニフティでスクラムエバンジェリストに任命されている西野香織と申します。 このN1! の中で使える費用で、アドバンスド認定スクラムマスターも取得させてもらっています。 写真で掲げている本「ニフティのスクラム」PDF版が以下から無料DLできます! ニフティのスクラム:ニフティ – 技術書典マーケット 登壇を希望した背景 今回公募枠で登壇させてもらったのですが、この話をしたいとおもったきっかけは、社内スクラムマスターの集まりから出た疑問でした。 社内にスクラムチームが10以上 スクラムを導入していこうという動きが会社全体で高まっており、今年はじめて認定スクラムマスターの資格をとった人も多いです。 月1のスクラムマスターの集まり そういう背景もあって、ニフティでは、去年から月1で「スクラムマスター共有会」という集まりを開催しています。 そのスクラムマスター共有会で「チームの自律をうまく引き出せない」という話をよく聞きます。 特に女性のスクラムマスターから「まるで自分がチームのお母さんのようになってしまう」という声があがりました。男性のスクラムマスターも共感していました。 たしかに自分も一時期「私はお母さんか?」という状態になってしまったことがありました。 セッションの対象層 本セッションの対象者は、気を使いがちなスクラムマスターを想定しています。 スクラムをやっていなくても、先輩だとかリーダーだとかで、何かを教える立場になった人や、チームで開発をした経験がある人なら共感できる点があると思います。 スクラムマスターは責任感が強いタイプが多い スクラムマスターは、チームにひとりしかいない役割ということもあってか、責任感が強いタイプが多いなと感じます。 特に「みんなが困っているから」役割を引き受けがちな人 あとは、「誰かリーダーになって」と言われたときに、自分がすすんでリーダーをやりたい、というよりは、みんなが困っている雰囲気を感じて「じゃあ…」とリーダーになるような、献身的なタイプも多い印象です。 スクラムマスターが抱えがちな悩み ここから帽子を被ったネコがちょくちょくでてきますが、スクラムマスターだと思ってください。 自分がスクラムマスターになりたてのころ、お母さんみたいになっていたという状況です。 開発以外の仕事を一手に引き受けている 自分がスクラムマスターになりたてのころ「スクラムをやっていくぞ!」という気負いと「でもスクラムマスターって具体的になにをやればいいんだろう?」という不安から、開発以外の仕事を全部やる人になってしまいました。 チームのお母さんみたいになってしまっている ここでいう「お母さん」とは 誰がやるか明確でない仕事を率先して引き受ける これから問題になりそうなことを先んじて潰す というような、仕事に集中しやすい環境を整えることをやる人、を指しています。 お母さん的な状態になると、なにか問題があると「ちょっとお母さん……じゃなくてスクラムマスター来て!」と、みんながまずスクラムマスターに頼ってしまう、という状態を引き起こしてしまいました。 このセッションで伝えたいこと そういった経験をふまえて、このセッションでは以下の3点を伝えたいと思います。 「よかれと思って」は危険信号 みんなが働きやすい環境をつくらなきゃという思いから「よかれと思って」いろいろな仕事を引き受けると、どのような悪影響があるか スクラムマスターはなんでも屋さんではない スクラムマスターの仕事は、スクラムを回すこと以外は厳密には定義されていないが、そのせいで開発以外の仕事を全部やる人として立ち回ると、スクラムマスターとしてどうなるか 自分がいなきゃ…という欲望からの脱却 このチームには自分がいなきゃだめだという状況は、ある意味で自己肯定に繋がってしまうことがある。そういう執着を手放すために知るべきこと よかれと思っては危険信号 スクラムマスターになりたての頃の悩み とにかくみんなが仕事をしやすい環境を作らなくては……という気持ちから、いろいろな仕事を抱え込んでしまっていました。 かつて自分が製作や開発をやっていた頃、差し込みや調整で作業が中断されてしまうことにストレスを感じていたため、チームメンバーにはそういうつらさを味わってほしくないという思いもありました。 また、1-2年目の人が多いチームだったので、自分はWEB製作のディレクションをしたことはあるが、チームメンバーにはまだそういった経験がないから、自分がやらないとプロジェクトがうまく回らなくなってしまうかも、という不安もありました。 すべてスクラムチームのために「よかれと思って」やっていたことで、これでチームがより良くなるのだろうと信じていました。 どんどんチームのお母さんになっていく そうした「よかれて思って」をつみかさねていった結果、どんどん自分のお母さん化が進んでいきます。 何でも屋さん化 毎日目の前に雑務が積み重なって、スクラムに関する話をチームにすることもどんどん減っていきました。自分は本当にこれでスクラムマスターをやっているといえるのか?という不安も増えていきます。 仕事へのモチベーションが下がる 「誰がやってもいいけど、誰かがやらなきゃいけない仕事」の多くは、明確なアウトプットがないからか達成感も弱く、仕事へのモチベーションが維持しにくくなりました。 チームはそれがスクラムマスターの仕事だと思ってしまう チームもはじめてスクラムをやっている状態なので、自分がそういうふるまいをすることで、スクラムマスターは開発以外の仕事をする役割なのだと思われていました。 最初から「そういうもの」だと思われていると、チームが仕事しやすい環境になるよう尽くしているつもりでも、あまり評価も感謝もされにくかったです。 お母さんが毎日家事をして家を整えていても、それがあまり家族から感謝されなくて悲しくなる状態ってこういうことか……と思ったりもしました。 スクラムマスターはなんでも屋さん ではない スクラムマスターは、スクラムをうまく回していくこと以外に明確にこれをするということがルールとしてないため、なんでも屋さん的になりがちにですが、それは正しい振る舞いでしょうか。 「名前のない仕事」が属人化した弊害 「透明性」がなくなる 「集中」はスクラム価値基準のひとつですが、みんなが仕事に集中できることに目を向けすぎて、スクラム3柱の「透明性」が失われました。 具体的には、プロダクトオーナーと開発者の間で行われるべき調整を、スクラムマスターとプロダクトオーナーだけに閉じて調整している状態がありました。 体調不良などで2日ほど休んでしまったときに、プロダクトオーナーと自分の間で話していたことが開発者に共有されておらず「このタスクはこの方向性でいいんだっけ」と現場を混乱させてしまうこともありました。 チームの状態を見る余裕がない 「明日までに経費申請をしなきゃ」とか「ユーザーから問い合わせが来てたな」ということをひとりで対応していくと、目の前の仕事にばかり意識がむいてしまい、チームの進捗状況やチームメンバーに問題がおきていないかということにも目を配る機会がどんどん減っていきます。 サーバントリーダーシップ=献身的だけど……? スクラムマスターに求められるものとしてサーバントリーダーシップがあげられます。 献身的なふるまいでチームをサポートすることでリーダーシップを発揮するというもので、自分としてはみんながあんまりやりたくない仕事を引き受けることで、献身的にふるまっているつもりでした。しかし消耗するばかりで、なんだかうまくいっている感じがしません。 このままのやり方を続けて、チームメンバーが自己管理や自律ができた状態になっていくイメージがまったく浮かびません。 どこから間違えてしまったのか、あらためてサーバントリーダーシップのあり方について調べ直してみました。 「献身」の方向性を間違えない 今思えば、献身的にふるまうことと、犠牲的であることを履き違えていたように思います。 チームメンバーに献身するということは、誰かにとって面倒なことを代わりにやるのではなく、チームの、その人の成長に身を捧ぐことです。 スクラムガイドにあるスクラムマスターの項目には「支援」という言葉がたくさんでてきますが、自分がやっていたことは、支援・サポートではなくて、代役や代理でした。 犠牲的なふるまいをやめて、チームの成長にフォーカスすることで、チームの得意なことを伸ばし、苦手なことを克服するサポートをしていく、本来のスクラムマスターという役割に戻れた気がします。 デイリースクラムのファシリテーションを分担 絵にあるように、まずはデイリースクラムのファシリテートからメンバーに分担してみました。 自分がファシリテーターを離れたことで「カンバンに載せられなかった作業が実はあって」という感じで、チームに対して相談をしたかったことが話せるようになり、チームメンバーも自分達で仕事を管理していくという意識を持ちやすくなりました。 成長に対して向き合ってみたことで、人に仕事を任せることは負担になることばかりでなく、学びや成長に繋がると実感を持てるようになってきました。 あまり過干渉になりすぎず見守ろう、ということを自分でも意識的にやるようになりました。 ファシリテーションをローテ制にした社内事例 社内で行っているスクラムマスター共有会でも、自分ばかり頑張ってしまうとか、なかなかチームメンバーの方からアクションを起こしてくれない、といった悩みを聞くことがありました。 そういう状況の改善策として、デイリースクラムのファシリをローテ制にしてみるのはどうかという提案をして、実際にうまくいったケースが多いです。 そのときのスクラムマスター共有会には5チームぶんのスクラムマスターがいたのですが、うち半分以上はデイリースクラムのファシリテートをローテーションする文化が根付いていました。 チームが成長する機会を奪い取らない ここでポイントなのですが、気を遣いがちな人は、仕事を人に任せてみようと言われても、実際行動に移す心のハードルが高いと思います。なので、「自分がやってしまうことで、人が成長する機会を奪っているかもしれない」と思うくらいでちょうどいいです。 他律的な状態 自律の対義語として他律という言葉があります。自分にルールがなく、他人にルールを預けてしまっている状態です。 「スクラムマスターの私がうまくまわさなきゃ!」と気負ってしまうと、チームメンバーに「デイリー集まってください」「リファインメントしたほうがいい」というような、指示をするような話し方、いわゆるティーチングの状態になりがちです。 これが続くと、チームメンバーはこの場合、スクラムマスターという他人にいろいろなルールを任せている状態になってしまいます。 自律的な状態 チームの自律を引き出すためにはどうすればいいでしょうか。 自律は、自分で自分のルールをつくって動けること、自分で自分を管理できている状態をさします。 「こうしてください」とは言わずに、問いかけや傾聴を中心としたコーチングをして、自分でトラブル対処できる力をつけてもらうことが自律を引き出す近道でしょう。 そう考えると、今までチームメンバーがハードルに躓く前に取り除いていたことは、成長を阻んでいたともいえます。 スクラムマスターが本当にやるべき仕事は、ハードルを見つけてもらうこと、そのハードルをどう乗り越えるか考えるきっかけを与えることです。 とはいえ、いままでスクラムマスターがやってしまっていた仕事をチームに任せるのは、ただ仕事が増えるように見えてしまい、イヤがられないか心配ですよね。 そのときは、チームがスクラムをどの程度活用できているか、そして次のフェーズはどうなっていくといいか、という観点が持てるといいので、次の章で話していきます。 スクラム活用までの3フェーズ スクラムが浸透するまでをフェーズにわけてみました。 スクラムを導入期、適用期、活用期の3フェーズにわけたとしたら、この3フェーズのなかで、スクラムマスターはティーチングとコーチングの割合を変えていく必要があります。 導入期 チームがスクラムというルールの把握ができるまでです。 この期間は、スクラムとはこういうものであると指示したり教えたりする、ティーチングの割合が多くなります。ここは多少、面倒見のいいお母さん的な振る舞いでも問題ないと思います。 適用期 スクラムイベントに慣れ出して、スプリントごとにリリースも出せるようになってきた時期です。 適用期は「スクラムをやっている」状態ですが、チームや組織が抱える問題解決のために「スクラムを活用できている」状態になることを目指した方がいい状態です。 適用期〜活用期 この時期は、ティーチングの割合を下げてコーチングの割合を増やしていくといいでしょう。お母さん的な振る舞いをやめて、スクラムにおけるコーチや師匠のような振る舞いに変化していきます。 スクラムを学び始めた頃、スクラムマスターは、スクラムを確立させること以外に明確にこれをやれというルールがないのは困るなと思っていたのですが、スクラムの適用度合いやチームの様子をみて、どう振舞うといいかを常に変えていく必要があるから、厳密な定義がないのだと改めてわかりました。 この適用期から活用期に移行するための取り組みとして、スクラムマスターだけがやっていた仕事をチームに分担していくといいです。 チームメンバーには「スクラムがまわってきたので、より上のフェーズにいく」というような説明をあらかじめしておくと良いでしょう。まずはファシリテーションやスクラムイベントの準備・調整などの仕事から分担して、自分達でスクラムをまわしていくという意識を持ちやすくします。 細かい仕事やチーム内外の調整なども「誰かが(スクラムマスターが)やってくれるだろう」というマインドから「私たちがやっていくためにはどうすればいいか」というマインドに変わっていけるように導く必要があります。 スクラムマスターだけができる仕事を無くしていく スクラムマスターだけができる仕事はルール上ないはずですが、さきほど述べたスクラム導入期〜適応期のスクラムマスターは、自分だけがやりがちな仕事を持っていることが多いと思います。 チームメンバーにスクラムを回すことから任せていく 最初にスクラムチームに任せてみることとして、スクラムイベントのファシリテート、特にデイリースクラムがおすすめという話をしました。 ほか、開発外の誰がやるか決まってない仕事をもしスクラムマスターだけがやっていたなら、少しずつチームに戻していきましょう。フォローはしてもいいですが、それをやる機会をチームから奪ってはいけないと基本的に考えます。 仕事しやすい環境を自分たちで作れるようにすることが、チームの自律に必要なことです。 任せた最初は開発が停滞しうまくいかないこともあるかもしれませんが、それ自体はあまり問題ではありません。私たちスクラムマスターが本当にやるべき仕事は、チームの成長を促すことです。 チームメンバーが不得意な仕事を代わりにやるのは、過干渉なお母さんになってしまいます。 こういった仕事をどうしたらうまくいくようにできるか、自分の経験をもとにコーチしていくことが、本来のスクラムマスターの役割・責任といえます。 スクラムマスターがいる意味なくなっちゃう? こうして徐々にスクラムマスターが実務を手放していくと、最初のうちはコーチングすることも多いのですが、だんだん自分がスクラムをまわすために頑張る必要が減っていきます。 チームの自律を考えると本来は良いことですが、自分はなんのためにこのチームにいるのだろう?という気持ちになってしまうこともあるかもしれません。実際問題として、自分は不安な時期がありました。 自分がいなきゃ…という欲望からの脱却 そういえば新入社員のころに、社内報になりたい将来像をというのを書かされたことを思い出しました。 頼られる人になりたい、唯一無二の人になりたいと書く人がとても多いです。人間だれしも、年代問わず、人に必要とされたいという欲求があると思います。 仕事でもなんでも「あなたがいてよかった」って言われると、うれしいですよね。 ただ「君がいないと困る」までくると、スクラムチームとしてはおそらく健全ではない状態です。 スクラムマスターはなぜ1人なのか 「君がいないと困る」状態は健全とはいえませんが、実際問題として、スクラムマスターは一人しかいません。 やっぱり、スクラムマスターの自分がいないと困るのでは……?となりそうですが、スクラムマスターがいないことと、私個人がいないと困る状態は違います。 スクラムをうまく回すテクニックや観点そのものは、個人に属人化させずに、チームに共有していくべきです。 スクラムマスターがチームに1人しかいない理由は スクラムマスターが誰かに感情や意見をひっぱられない中立的な立場をとるため 開発者にもプロダクトオーナーにも属さないポジションの人がチームにひとりいることで、対立を防ぐ という役割もあると思います。 自分もスクラム導入初期に開発とスクラムマスターを兼任していた時期もありましたが、スクラムマスター専任になって少し離れた視点からチームをみてみると、いままで開発者からの意見ばかり聞いていたかもとか、プロダクトオーナーがスクラムチームからやや浮いている状態かもしれないなど、スクラムとして不健全な状態が見えやすくなります。 スクラムマスターはいないと困るかもしれませんが、スクラムマスターをやっているその個人がいないと困る状態にする必要はありません。 スクラムマスターが、スクラムを支援する範囲 でも、スクラムマスターが、自分のチームのスクラムをまわすために頑張る必要が減ってきたということは、本当にスクラムマスターとしての仕事が減ってきたこととイコールでしょうか。 ここでスクラムマスターはそもそもどんな役割だったか、スクラムガイドを読み返してみましょう。 スクラムマスターがスクラムを支援する範囲ですが、スクラムマスターは、プロダクトオーナー、開発者といったスクラムチームに対してのみ、スクラムの支援をするわけではありませんでした。 スクラムガイドには スクラムマスターは、スクラムチームと、より大きな組織に奉仕する真のリーダーである スクラムガイド – ScrumGuide と書かれています。 スクラムチームが健全にスクラムを回せるようになってきたとしても、組織単位だとどうでしょうか。 スクラムが組織に十分に浸透しているといえる状態になるまで、私たちスクラムマスターの仕事は終わりません。 スクラムマスターとはなにか 最後に、スクラムマスターとは一体なんなのか、その目指すところを考えてみます。 直訳すると、スクラムの先生や、師匠という意味が一番近いように思います。 スクラムマスターは、スクラムの理解と教え方を極めるポジションです。 そのため、スクラムマスターがやっていたファシリテーションや、スクラムのノウハウをどれだけチームに伝授したとしても、組織にスクラムをひろめる仕事があります。 なので、今の自分がスクラムについてわかっていることをチームメンバーに伝えても「自分はスクラムをマスターした」さらに「組織全員がスクラムをマスターした」といえるようになるまでは、スクラムについて学ぶことはまだまだあります。 さらに、自分のチームが世界一のチーム、自分の組織が世界一の組織になるまでは、私たちスクラムマスターの仕事は終わりません。 まとめ 「よかれと思って」の先回りは危険信号です 信じて・任せて・フォロー/コーチすることが本当のスクラムマスターの仕事ではないでしょうか。 スクラムマスターはなんでも屋さんではありません スクラムマスターは、スクラムの浸透フェーズに合わせて振る舞いを変えていきます。 何かを任せないという判断は、他者の成長の機会を奪っているのと同じことです。 自分がいなきゃ…という欲望から脱却しましょう 先生、師匠として一歩下がって、チームの成長を見守りましょう。 チームの成長の次は、組織におけるスクラムの成長をサポートしていく仕事もあります。   ニフティ有志によるスクラム本無料配布中! 自己紹介のときに軽く触れましたが、スクラムがうまく行った話、うまくいかなかったけど頑張った話、さまざまなニフティのスクラム話が載っています。 無料ですので技術書典のサイトからDLしてみてください。 ニフティのスクラム:ニフティ – 技術書典マーケット   We are hiring! ニフティでは、さまざまなプロダクトへ挑戦するエンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトよりお気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering
アバター
はじめに こんにちは。入会システムチーム高畑と申します。 ニフティでは打ち合わせ等にGoogle Meetを使用しています。Google Meetで打ち合わせ開始時に誰がいて誰がいないのか、全員参加しているかを確認する時間が発生してしまうため、今回は予め作成したリストからMeet画面に出席していない人を表示するChrome拡張機能を作成しました。 Google Meetでは 出席機能 が提供されておりGoogleカレンダーの参加者情報を元に出席状況を確認できます。しかし、Googleカレンダーに紐付いていなかったり、複数の会議でURLを使いまわしたりしている場合には、Googleカレンダーの参加者リストとは別管理で誰がいないかを確認したい場合があるため拡張機能の作成に至りました。 拡張機能開発概要 Chrome拡張機能はHTML/CSS/Javascriptの知識があれば簡単に作成することができます。 ディレクトリにmanifest.jsonという名前の拡張機能の設定ファイルとHTML/CSS/JavaScript、画像等を配置し、zipにまとめれば完成です。manifest.jsonは以下のように拡張機能の名前や画面右上のアイコンクリック時にポップアップ表示するHTMLファイル名、アイコン画像のファイル名等を指定します。 ポップアップ以外にも設定次第で今タブで開いているページにJavaScriptを追加し内容を書き換えたり、バックグラウンドでJavaScriptを実行したりできるため、翻訳やブックマーク等さまざまな用途に活用できます。 { "manifest_version": 3, "name": "名前", "version": "0.0.1", "description": "誰が居ないかを通知する拡張機能。", "action": { "default_popup": "ポップアップとして表示したいHTMLファイル名" }, "icons": { "128": "アイコンファイル名" }, } この拡張機能でできること この拡張機能ではMeetの参加者から予め作成したメンバーリストに存在しない参加者をリスト表示します。 Meetを開いている際に拡張機能のアイコンをクリックしメンバーリストを入力します。 その後、「不在確認」ボタンをクリックすることで、MeetのHTMLをパースして得た出席者のリストと入力したメンバーリストを照合し、出席していない人のリストを表示します。 メンバーリストはMeetのURL毎にローカルストレージに管理しているため、別のMeetのURLでは別のメンバーリストを持たせることができます。 オプション画面からMeetのURLごとの設定状況を確認できます。不要な設定はこのページで削除することができます。 拡張機能を使いたい Chrome拡張機能として配布していませんが、ソースコードを配布していますのでデベロッパーモードとして導入することができます。導入方法の詳細は以下URLを参照ください。 https://github.com/octop162/dareinai 実現方法 拡張機能の紹介は以上ですが、ここからは実装方法のデータ受け渡し部分について説明します。データの受け渡し部分以外はHTML/CSS/JavaScriptでの開発のため通常のWebページと大差ありません。 Chrome拡張機能には3通りのJavaScriptを当てることができます。 default_popup :Chrome右上アイコンから表示される小さい画面に当てるJavaScript content_scripts :タブで開いているページのHTMLに当てるJavaScript background :開いているページとは別にバックグラウンドで動作するJavaScript 今回は画面右上に表示する小さい画面にタブのMeet画面で表示している内容をもとにしたリストを表示したいため、default_popupとcontent_scriptsの間でデータのやり取りをする必要があります。default_popupの画面からMeetのDOMを直接参照することはできません。 default_popupとcontent_scriptsの間でデータのやり取りをするためにはchrome.tabs.sendMessageを使用することができます( 参考 )。ここではボタンクリック時に{ action: “GET_MEMBERS” }を付加してメッセージを送信しています、結果は非同期で指定した関数(ここではshowAbsentees関数)にコールバックされます。 // popup.js // 不在確認クリック時にmeetのDOMから現参加者を取得する $("#confirm").on("click", function () { chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => { chrome.tabs.sendMessage(tabs[0].id, { action: "GET_MEMBERS" }, showAbsentees); }); }); 送信されたメッセージはchrome.runtime.onMessage.addListenerで受け取ることができます。受け取った{ action: “GET_MEMBERS” }を元にして処理内容を判別し、sendResponseに返したいオブジェクトを入れると元のJavaScriptにコールバックされます。 // content.js /** * ポップアップからメッセージを受け取り、メンバーリストを配列として返す * HTML構造が変化した場合ここのセレクタを修正する */ chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) { if (message.action === "GET_MEMBERS") { switch (message.action) { case "GET_MEMBERS": let members = document.querySelectorAll( "div[role='list'] > div[role='listitem'] > div > div > div > span:first-child" ); sendResponse(Object.values(members).map((member) => member.textContent)); return true; } } }); これで現在の参加者を配列として受けとれますので、あとは元のJavaScriptで照合すれば今回実現したかった不在者のリストを作成することができました。 感想と展望 Chrome拡張機能はデータの受け渡し部分以外は通常のHTML/CSS/JavaScriptの範囲で実装できるためそこまで大変でなく、日々のちょっとした不満の解決策としてとても便利に感じました。 今回はjQueryでサクっと作成してしまいましたが、HTMLの生成部分等が読みづらい箇所があるため、React.jsで書き直したいです。また、機能拡張としては欠席者が表示されるだけで本日お休みなのかたまたま不在なのか判別できないため状況確認できるようになると尚良くなると思いました。 We are hiring! ニフティでは、さまざまなプロダクトへ挑戦するエンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトよりお気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering
アバター
NIFTY Tech Talkは、ニフティ株式会社の社員が主催するトークイベントです。 本イベントではニフティグループの社員が業務を通じて学んだことを発信しています! 第6回目のテーマは「ニフティのスクラム」執筆者が語る! 〜スクラムぶっちゃけ話〜。現在、ニフティでは9人の認定スクラムマスターが在籍しており、多数のチームでスクラムを実践しています。 今回は、先日行われた技術書典13にて頒布した「ニフティのスクラム」について、執筆者によるLTと対談を頒布までの裏話を交えながら語ります。 「ニフティのスクラム」は↓から無料でダウンロードできます。   https://techbookfest.org/product/i99SVfgSGa1uSTwcez6H4Y?productVariantID=e3ivMhH8PXCWbtj3VdpiEi 概要 日程:10月27日(木)12:00〜13:00 配信方法:YouTube Live 視聴環境:インターネット接続が可能なPC/スマートフォン 参加方法 YouTube Liveにて配信いたします。 connpass にて登録をお願いいたします。YouTube LiveのURLは決定後、connpass内の参加者への情報欄に記載いたします。 こんな方におすすめ スクラムに興味がある方、携わっている方 悩めるスクラムマスター 他社のぶっちゃけ話が知りたい方 ニフティの開発スタイルに興味がある方 タイムテーブル 時間 コンテンツ 12:05-12:10 オープニング+会社紹介 12:10-12:15 登壇者紹介 12:15-12:30 LTx3 12:30-12:55 【対談】スクラム一問一答 12:55-13:00 クロージング テーマ 【対談】スクラム一問一答 ではこんな話をします。 システムを熟知している人の負担が大きくなりがちだけどどうしてる? よかったレトロスペクティブある? 今のプロダクトオーナーにどうやって権限を持たせることができましたか? スクラムやっててよかったエピソード・スクラムやってて一番しんどかったエピソード 登壇者プロフィール 西野 香織(登壇者) ニフティ株式会社 会員システムグループ 第2開発チーム ニフティのスペシャリストを任命するN1!制度にて、スクラムエヴァンジェリストを担当しています。 2019年〜スクラムマスターとして、ニフティへのスクラム導入を開始。 8チーム、エンジニア約30名以上に対しスクラム導入を支援しています。 アドバンスド認定スクラムマスター(A-CSM)資格所持。 技術書典では「ニフティのスクラム」の編集・レイアウトと一部執筆をしました。 畑谷 啓志(登壇者) ニフティ株式会社 会員システムグループ 第2開発チーム ニフティのオプションサービス全般の開発リーダー、スクラムマスター兼任。スクラムチームの立ち上げから解散までの経験を持つ。認定スクラムマスター保持者。 小浦 由佳(登壇者) ニフティ株式会社 インフラシステムグループ 社内情報システムチーム ニフティの情シス担当者です。主にプラットフォーム、アプリケーションの社内システムを担当するチームのサブリーダー兼スクラムマスターで、技術書典では情シスのスクラムについて書きました。昨年末に認定スクラムマスターを取得しています。NIFTY Tech Talk、NIFTY Tech Dayを始め、社内外のイベントを企画したりもしているニフティのにぎやかしです。 高田 渉(ファシリテータ) ニフティ株式会社 基幹システムグループ 入会システムチーム ニフティの基幹業務システムのサブリーダー兼スクラムマスターを担当しています。 2021年12月に認定スクラムマスター資格を取得しました。 技術書典では、「ビジネス部門との二人三脚」のテーマを執筆。 ニフティグループでは一緒に働く仲間を募集中です 新卒採用、キャリア採用を実施しています。ぜひ リクルートサイト をご覧ください。 ニフティエンジニアが業務で学んだことやイベント情報を エンジニアブログ にて発信しています! ニフティエンジニアのTwitterアカウントを作りました NIFTY Tech Talkのことや、ニフティのエンジニアの活動を発信していきます。 Tweets by NIFTYDevelopers 「NIFTY Tech Day 2022」を開催します! 技術イベント「NIFTY Tech Day 2022」を11月22日(火)に開催します。 本イベントに事前参加登録いただき、事後アンケートに回答いただいた方の中から抽選で100名様に当社ポイントサービス「ニフティポイントクラブ」のポイント3,000ポイントをプレゼントします。 NIFTY Tech Day 2022 アンチハラスメントポリシー 私たちは下記のような事柄に関わらずすべての参加者にとって安全で歓迎されるような場を作ることに努めます。 社会的あるいは法的な性、性自認、性表現(外見の性)、性指向 年齢、障がい、容姿、体格 人種、民族、宗教(無宗教を含む) 技術の選択 そして下記のようなハラスメント行為をいかなる形であっても決して許容しません。 不適切な画像、動画、録音の再生(性的な画像など) 発表や他のイベントに対する妨害行為 これらに限らない性的嫌がらせ 登壇者、主催スタッフもこのポリシーの対象となります。 ハラスメント行為をやめるように指示された場合、直ちに従うことが求められます。ルールを守らない参加者は、主催者の判断により、退場処分や今後のイベントに聴講者、登壇者、スタッフとして関わることを禁止します。 もしハラスメントを受けていると感じたり、他の誰かがハラスメントされていることに気がついた場合、または他に何かお困りのことがあれば、すぐにご連絡ください。 ※本文章はKotlinFest Code of Conductとして公開された文章( https://github.com/KotlinFest/KotlinFest2018/blob/master/CODE-OF-CONDUCT.md )を元に派生しています。 ※本文章はCreative Commons Zero ライセンス( https://creativecommons.org/publicdomain/zero/1.0/ ) で公開されています。
アバター
概要 サービス開始から35年、ニフティは常に技術者やIT技術と共にありました。 高い好奇心で新しい技術トレンドをいち早く導入し、システムやサービスへの活用を常に模索しています。ニフティが創業時から変わらないのは、好奇心と挑戦し続けること。 この”NIFTY Tech Day”を通じて、私たちの今と未来をお伝えします。 詳細 公式サイト 日程 11月22日(火)13:00〜18:00 配信URL 準備次第掲載いたします 視聴環境 インターネット接続が可能なPC/スマートフォン 参加方法 connpass にて参加登録を受け付けています。 またイベント参加登録時にぜひTwitter投稿をお願いします! タイムテーブル 時間 セッション 13:00-13:20 キーノート 13:20-13:45 ゲストセッション 13:45-14:10 スクラムのハードルの越え方〜リリース数を1.5倍にするまで〜 14:10-14:35 複数プロダクトを抱えて行うスクラム開発のこれまでとこれから 14:40-15:05 クリーンアーキテクチャはこの3年間で私たちのチームに何をもたらしたのか 15:05-15:30 なぜニフティはLeSSを選んだのか。 @nifty MAX光における大規模スクラム開発体験談 15:35-16:00 若手エンジニアによるリモートワーク支援ツールの開発 ①AWS/GCPとSlackを駆使したオフィスの固定電話廃止への取り組み ②オンライン会議を盛り上げる!音声リアクションツール「もじこえ」の開発 16:00-16:25 安心・安全な@niftyメールサービスの裏側 16:30-16:55 セシール事業におけるモノリシックアーキテクチャとの向き合い方 16:55-17:20 CI/CDを導入して変わったモバイルアプリ開発体制 17:20-17:45 深層自然言語処理によるニュース記事要約の手法と実装 17:45-18:00 クロージング アンケート&懇親会ご案内 各セッションの詳細につきましては公式サイトをご覧ください。 ニフティグループでは一緒に働く仲間を募集中です ニフティ株式会社 新卒採用、キャリア採用を実施しています。ぜひ リクルートサイト をご覧ください。 ニフティエンジニアが業務で学んだことやイベント情報を エンジニアブログ にて発信しています! ニフティライフスタイル株式会社 想像以上を、みつけよう。 をコーポレートメッセージに、“一人ひとり”のライフスタイルを便利で豊かにするため、ニフティライフスタイルのエンジニアは日々開発をしています。 採用情報 や ニフティライフスタイル Tech Blog をご覧ください。 ニフティエンジニアのTwitterアカウントを作りました NIFTY Tech Talkのことや、ニフティのエンジニアの活動を発信していきます。 https://twitter.com/NIFTYDevelopers
アバター
はじめに はじめまして。ニフティ株式会社の森田です。前に社内の1dayハッカソンに参加して、WebSocketを利用して用意した音声を同時共有する簡易アプリを作りました。AWSの勉強も兼ねて色々なサイトやサンプルを参考にして、改めて似たような簡易アプリを個人的に作ってみたのでその紹介をしたいと思います。 WebSocketとはクライアント・サーバ間で対話的な通信ができる技術です。HTTPを使ってソケット通信ができるイメージです。スマート家電とかでも使われていると聞きます。今回は、WebSocketを使ってコネクション接続中の全てのクライアントに画像(正確には画像保存先のS3 URL)を共有するアプリをAWSで作成してみました。 構成 AWS構成図はこんな感じです。 AWS API Gateway API GatewayではWebSocket APIも作成できるため、サーバレスでの双方向通信が割と簡単に実現できます。REST APIではパスやメソッドごとにリソースを用意して、それにLambdaなどのサービスを統合します。WebSocket APIでは、接続時 ($connect) や切断時 ($disconnect) などのリソース(ルート)が最初から用意されています。最初から用意されているもの以外は別にルートを作成して、ルート選択式でそのルートに向けれらるようにします。それら各ルートに対してLambdaなどのサービスを統合するという基本的な流れは同じです。 API Gateway での WebSocket API について https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/apigateway-websocket-api-overview.html AWS Lambda WebSocket接続、切断、など色々なタイミングで、Lambdaに書かれたコードで実際に処理されます。そうするために関数ごとにAPI Gatewayとの統合が必要となります。WebSocket APIで言うと、$connectルートに統合したLambda関数は接続時に動作します。このようにルートごとにLambda関数と統合させます。 AWS DynamoDB API GatewayでWebSocketのサポートをしてくれるとはいえ、コネクションIDの管理は別でやらないといけません。今回はDynamoDBで1つテーブルを作成して、接続してきたコネクションのIDを雑に保存します。接続時にはIDをINSERTして、切断時にはIDを物理削除します。テーブルにIDがあればコネクションが接続中と判断します。 AWS S3 画像の保存用。今回、クライアント側で画像表示をするため、オブジェクトのURLをWebSocketでやり取りするようにします。S3で用意したバケットのバケットポリシーを変更して、外部からアクセスできるようにする必要もあります。今回は以下の3つの画像を用意しました。「いらすとや」の画像を使わせていただいています。 詳細 実際のデプロイについては最後にまとめています。Lambdaの処理内容の説明では、WebSocketを利用した時の処理の雰囲気が伝われば良いなと思います。 Lambda 接続時(onconnect) 処理内容 接続してきたコネクションのコネクションIDをDynamoDBに保存しています。Lambda関数のコードは以下です。 import os import logging import boto3 logger = logging.getLogger() logger.setLevel(logging.INFO) CONNECTION_TABLE = os.environ['CONNECTION_TABLE'] def lambda_handler(event, context): dynamodb = boto3.resource('dynamodb') connection_table = dynamodb.Table(CONNECTION_TABLE) connection_id = event.get('requestContext', {}).get('connectionId') try: # コネクションIDをDynamoDBに保存する connection_table.put_item(Item={'connectionId': connection_id}) logger.info(f'connected id: {connection_id}') except Exception as e: logger.error(e) return {'statusCode': 500, 'body': f'Failed to connect: {e}'} return {'statusCode': 200, 'body': 'Connected.'} Lambda 切断時(disconnect) 処理内容 切断したコネクションのコネクションIDをDynamoDBから物理削除します。Lambda関数のコードは以下です。 import os import logging import boto3 logger = logging.getLogger() logger.setLevel(logging.INFO) CONNECTION_TABLE = os.environ['CONNECTION_TABLE'] def lambda_handler(event, context): dynamodb = boto3.resource('dynamodb') connection_table = dynamodb.Table(CONNECTION_TABLE) connection_id = event.get('requestContext', {}).get('connectionId') try: # コネクションIDをDynamoDBから削除する connection_table.delete_item(Key={'connectionId': connection_id}) logger.info(f'disconnect id: {connection_id}') except Exception as e: logger.error(e) return {'statusCode': 500, 'body': f'Failed to disconnect: {e}'} return {'statusCode': 200, 'body': 'Disconnected.'} Lambda 画像共有(sendimage) 処理内容 11行目の IMAGE_S3_BUCKET_NAME には画像保存用に 作成したバケット名 を記述してください。 クライアントからリクエスト時に送られた選択画像名(selectedImage)から画像を保存しているS3 URLを構成して、コネクション接続中のクライアント(DynamoDBに保存されてる全てのコネクションID)にそのURLを送る処理をします。Lambda関数のコードは以下です。 import json import os import logging import boto3 logger = logging.getLogger() logger.setLevel(logging.INFO) CONNECTION_TABLE = os.environ['CONNECTION_TABLE'] IMAGE_S3_BUCKET_NAME = '' S3_ENDPOINT_URL = f'https://{IMAGE_S3_BUCKET_NAME}.s3.ap-northeast-1.amazonaws.com' def lambda_handler(event, context): dynamodb = boto3.resource('dynamodb') connection_table = dynamodb.Table(CONNECTION_TABLE) DOMAIN_NAME = event['requestContext']['domainName'] STAGE = event['requestContext']['stage'] WEBSOCKET_ENDPOINT_URL = f'https://{DOMAIN_NAME}/{STAGE}' try: # DynamoDBに存在するコネクションIDを全て取得 items = connection_table.scan(ProjectionExpression='connectionId').get('Items') except Exception as e: logger.error(e) selected_image = json.loads(event.get('body', '{}')).get('selectedImage') apigw_management = boto3.client('apigatewaymanagementapi', endpoint_url=WEBSOCKET_ENDPOINT_URL) for item in items: try: # 画像保存先のS3 URLを構成して、コネクションごとにデータとしてそのURLを送る image_s3 = f'{S3_ENDPOINT_URL}/{selected_image}.png' apigw_management.post_to_connection(ConnectionId=item['connectionId'], Data=image_s3) logger.info(f'ConnectionID: {item["connectionId"]}, image: {image_s3}') except Exception as e: logger.error(e) return {'statusCode': 500, 'body': e} return {'statusCode': 200, 'body': 'Data sent.'} デプロイ(AWS SAM) 先ほどの構成図のS3以外はAWS SAMで構築できるように準備しています。template.yamlは AWS公式のサンプル を参考にしています。画像保存用のS3は別で作成しておきます。lambda_function.pyは上で説明したLambdaの処理をそれぞれ記述して下さい。以下のディレクトリ構成でsam deployを実行すればデプロイされます。 コマンド sam deploy --template ./template.yaml --s3-bucket (sam用s3バケット名) --stack-name (CFnスタック名) ディレクトリ構成 / ├── src/ │ │ │ ├──── onconnect/ │ │ │ │ │ └─── lambda_function.py │ │ │ ├──── disconnect/ │ │ │ │ │ └─── lambda_function.py │ │ │ └──── sendimage/ │ │ │ └─── lambda_function.py │ └── template.yaml template.yaml のソースコード https://github.com/mrtmyix/image-change-websocket-test/blob/main/template.yaml クライアント 以下のような簡易な画像表示のHTMLを用意しました。ラジオボタンで選択して送信すると、選択した値を$sendimageに送ります。すると画像は選択したものに表示が変わって、同じく接続中セッションでも同じ画像が表示されるはずです。 HTMLファイルは以下です。 WebSocket URL をSAMデプロイして割り当てられたURLに 変更が必要 です。 クライアント側のHTMLソースコード https://github.com/mrtmyix/image-change-websocket-test/blob/main/index.html WebSocket URLはAPI Gatewayのコントロールパネルから確認ができます。 onopen()は接続が開かれた時に発火するメソッドで、画面上の文字列「未接続」を「接続中」に変更します。onmessage()は先ほど説明した、$sendimageのLambdaでS3 URLが送られてきた時に発火するメソッドで、現在の表示画像を送られてきたS3 URLの画像に変更します。 動作確認 上で説明したクライアントのHTMLファイルをローカルにダウンロードして、ブラウザで開けば動作を確認することができます。Webサーバなどに配置する必要がないため非常に楽です。 以下は4つのウィンドウで動作させた時の動画です。 おわりに 簡単なモノでも作ってみると勉強になりますね。WebSocketを使えばもっと色々な事ができそうだなと思いました。より深く仕組みを知りたければ、AWS公式サイトだったり、多くの方がブログ等で解説しているので見てみてください。 We are hiring! ニフティでは、さまざまなプロダクトへ挑戦するエンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトよりお気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering
アバター
はじめに 会員システムグループ SREチームの浅見( @rubihiko )です。 ニフティでは、全社的にサービスの品質向上に関わる取り組みを行っています。 トラブルに対して適切で素早い対処を行う訓練の一環として、アマゾンウェブサービスジャパン合同会社様(以下AWS様)協力のもと AWS GameDay を社内開催しました。 今回、私はニフティでは初となる社内開催に向けて調整をしたので、裏方としての観点でレポートしたいと思います。 以前参加した、通常の参加レポート記事はこちらです。 オンラインのAWS GameDayにニフティグループで参加してみました AWS GameDay とは AWS re:Invent などで開催される、実践的トレーニングで、システムを構築し安定稼働させることが目的となります。 スコア形式でランキングがあります。降りかかる課題や、障害に対してチームでいかに対応するか、本番さながらの対応力が求められます。 本来はグローバルなイベントで参加するものですが、今回はプライベート版ということで、ニフティグループ内での開催となりました。 ※ AWS GameDay 開催の経緯 最近の社内トレンドとして、サービスの品質維持・向上があります。 問題が起きた際の素早い復旧と関係者への連携など、対応力向上が求められていました。 また、ニフティではAWSを導入してから4年ほどが経ち、多くのサービスがAWS上で稼働している状態ですが、このトレーニング経験してもらい、AWSへの理解をより深めてほしいという思いもありました。 そんな中、AWS様に協力いただき、この両方を強化できるイベントの開催へと至ったというわけです。 調整 流れはこのような感じです。 何度かAWS様と詳細を詰める打ち合わせをさせて頂き、社内調整を進めていきました。 開催概要について資料の作成 各部署のマネージャーへの説明 開催日・参加者のスケジュール設定 参加者への概要説明・チーム分け ツール準備・設定 賞品の調整 当日運営 振り返り会 参加者集め 今回、ニフティのエンジニア全員に基本参加してほしかったので、 オプトアウト方式 を採用しました。 3ヶ月ほど前から予定を抑えて、無理な方は欠席していただく方針にしました。 参加実績ですが、125名ほどのメンバーに参加いただきました。(ニフティ、ニフティライフスタイルより) 100名超えのメンバーに集まってもらい感謝! この規模はプライベート開催の AWS GameDayではなかなか無いみたいです。 特別ルール チーム編成について、社内開催のため、グローバルとは異なるルールを一部採用していました。 既に過去のAWS GameDayに参加したメンバーもいたため、本来は1チーム最大4名となるのですが、 未経験者4名+経験者1名の5人でチーム を組んで行いました。 経験者には、 ネタバレ・手を動かすことを禁止 し、あくまでオブザーバ、チームリーダーのような役割で動いてもらいました。 事前の勉強会 AWS GameDayでは開催前にAWS様からAWSサービスについての勉強会を開いていただける場合もありますが、今回は事前勉強会は行いませんでした。 参加者からの要望はありましたが、定期的にAWS様より勉強会を開催していただいたり、 社内でもAWS勉強会をしていたため、各自で基本的なサービスの復習をしてもらうようお願いする程度にしていました。 ツールについて 社内開催をする場合、主催者(つまりニフティ)がツールを用意する必要があります。 AWS GameDayの環境はAWS様が用意していただけるため主催者が用意する必要はありません。 主催者側で用意する必須ツールは2つで、チャットツールと、ビデオツールです。 どちらも、AWS様からのサポート部隊のメンバーを招待できる必要があります。(当日サポート頂きありがとうございました。) 今回、 チャットツールは Slack ビデオツールは Google Meet を採用しました。 Gather を使おうかという話も出ましたが、業務で利用しているわけではないので、普段から使っているツールを使い、AWS GameDayそのものに集中してもらうため、この様になりました。 その他チーム内で使うツールについては制限は特にしていませんでした。ホワイトボードを使ったり、Notionを使ったりは自由です。 賞品について 上位者にはなんとAWS様から賞品がでました!ありがとうございます!! また、社内開催ということ、ニフティからも副賞で ニフティポイント をプレゼントしました。 ※ ニフティポイント 当日の様子 ワイワイしていました。 今回開催した、プライベートのAWS GameDayは場合によっては一定の参加人数が求められており、それを満たせるか不安な部分がありました。 しかしながら、ニフティ、ニフティライフスタイルから沢山のメンバーが集まり、無事開催できてホッとしています。 結果発表の様子 振り返り会&アンケート 終了後アンケートと振り返り会を実施しました。 振り返りは非常に重要です。自分たちが解決できなかった部分の復習にもなりますし、新たなアイデアをインプットできる場でもあります。 人数が多かったので、以下のように行いました。 各チーム単位で振り返り・資料をまとめる(事前準備) 全チーム集まりグループに分かれて発表を行う(振り返り会当日) 全員の発表資料はNotionにまとめておく 振り返り会当日は1時間しか取れなかったので、かなり慌ただしくなってしまいましたが、各チームとも色々と学びを多く語ってくれて良き振り返り会となりました。 振り返り会はネタバレありのため、当日参加していないメンバーは参加できません。そこが辛いところではありますが、しかたありません。 社内向けアンケートについても一部共有したいと思います。 総合的な満足度 AWSの知識が深まったか AWSをもっと学びたいと思ったか 障害対応などのトラブル時に役立つ内容だったか 課題だと気が付きがあったか 概ね良い感じで、今回の経験を業務に活かすきっかけとなるアイデアを掴んだ人も多くいた印象です。 また、AWSをより深く学びたいと思う人も多くいました、社内勉強会の需要も高まっているような気がします。 感謝 運営として関わってくれたメンバーに感謝します。今後も社内勉強会やイベント盛り上げていきましょう! 参加していただいた皆様もありがとうございました。この経験を業務で活かしてもらえれば最高です! そして、AWS様には改めて感謝いたします。このような素晴らしいイベントを提供いただきありがとうございました。環境の準備や調整、当日のサポートなどお世話になりました。 最後に 多くの人に参加してもらえるイベントを開催できてよかったのと、無事終わってよかったと思っています。 このような経験がエンジニアとしてレベルアップに繋がると思っています。 今後も社内イベントは開催していきたいと思っているので、よい経験を積むことができました。 We are hiring! ニフティでは、さまざまなプロダクトへ挑戦するエンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトよりお気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering
アバター