TECH PLAY

ニフティ株式会社

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

487

この記事は、 ニフティグループ Advent Calendar 2023 5日目の記事です。 はじめに こんにちは、最近はひょんなことから見つけた古のWebページに衝撃を受けている宮本です。jQueryが生まれる前の時代のページともなると流石に趣が違いますね。 さて、今回は Astro の機能のひとつについてご紹介したいと思います。 Astroって? AstroはJavaScriptを使ったモダンWebフレームワークの一つです。JS製のWebフレームワークというとNext.jsが有名ですが、Astroはコンテンツが多い静的サイトに特化している点でかなり特徴的です。どのくらい特徴的かというと、Astro単体だとReactのstateにあたる機能が一切存在せず、クライアント側のDOM動作はほぼ素のJavaScriptやjQueryになるくらい特徴的です。 まだAstroを触ったことがない方は、ぜひ公式サイトをご覧ください。 https://docs.astro.build/ja/concepts/why-astro/ 同じレイアウトで異なるJSとCSSを読み込みたい 前述の通り、純粋なAstroの場合は細かいDOM操作はほぼ素のJavaScriptやjQuery頼りになります。拡張としてReactやSvelteのコンポーネントを部分的に導入することもできますが、単純な操作の場合は意外と昔ながらの方法でも十分に感じます。 さて、そうなると出てくるのが「レイアウトが同じだけど、このページだけ〜するから特定のJSライブラリをscriptタグで読み込みたい」という悩みです。もちろんscriptタグで読み込まずとも、Astroなのでnpmやyarnのようなパッケージ管理ソフト経由でjQueryなどをインストールし、内部でまとめてビルドしてしまうこともできます。こちらの方がだいぶスマートに感じます。 しかし、場合によっては昔ながらのscriptタグでの読み込みがしたい場合もあります。あるんです。たとえば、ただのhtmlだった既存のページを大量にAstro化する場合など……。また、この場合はJSが元々外部ファイルとして作成されている場合もあります。 CSSも同様に、時と場合によっては同じレイアウトで異なるCSSを読み込みたくなります。Astroにはデフォルトで簡単にスタイルをコンポーネント内で閉じる機能があるので、今後はそうした機能を使っていきたいですね。 複数のCSSやJSを読み込む場合には、読み込み順序を間違えると正常なページ表示にならない可能性もあります。これもCSSやJSファイル自体を修正しようとすると、程度にもよりますがなかなか大変だと思います。可能であれば、読み込み順を変えて対応してしまいたいです。 そうなってくるとレイアウトを新しく作る必要がありそうですが、ほとんど一緒なのに読み込むJS、CSSが違うだけでわざわざ新しいレイアウトは作りたくありません。特にそれを使うのが1~2ページだと、なんとも残念な気分になります。ということで、レイアウトは増やすことなく自由にJSとCSSファイルを読み込めるようにします。 ページごとにJSとCSSを読み込めるようにする 同じレイアウトでも自由に読み込むJS/CSSを制御できるようにしたレイアウトの例がこちらです。 --- import Header from "./Header.astro" import Footer from "./Footer.astro" const { title } = Astro.props --- <html> <head> <meta charset="utf-8" /> <link rel="icon" type="image/svg+xml" href="/favicon.svg" /> <meta name="viewport" content="width=device-width" /> <title>{title}</title> <slot name="OverrideHead"> <link rel="stylesheet" type="text/css" href="/css/base.css" /> <slot name="AppendHead"> </slot> </head> <body> <Header /> <slot /> <Footer /> <slot name="OverrideFoot"> <script is:inline src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <slot name="AppendFoot"> </slot> </body> </html> 名無しのslotはページ側の内容を表示するためですが、それ以外の名前付きslotの役割は以下のようになっています。 AppendHead slot: head要素の最下部に自由に読み込めるタグを追加できるslot AppendFoot slot: body要素の最下部に自由に読み込めるタグを追加できるslot OverrideHead slot: head要素内で読み込むCSS/JSを全て上書きするslot OverrideFoot slot: body要素の最下部で読み込むタグを完全に上書きするslot AppendHead/Footではほぼ基本的なslotタグの使い方です。OverrideHead/Footでは、 slotのフォールバックコンテンツ機能 を使っています。あらかじめ用意したslotに挿入がない場合はLayout側で指定した要素をそのまま表示、Layoutの呼び出し側で指定があればそちらを表示してくれます。 使用例 --- import Layout from "@layout/Layout.astro" --- <Layout title="ページ内で読み込むJS/CSSを追加する"> <Fragment slot="AppendHead"> {/* Layoutで指定した/css/base.cssに加えて以下を読み込む */} <link rel="stylesheet" type="text/css" href="/css/content.css" /> </Fragment> <p>ページ内で読み込むJS/CSSを追加する例</p> <Fragment slot="AppendFoot"> {/* Layoutで指定したjQueryに加えて次のファイルを読み込む */} <script is:inline src="/js/hoge.js"></script> </Fragment> </Layout> --- import Layout from "@layout/Layout.astro" --- <Layout title="ページ内で読み込むJS/CSSを全て制御する"> <Fragment slot="OverrideHead"> {/* Layoutのcssの読み込み順序を完全に上書き */} <link rel="stylesheet" type="text/css" href="/css/content.css" /> <link rel="stylesheet" type="text/css" href="/css/base.css" /> </Fragment> <p>ページ内で読み込むJS/CSSを全て制御する例</p> <Fragment slot="OverrideFoot"> <script is:inline src="/js/hoge.js"></script> {/* ここに追加しない限りjQueryの読み込みは行われない */} </Fragment> </Layout> さいごに 今回はAstroのLayout機能で柔軟にCSS/JSの読み込みを制御する方法を紹介させていただきました。一からAstroを使って新規にWebサイトを作成する場合はあまり必要ないかもしれませんが、既存のサイトをAstroに書き換える際には役に立つかもしれません。 明日は、@yukiex さんの EMとしてISUCONに参加するようになって です。 お楽しみに! 参考 Getting Started Astro Documentation We are hiring! ニフティでは、さまざまなプロダクトへ挑戦するエンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトよりお気軽にご連絡ください! ニフティ株式会社採用情報 カジュアル面談も受け付けています! カジュアル面談 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering ニフティ株式会社 – connpass
アバター
インナーソースコミュニティであるInnerSource Commons JapanのMeetupに当社エンジニアが登壇いたします。 イベントの詳細、参加につきましては下記ページを参照ください。 InnerSource Commons #11 – connpass 当社では、今回登壇する基幹システムグループの芦川、小松を中心にインナーソースの推進活動をしております。活動の様子につきましては、 インナーソースを導入してみた その① お試し導入編 をぜひご一読ください。 また、芦川がチームリーダーをしているサービスインフラチームの紹介記事もあります。インナーソースのような活動を尊重するチーム文化が窺い知ることができますので、こちらも合わせてお読みいただけると幸いです。 基幹システムグループ サービスインフラチームの紹介です
アバター
この記事は、 ニフティグループ Advent Calendar 2023 3日目の記事です。 はじめに こんにちは。ニフティ株式会社の並木です。 今回は、Lambdaで「AWS Secrets Manager」を使う方法をご紹介いたします。 AWS Secrets Managerとは AWSのサービスの一つで、APIキーなどの他人に知られては困る情報を管理してくれます。 LambdaでAPIを叩くにあたってAPIキーの設定が必要になったのですが、APIキーをハードコーディングせずに管理する方法はないかと考えていた時に、Secrets Managerの存在を知りました。 導入までの手順は大きく分けて以下の3つになります。 ①シークレットを作成する ②シークレットにアクセスするためのリソースポリシーを追加する ③Lambdaでシークレットを使う ①シークレットを作成する まずは、APIキーなどの隠したい情報をSecrets Managerに保存していきます。 公式ドキュメントは こちら です。 今回は、APIキーを保存するにあたり、以下の値を設定しました。 ・シークレットのタイプ:「その他のシークレットのタイプ」を選択。 ・キー:任意の名前。後にLambdaで情報を取り出す時にここで付けた名前を使います。 ・値:隠したい情報。今回の場合はAPIキーを設定します。 ・暗号化キー:「aws/secretsmanager」を選択。 「次」を押下すると、以下のページが出てきます。 ・シークレットの名前:任意の名前。後にLambdaで情報を取り出す時にここで付けた名前を使います。 ・説明:シークレットの説明を書いておくことができます。日本語も使えました。 ・リソースのアクセス許可:このタイミングでも設定可能ですが、今回は「②シークレットにアクセスするためのリソースポリシーを追加する」で設定するので飛ばします。 さらに「次」を押下すると、シークレットの自動ローテーションの設定ページが出てきます。 今回は設定せずにそのまま「次へ」を押下します。(後から設定することも可能です) これでシークレットの作成は完了です! ②シークレットにアクセスするためのリソースポリシーを追加する ①で作成したシークレットをLambdaで呼び出すためには、リソースポリシーの追加が必要になります。 そのためには「Lambdaの実行ロールのARN」が必要となるので、まずはそちらを取得していきます。 Lambdaの管理画面のアクセス権限を表示します。 ロール名のリンクを押下すると、Lambdaの実行ロールの管理画面に飛びます。 Lambdaの実行ロールの管理画面に記載されているARNをメモしておきます。 これで「Lambdaの実行ロールのARN」が取得できました! 次に、①で作成したシークレットの管理画面を表示し、「許可を編集」を押下します。 公式ドキュメント に記載されている以下の「mypolicy.json」の定義をコピーして使います。 ARN部分のみ、先ほど取得してきた「Lambdaの実行ロールのARN」に置き換えて保存します。 { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::123456789012:role/MyRole" }, "Action": "secretsmanager:GetSecretValue", "Resource": "*" } ] } これでリソースポリシーの追加は完了です! ③Lambdaでシークレットを使う Lambdaでシークレットを使うには、レイヤーの追加が必要になります。 「AWS Parameters and Secrets Lambda Extension」を選択し、「追加」を押下します。 シークレットを呼び出すためのコードをLambdaに書いていきます。 ①で作成したシークレットの管理画面にサンプルコードが表示されているので、それを活用します。 言語がいくつか選択できますが、今回はPythonのサンプルコードを使用します。 サンプルコードをベースに、コードを書いてみました。 サンプルコードには「secret = get_secret_value_response[‘SecretString’]」までしか書かれていなかったため、その後どのようにして値を取り出せばよいのか分からず、個人的にはここで少し詰まりました。 「ast.literal_eval()」を使うと良いようです。 import ast import boto3 from botocore.exceptions import ClientError def get_secret(): secret_name = "①で設定したシークレットの名前" region_name = "リージョン名" # Create a Secrets Manager client session = boto3.session.Session() client = session.client( service_name='secretsmanager', region_name=region_name ) try: get_secret_value_response = client.get_secret_value( SecretId=secret_name ) except ClientError as e: # For a list of exceptions thrown, see # https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_GetSecretValue.html raise e # Decrypts secret using the associated KMS key. secret = get_secret_value_response['SecretString'] # Your code goes here. secret_list = ast.literal_eval(secret) value = secret_list['①で設定したキー'] おわりに 他人に知られては困る情報の管理や呼び出しが簡単にできて便利だと感じました。 ハードコーディングを防ぐためには欠かせないサービスだと思いますので、ぜひ利用してみてください。 明日は、 nahiro_tus さんの「Notionの作図機能についてまとめる」です。 お楽しみに! ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
アバター
この記事は、 ニフティグループ Advent Calendar 2023 3日目の記事です。 はじめに こんにちは。会員システムグループでエンジニアをしている山田です。 私の担当しているプロダクトではシステム刷新を進めており、20年来のレガシーなJavaシステムからNode.js(Next.js)を利用したフロントエンドシステムへのフルリプレースを行いました。その後の運用体制を整えていく中で、GitHub Dependabotを使ったアップデート運用を導入したので、この紹介になります。 これまでの課題 今までミドルウェアや依存パッケージなどのアップデート対応には明確な指針がなく、だいたい以下のどちらかの運用になっていました。 なんらかの問題(脆弱性など)が起こるまで放置 担当者が気づいた時に更新 前者は変更の少ない言語環境であれば機能する可能性がありますが、Node.jsのような依存パッケージ数も変更頻度も多い環境では大きなリスクを生みます。いざ変更が必要になった時には変更差分が把握しきれない量になっていたり、依存関係の都合で複数のアップデートを同時に行う必要が生じることも頻繁にあります。しかもこれが突発的に発生することで、必要な開発の妨げになる可能性を孕んでいました。 一方で後者は属人化しており、担当者の知識や意識に左右されます。担当者の異動などで継続不能になる可能性を孕んでいます。 以上から、継続的にアップデートを続ける体制を作っていく必要がありました。 Dependabotとは DependabotはGitHubの機能として利用できるサービスであり、プロジェクト内の依存関係に含まれる脆弱性やアップデートの有無を検知して自動でプルリクエストを作成したり、アラートを出すことができるサービスです。 類似のサービスとして Renovate がありますが、RenovateはGitHub AppsのインストールやGitHub Organization管理者による設定が一部必要と、導入にややハードルがあります。 一方でDependabotはGitHub組み込みの機能なので、リポジトリごとの設定で有効化することで使い始めることが可能です。 リポジトリ設定の「Code security and analysis」から有効化できます 設定 Dependabotの設定 Dependabotの設定は . github / dependabot . yml に記述します。 アップデート可能なパッケージがある場合、Dependabotはこの設定を元に自動的でプルリクエストを作成します。 version: 2 updates: - package-ecosystem: "npm" directory: "frontend" schedule: interval: "weekly" groups: nextjs: patterns: - next - react - react-dom - "@types/react" - "@types/react-dom" - eslint-config-next dependencies: dependency-type: "production" devDependencies: dependency-type: "development" ignore: - dependency-name: "*" update-types: ["version-update:semver-patch"] 詳細な文法は ドキュメント を参照していただくとして、大まかに以下の設定をしています。 package-ecosystem npmなどパッケージ種別を指定します。指定値としてはnpmでもyarnでも「npm」の指定になります。 実際にDependabotがどのパッケージマネージャに対応しているかは、ドキュメントで確認が必要です。例えば「pip」はpipenvとpoetryに対応していますが、ryeには対応していません。 言語ごとのパッケージだけでなく、Dockerfileのベースイメージなどにも対応しています。 directory チェック対象ディレクトリ指定です。モノレポ構成などの場合に、個別ディレクトリを指定することで分けて管理することができるようになります。 schedule チェック間隔です。daily、weeklyなどが指定できます。 groups グルーピングの設定です。2023年になってから 追加された 機能になります。 デフォルトでは、パッケージ1つ1つのアップデートに対してプルリクエストが作成されてしまうため、多すぎて管理しきれなくなります。groupsを設定することにより、指定したパターンに一致するパッケージアップデートを1つのプルリクエストにまとめることができます。 私のプロジェクトでは Next.js関連 TypeScript関連 その他dependencies その他devDependencies くらいのグループに分割して管理するようにしています。 ignore 除外条件です。特定パッケージやバージョンを除外することができます。 パッチバージョンまですべて追う必要はないと考えているため、パッチバージョンの除外指定を入れています。 以上の設定の他にも、プルリクエストコメントやラベルのカスタマイズなどが可能です。 設定によっては自動マージも可能ですが、リスクを鑑みて適用していません。 他のGitHub Actionsの修正 Dependabotに限った話ではないのですが、botアカウントは権限が絞られているため、GitHub Actionsのワークフローをbot権限で実行してしまうと失敗する場合があります。 私のプロジェクトではプルリクエスト起票者をAssigneeに追加するワークフローがあったため、botアカウントを除外するような対応を行いました。 jobs: add-info: name: Auto Assign runs-on: ubuntu-latest if: ${{ !contains(github.actor, '[bot]') }} # [bot]を含む場合は除外 steps: ... 運用 アップデート対応 Dependabotのチェック頻度をweeklyに設定し、毎週のミーティングで自動作成されたプルリクエストを全て確認し、アップデート可否の判断をするようにしました。 動作そのものはGitHub Actionsで組まれたCIで担保していますが、隠れたリスクの発見や業界情勢の把握のため、全てのリリースノートを確認するようにしています。 Gihub管理のパッケージならコミットメッセージにリリースノートが載るので、確認は楽です 除外対応 CIを用意しているとはいえ、Next.jsのメジャーバージョンアップなどの影響の大きい変更はリスクがあるため、一定期間を置いてから適用するようにしています。 この間、そのままだと同一グループの他のパッケージもアップデートができなくなってしまいます。このような場合はプルリクエストコメントに @dependabot ignore <パッケージ> とコマンドを打つと、アップデート対象から一時的に除外できます。アップデートできるようになったら @dependabot unignore <パッケージ名> で解除します。 運用してみて 良かったところ 目論見通り、属人化していたアップデートを誰でも行うことができるようになりました。 またリリースノートの確認を行うようになったことでコミュニティの動きをチームメンバー全員が把握できるようになり、知識向上にもつながるようになりました。 微妙なところ 一方で微妙だな、と思うポイントもいくつかあります。 コンフリクト時の挙動 複数グループにアップデートがある場合、1つのプルリクエストをマージするとロックファイルがコンフリクトを起こすため、他のプルリクエストは修正が必要になります。 このような場合、Dependabotは rebaseしてforce-push 既存プルリクエストをcloseし、新規に作成 のどちらかの対応を行うようなのですが、どちらになるかが現状予測できません。無駄なプルリクエストを減らすため、できれば前者に寄せたいところです。 またいずれの場合も数分待たされるので、グループ数が多いとすべて解消するのに時間がかかります。CIの実行時間もかかるので、グループ数はなるべく少なくした方が良いでしょう。 複数グループに同じパッケージが出現する グループについて、ドキュメントには以下の記載があります。 Dependabot creates groups in the order they appear in your dependabot.yml file. If a dependency update could belong to more than one group, it is only assigned to the first group it matches with. つまり上から順に評価され、複数グループに同じパッケージがマッチすることはないと読めます。 これはスケジュール通りの実行時には確かに機能するのですが、コンフリクト解消時にはうまく機能してくれないようで、rebaseされたタイミングで複数グループに同じパッケージが存在する状態になってしまうことがありました。 グループの設定で exclude - patterns を設定すれば確実に除外ができるのですが、全てのグループに除外設定を書く必要が出てくるため非常に煩雑です。 複数種にまたがるアップデートを同時に行えない Node.jsのメジャーバージョンを上げたい場合、私のプロジェクトでは 本番用のDockerfileのベースイメージ 開発用のDockerfileのベースイメージ GitHub Actions上でのsetup-node を揃えてバージョンアップする必要があります。しかしDependabotのグループ設定は複数ファイルにまたがる更新に対応しておらず、複数のプルリクエストに分かれてしまいます。 メジャーバージョンアップ自体は頻繁に発生するものではないので、個別で手動対応するようにしています。 まとめ GitHub Dependabotを利用したアップデートプルリクエストの自動作成と、その運用についてご紹介しました。 Node.jsに限らず最近の言語ではOSSパッケージへの依存度が高く、そのアップデート対応は煩雑になりがちです。GitHubを利用されている方は、ぜひ活用して見ることをお勧めします。 ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
アバター
この記事は、 ニフティグループ Advent Calendar 2023 2日目の記事です。 はじめに こんにちは!中途入社1年目の福島です。 私は日々の業務に加えて、世間の皆様にニフティについてより深く知っていただくために、ブログ運用チームの一員として活動しています。 先日、当ブログが念願の10,000MAUを達成しました! この記事では、10,000MAUを達成するまでに行ったブログ運用チームの取り組みをご紹介します。 他社でブログを運用している方やこれから運用したいけど何をすればいいかわからない方の参考になれば嬉しいです。 活動内容 ブログ関連作業効率化 X(旧Twitter)投稿自動化 これまで手動で行っていたXへのブログ更新ポストを自動化することで、投稿文のミスやダブルチェックの手間を削減し、スムーズに記事をポストできるようにしていました。 ブログを更新しました! 今回の記事は「Zapierを使ってRSSフィードの更新をトリガーにしたTwitterへの自動投稿機能を作ってみた」です。 https://t.co/jODL6wIIbS #nifty_dev #RSS #Twitter #Zapier #自動化 — NIFTY Developers (@NIFTYDevelopers) July 26, 2023 自動でポストされた投稿 これにより簡単に記事を共有しより多くの方に閲覧いただける環境が整いました! ! が、残念なことに、開発後、ZapierのX(旧Twitter)関連の機能がサービス終了となり、現在は使用できなくなってしまいました… 現在別の手段で自動ポストできるように誠意修正中です!! ※当時の実現方法はこちら Zapierを使ってRSSフィードの更新をトリガーにしたTwitterへの自動投稿機能を作ってみた – NIFTY engineering 社内ルールの整備 執筆から公開までの手順の整備 執筆~公開までの手順がわかりにくいとストレスになります。 皆さんに楽しく、スムーズにブログ執筆行っていただくために、明確な執筆手順を整備しました。 問い合わせ窓口もSlackに開設しており、連絡があった内容はTIPSとして随意追記しています。 記事のテンプレートを準備 「ネタはある!けどブログ記事の書き方がわからない….」という社員のために、テンプレートを準備して書きやすくしました。 WordPressの再利用ブロックからテンプレートを呼び出し、穴埋めするだけで簡単にブログ記事が書けちゃいます! 記事数を増やす試み 執筆計画の作成 エンジニア全員に年度初めに執筆スケジュールを記載してもらうことで、計画的にブログを書けるように調整しています。 アドベントカレンダー(執筆イベント) ニフティではイベントとして毎年アドベントカレンダーを実施しており、アウトプットの恒例行事となっています。 ありがたいことに毎年多くのメンバーが参加し、記事を執筆していただけています。 2023年度はまさに現在実施中です!!良い記事がたくさん投稿されるのでぜひチェックしてみてください! https://qiita.com/advent-calendar/2023/nifty ネタ切れ対策 社内のナレッジからのブログネタの選定・依頼 社内のナレッジを活用し、ブログに転用可能な情報を見つけ出して執筆依頼することで、多岐にわたるテーマをカバーできるようにしています。 今年度の新卒とトレーナーにブログ執筆依頼 「新人の方ニフティで学んだことをアウトプットしてもらう」、「ブログ記事に新しい視点や経験を取り入れる」という2つの目的を持ち、新人とトレーナーに積極的に執筆依頼を行っています。 最近の記事だとこちらの記事が今年度の新卒の方が書いた記事です。 非常に優秀な方々で面白い記事なのでぜひ読んでみてください!! 新卒1年目の働き方改革: AWS+Boltで作成したSlackアプリによる出退勤タスク自動化術 – NIFTY engineering LambdaでS3に置かれたファイルの署名付きURLをSlackに通知してみた – NIFTY engineering ブログネタ集めを楽にする(リアク字チャンネラー ) 社内でのアイデア共有を活発化させ、ブログネタの収集を楽にするためにニフティで活発に利用されているSlackで誰かが「ブログ書いて」の絵文字のリアクションを押すと自動的にブログネタが流れてくるチャンネルを作成しました。 執筆者のフィードバックを活用したコンテンツ改善 アンケート(半期に1回) 執筆者であるエンジニア全員に対して半期に1回、アンケートを行っています。 執筆者の要望を把握し、それに基づいて企画を立案、運用を改善することで、執筆者に気持ちよく記事を書いてもらうことを目指しています。 これらの活動により、読者の皆様への価値提供の向上はもちろん、目標だった10,000MAU達成にも繋がりました。 また、今後も良いブログにしていくため、以下の活動を計画しています。 WordPressの使い勝手を向上させるためのカスタマイズ より一層のパフォーマンス向上を目指し、別のプラットフォームへの移行検討 記事の作成から公開に至るフローの簡略化 これからも改善を重ね、皆様に愛されるブログへと成長させていきます。 どうぞよろしくお願いいたします! ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する 明日は、並木さんによる「LambdaでSecretsManagerを使ってみた」です。 お楽しみに!
アバター
この記事は、 ニフティグループ Advent Calendar 2023 1日目の記事です。 はじめに こんにちは。ニフティ株式会社の会員システムグループの上原です。 2023年ニフティグループAdvent Calender1日目に滑り込みの投稿です。 今回は、ISUCONという競技イベントにニフティ社員でチームを組んで参戦したので、そのご報告になります! ISUCONとは? ISUCONとは制限時間8時間でお題となるwebアプリケーションを限界まで高速化して性能を競いあうコンテストです。 今年は11/25(土) 10:00-18:00に開催されました。 LINEヤフー株式会社が主催しており、優勝すると100万円を獲得できます。 ※ 「ISUCON」は、LINEヤフー株式会社の商標または登録商標です。 チーム「nisucon-bu」のメンバー なおき(私) アプリケーション・データベースのチューニング・コード内部調査 初参加 yukiex 環境構築・インフラ たくさん参加している mito アプリケーション・データベースのチューニング・マニュアル読み込み 2回目 お題 ライブ配信サービス「ISUPipe」の高速化 お題に対する所感 こんいす〜 今回のお題は動画配信サービスなのでドメイン知識の理解は辛くない スコアはベンチマーカーが配信中に投げ銭をして、どれくらい売上を獲得できたかで決まる いつもはエンドポイントの実行結果で決まるが、今回は売上でスコアが決まるので面白いなと思った 動画配信をチューニングするかと思って驚いたが、動画配信はチューニング対象外 運営が用意したCloudFrontから動画配信されるので、どう頑張ってもチューニングできない 最悪ブラウザから動画を見れなくても構わない(らしい) リバースプロキシ(nginx) + go(アプリケーション) + データベース(mysql) + DNS(PowerDNS)の構成 DNS以外はいつも通りの構成 DNSが選手のサーバー上にあり、ベンチマーク実行中はDNS水責め攻撃が行われる いつも通りアプリケーションやデータベースを頑張ってチューニングしても、DNS水責め攻撃を対策しないとスコアが上がらない DNS水責め攻撃とは、適当なサブドメインをくっつけてどんどんDNSサーバーに問い合わせることで、DNSをダウンさせる攻撃 例えばnifty.comであれば、hogehoge.nifty.comのIPアドレスは?、fugafuga.nifty.comのIPアドレスは?、puipui.nifty.comのIPアドレスは?… とランダムにサブドメインをつけてDNSに問い合わせていく DNSサーバーに対するDDoS攻撃 これまでのISUCONで出てこなかった内容なので、全然対策できていない この辺りの知識が必要らしいが、全く知らなかった… DNS権威サーバのクラウドサービス向けに行われた攻撃および対策 〜後編〜 | さくらのナレッジ DBやめてみた / DNS water torture attack and countermeasures やったこと アイコンの静的配信、トランザクション削除(mito) PowerDNS Recursor による水責め対策(TTLを減らす、タイプANYリクエストを抑制)(yukiex) 統計(LivestreamStatistics, UserStatistics)のN+1を改善(なおき) 【反映せず】icon のハッシュ値をDBに保持(yukiex) TAG処理のN+1をIN句に変更、インデックス追加(mito) NGワード除去処理を修正、インデックス追加(mito) 【反映せず】DNS 用 records テーブルに対するindex追加(なおき) Livestream のキャッシュ機構を追加(なおき、mito) サーバの分散化(yukiex) その他(themes、reservation_slots、livestreams)のインデックス追加(mito) https://github.com/niftyisucon/isucon13 タイムライン 環境構築、初回ベンチマーク(46分) 10:00 ISUCONのポータルサイトに置いてあるCloudFormationのYAMLファイルをダウンロードした後、自分のAWS環境にサーバー構築を行う(なおき) 10:00 マニュアルなどを読む(yukiex, mito) 10:08 サーバー構築が終わったので、サーバー内のファイルをGit管理にしたり必要なコマンドを導入する(yukiex) 10:08 引き続きマニュアルを読む(mito) 10:08 データベースのスキーマや実行しているプロセスの調査(なおき) 10:12 初回ベンチマークを実行。3757点。 10:37 alpとpt-query-digestによるログ解析が完了。 10:46 環境構築完了 チューニング 午前 10:39 スロークエリに対してインデックスを貼る(mito) 11:00 DNS水責め攻撃の対策のため調査(yukiex) 11:35 PowerDNS Recursor の導入(yukiex) 設定方法とかもChatGPTに聞いたらしいです 11:00 – 12:00 ブラウザでアプリケーションを触りながらAPIのエンドポイントをまとめていく(なおき) 午後 12:00 ユーザーのiconをDBからはがしディスクに保存、nginxで配信するようにする(mito) https://github.com/niftyisucon/isucon13/pull/12 13:15 nginxの設定に手こずり、苦戦したが完了(mito) 6905点 12:00 配信のstatisticsエンドポイントで使用しているクエリを変更(なおき) https://github.com/niftyisucon/isucon13/pull/16 https://github.com/niftyisucon/isucon13/pull/19 配信の順位や投げ銭の最大値などを返すエンドポイント N+1問題でパフォーマンスが落ちていた キャッシュさせて回避するか、クエリを変更するかで迷った挙句、yukiexさんからアドバイスを受けクエリを変更することにした 13:30 ランキングの順位がバグりまくって大変だったが、ベンチマークの指摘とデータベースの中身をにらめっこしつつ直す(なおき) 6422点 13:30-14:15 トラブルでベンチマークがうまく動かなくなり、何もできなくなる 14:15 iconのハッシュ値をDBに追加(yukiex) マージせず 15:12 タグのN+1問題を解消、インデックス追加(mito) https://github.com/niftyisucon/isucon13/pull/21 7776点 15:20 ユーザーのstatisticsエンドポイントのN+1問題をクエリを変更して解消(なおき) https://github.com/niftyisucon/isucon13/pull/24 ユーザーの順位などを返すエンドポイント こちらもランキングの順位がバグりまくったが、ベンチマークの指摘とデータベースの中身をにらめっこしつつ直す 8703点 15:30 DNSサーバーのisudnsデータベースのrecordsに対しインデックスを追加したが、スコアが下がったため巻き戻し(なおき) インデックスを貼った結果、INSERT UPDATEの時間が悪化したのだと思われる 9972点 15:35 icon取得にかかっている不要なトランザクションを除去(mito) https://github.com/niftyisucon/isucon13/pull/27 10667点 16:15- サーバーを1台から複数台構成に変更するための準備(yukiex) サーバー3台のうち2台を複数台構成に変更するために使用 残り1台でアプリケーション・データベースのチューニングを試してみる 15:45 ライブ配信で投稿できるコメントのNGワードにインデックスを追加(mito) https://github.com/niftyisucon/isucon13/pull/29 11709点 16:15 ライブ配信で投稿できるコメントのNGワード削除処理を修正(mito) https://github.com/niftyisucon/isucon13/pull/34 NGワードを追加すると過去のコメントも含めて削除する処理になっていた N+1問題の解消 11795点 16:30 – 17:15 配信情報を取得する関数にキャッシュを導入(なおき) https://github.com/niftyisucon/isucon13/pull/35 自分の書いた章を読みながら実装( NIFTY Tech Book #1:ニフティ執筆部 ) 21743点 17:30 インデックス追加(mito) https://github.com/niftyisucon/isucon13/pull/40 残り時間30分でやったチューニング 16449点 終盤(複数台構成に変更、ログ出力停止、再起動試験対策) 17:15- 2台構成に変更(yukiex) isu1: nginx, app(Go), PowerDNS Recursor, PowerDNS, mysql (PowerDNSのバックエンド) isu2: mysql (app) 17:45 ログ出力停止(mito) https://github.com/niftyisucon/isucon13/pull/41 https://github.com/niftyisucon/isucon13/pull/42 17:49 ベンチマーク実行 25,674点 18:00 競技終了 結果 661チーム中81位(上位12.04 %) スコア 25,674 自チームのスコア変動をグラフ化してみると17:00からの伸びがすごい 他のチームの状況 1位のスコアがすごい(468,006点) 2位のスコアと2倍近く差をつけている うまくできたこと 1時間以内に環境構築・準備を終えることができた 練習ではごたつくこともあったが、事前準備でなんとかなった 練習でやったことは確実にこなすことができた N+1問題の解決やキャッシュを入れ込むことができた 自分の書いた本のおかげ ニフティ社員で執筆した技術総合誌「NIFTY Tech Book #1」のダウンロードよろしくお願いします(宣伝) NIFTY Tech Book #1:ニフティ執筆部 ChatGPTやCopilotを駆使して素早く問題の解決ができた AIすごい 役割分担がうまくいった 自分はゴリゴリコードを直していきつつ、マニュアルを読み込んで仕様を完全理解していたmitoさんに軌道修正してもらいながら、yukiexさんがインフラに手を入れる 17:00からグイグイ点数が上がった 反省点 個人練を頑張りたい 低レベルな技術を理解することは大事 DNS様 まだまだやりたいことはあったが、時間が足りなかった。 無駄な時間を削ぎ落としたい プリペアードステートメントを切ったり、コネクション数を増やすのを忘れた 1,2行追加するだけでスコアが上がるのに… 次回参加時は手順に入れておく 途中ベンチマークが動かないトラブルがあって何もできなかった 何も検証できない時でも何をするか決めておく ランキングを作る部分はISUCONでよく出てくるなぁと感じたので、Redisを使っていい感じにしたい これから練習していって知見をためていく 学び GoでDNSを自作できるらしい github.com/miekg/dns というライブラリを使えばいけるらしい 実装例 https://gist.github.com/ryotarai/2938ac139c23417da222c1a387415836 上位層は当たり前のように知っていて驚き discordにGitHubの通知を入れるのは便利 他の人が何をやっているのかわかって安心感がある まとめ 自分は初参戦のため普段の練習の力が出せるか不安でしたが、N+1問題を解消したりキャッシュを導入できたので十分成果は出せたのかなと思います。 ISUCONで初めてDNSの話が出てきたのでギョッとしてしまいましたが、結局基本が大事 午後は1万点を超えずになかなか辛い気持ちになっていましたが、17:00からぐんぐん点数が伸びていったので興奮しました。 途中、1時間くらいトラブルでベンチマークが実行できず手持ち無沙汰な時間を過ごしたのは残念でしたが、全体的には問題の質が高く楽しく解くことができました。 終了後はISUCON13の参加者全員が集まるdiscordで感想ややったことが共有され非常に勉強になりました。 次回ISUCONではさらに成長して上位層に食い込めるよう挑戦していきたいと思っています。 8時間という長丁場でパフォーマンスチューニングだけを考え、ひたすらボトルネックを解消していく機会はISUCON以外にはないと思います。皆さんもぜひISUCONに参加してパフォーマンスチューニングを身につけてみてください。 明日は、fu9shimaさんの「【祝10000MAU!】エンジニアブログ運用チームの活動まとめてみた」です。 どんなことをしているか気になりますね!お楽しみに! We are hiring! ニフティでは、さまざまなプロダクトへ挑戦するエンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトよりお気軽にご連絡ください! ニフティ株式会社採用情報 カジュアル面談も受け付けています! カジュアル面談 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering ニフティ株式会社 – connpass
アバター
前編はこちらの記事をご覧ください。 【インタビュー】主力事業を支える回線サービスシステム開発の裏側とは?【入会システム前編】 入会システムチームを更に深堀ります。 別チームから異動してきてどういった印象を受けましたか? D.Tさん まず最初に思ったのは扱っているサービスが多いと思いました。そうなると属人化が起こってもおかしくはないと思うのですが、スクラムを通してコミュニケーションを積極的に取って認識違いを失くしたりタスク内容などを全員が把握できる状態にしているので、誰が何をやっているのかわからない・・・といったことはないですね。 どのサブチームもスクラム開発を取り入れているのでしょうか? K.Nさん そうですね、ニフティ全体としてもスクラム開発を実践しているチームは結構あります。 D.Tさん K.Nさんはスクラムマスターの資格を取ってましたよね。 K.Nさん 去年取得しました。 社内でもスクラムマスター同士が集まれる環境があって、そこでそれぞれのチームの悩みなどを持ち寄って情報交流などをしています。 ひと言で言うとどんなチーム? K.Tさん アットホームなチームだなと思います。 D.Tさん フラットなチームですよね、異動してきたときハードルを感じなかったです。 K.Nさん お子さんがいる方も多いですね。私も小学生の子供がいるので熱が出ちゃったり、PTAの役員を引き受けちゃったりという事情はあったりしますが、チームメンバーも家庭の事情を分かってくれているので調整ができます。休暇も比較的自由にとれるので働きやすい会社だなと思います。 D.Tさん 女性に限らず、男性も子育てで休暇をとる人も多いです。それと、頻繁に飲み会があったりしますよね K.Nさん リーダー自ら声かけたりとね笑 D.Tさん 2次会の出席率が高いですよね、多いと7割くらいいくんじゃないかな笑笑 強制参加ではないですか?笑 「「それは全くないです!!」」 最後に、皆さんの仕事のやりがいを教えてください K.Tさん コードを書くことが好きなので開発そのものがとても楽しいですが、作ったものがお客様にご利用いただけているということがやりがいになっています。 自分で利用するためのシステム開発は入社前から趣味でやっていましたが、自分以外の方に使っていただけるのはとても嬉しいことなんだなと思いました。 K.Nさん 昔はリリースできたら達成感があって嬉しいという気持ちが大きかったのですが、今は達成して誰かが喜んでくれることが一番に感じています。 D.Tさん お客様の要望を叶えたり、チームの働き方を変えて良い方向に向かっていることが実感できたりと、何かを革新した時ですね。何かを作る・変えるだけではなくてその結果良い方向に向かったりすることが大事だなと感じます。 今回はニフティの入会システムチームのインタビューの様子をお届けしました。ニフティでは、さまざまなプロダクトへ挑戦するエンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトよりお気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する .is-style-rounded + .has-text-color{ font-size:95%; }
アバター
回線サービスシステムの裏側とは 自己紹介をお願いします K.Nさん 入会システムチームでサブリーダーをやっています。主にauひかりの申し込みシステムやオプションサービスであるまかせて365、Wi-Fiルーターレンタルサービスの開発運用を担当しています。2019年に中途入社しました。最近はスイカゲームを息子と一緒にやるのにハマっています。 D.Tさん 2023年に入会システムチームに異動してきました。社歴としては2018年からニフティで働いています。前のチームでは主に会員情報を扱うチームでしたが、現在は@nifty光やドコモ光などの申し込みシステムの開発・運用をしています。 K.Tさん 2020年度に入社をして現在新卒5年目社員です。入社時より入会システムチームで代理店やサポートセンター用の回線申し込みシステムや回線オプションの申し込みシステムを担当しています。 入会システムチームではどういったプロダクトを扱っているのでしょうか K.Nさん 主力事業である回線サービスの申し込みやキャリア間での連携するためのシステムの開発と運用を手掛けています。 D.Tさん プロダクト数が多いので、入会システムチーム内でもサブチームという単位で現在は3つに分かれていますが、朝会や勉強会は普段から一緒にやっているので交流は結構ありますよね。 K.Nさん そうですね、サブチームを跨いでの開発業務もありますし、チームも跨いでの開発もやることはありますね。ニフティでは開発手法にスクラムを取り入れているのでLeSSでの開発も経験しました。(LeSS開発については こちら ) 印象に残っているプロジェクトはありますか K.Nさん 立候補で集まったメンバーで光回線の申し込みシステムの刷新をしました。ちょうどこのメンバーが集まったんですよね。 D.Tさん 確かその時K.Tさんは新卒1年目でしたよね。 K.Tさん そうですね、当時色々なプロダクトに触れたいという気持ちが強くて触ったことのないAWSが触れるので思い切って手を上げました。サブチームメンバーが快く送り出してくれたので一生懸命頑張れました。 プロジェクトを進める中で大変だったことはありますか K.Nさん AWSを使って刷新しようと決めていたものの、当時AWSがわかるメンバーがプロジェクトメンバーにいなかったので手探りでのスタートでした。 K.Tさん 社内のAWSに詳しい人にノウハウを共有してもらって、進めていきました。チームを越えて質問しやすい環境なのは結構ニフティのいいところだなと思います。 D.Tさん あ、そういえば打ち上げまだやってないですね〜。 K.Nさん そうだ、やらないと!! 現在取り組んでいるプロジェクトについて教えてください K.Nさん 入会システムチームが持っているDBのリプレイスを現在しています。 単純なDB移行であれば移行して向き先を切り替えるだけで良いのですが、他のDBやシステム間でのデータのやり取りが発生している箇所があるので一筋縄ではいかないです。 それらをまとめてデータ移行する必要があるので、プロジェクトメンバー総出で頑張っています。 課題へのアプローチはどういったことをされていますか D.Tさん DBを参照しているシステムが多岐に渡るため、その担当者との会話が必須になります。他のチームへの定期的な会話であったり、早め早めの情報共有を心がけています。 後編に続きます! 今回はニフティの入会システムチームのインタビューの様子をお届けしました。続きは後編の記事をご覧ください。 【インタビュー】入会システムチームはどんなところ?【入会システム後編】 ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する .is-style-rounded + .has-text-color{ font-size:95%; }
アバター
この記事は、 ニフティグループ Advent Calendar 2023 1日目の記事です。 前段の話 私が所属するプロジェクトでは、Design Docsでソフトウェアの設計や、目的、背景などを記述しており、継続的に更新しています。 Design Docsには、細かな設計方針や、その意図は明確に記述されていますが、読みやすさの観点から結論や重要なポイントのみを載せるようにしています。なので、粒度の細かい情報が失われてしまうということが起こってしまいます。 これでは新規参入者になぜ他の選択肢を選ばなかったか、なぜこの選択肢を選んだのかについて伝授されません。 未来の運用者は数ある選択肢からの導入理由や、決定の過程や議論した内容がわからないまま機能の追加や改修を行わなければなりません。 これが積み重なっていくと日々の運用のコストが膨らんでいき、運用したくないシステムになってしまい、技術的負債となってしまいます。 そこで、私のプロジェクトでは決定の過程や議論の内容、導入理由をArchitecture Decision Record (ADR)に残していくことに決めました。 ADRとは何か Architecture Decision Record(ADR)は、ソフトウェアやシステムの設計における意思決定を文書化するための手法の一つです。ADRは、プロジェクトのアーキテクチャに関する重要な意思決定やその背景、理由、代替手段などを記録するために使用されます。これにより、将来の開発者やメンバーがプロジェクトの進化や変更に理解を持ちやすくなります。 ADRには概ね以下のような情報が含まれています: コンテキスト(Context) : 意思決定を行った背景や文脈に関する情報。プロジェクトの現状、問題点、要件などが含まれます。 意思決定(Decision) : 実際に行われた意思決定に関する情報。採用されたアーキテクチャの選択、採用しなかった代替手段などが含まれます。 結果(Consequences) : 意思決定の結果として期待される影響や将来の課題に関する情報。採用したアーキテクチャがどのようにプロジェクトに影響を与えるか、またその他の代替手段がどのような結果をもたらす可能性があるかなどが含まれます。 ステータス(Status) : ADRがどのような状態にあるかを示す情報。進行中、完了、廃止などが含まれます。 ADRは、ソフトウェアアーキテクチャにおける変更や進化が行われる際に、なぜ特定のアーキテクチャが採用されたかを理解しやすくするために使用されます。これは特に、プロジェクトが長期間にわたって拡大し、新しいメンバーが参加する場合に役立ちます。 ADRはAWSやGoogle Cloudでも紹介されています。 Using architectural decision records to streamline technical decision-making for a software development project アーキテクチャ決定レコードの概要 ADRを使ってみる いつ書くか 先述したようにADRはアーキテクチャの設計における意思決定を文書化するための手法です。 しかし、私が担当するプロジェクトではアーキテクチャ以外においても、設計上の判断に 複数の選択肢 が考えられ、後の運用者から見て推定が困難であるものに対してADRを書くようにしています。 こうすることで、担当プロジェクトのさまざまな意思決定や選択理由を一括管理することができ、新規参入者が容易に参照し、理解することができるようになります。 例: 言語・フレームワークや開発ツールなどの選定 使用するインフラの構成 アプリケーション全体の設計やディレクトリ管理方針 アプリケーションロジックにおける計算方法の選択 一方、自明であるものに対しては記述しません。 (自明であるかどうかに対してチーム内で意見が分かれる場合は書くべきである) 例: ECSに対してALBを導入する決定 また、一つの意思決定につき一つのADRを作成します。 なので、再利用をせずに、新たな変更があったときは新しいADRを作成する必要があります。 実際に使っているテンプレート 設計判断を蓄積し、検索・参照可能とすることが求められるため、軽量なフォーマットが適しています。また、テンプレートにはばらつきがあり確定したフォーマットがありません。 実際に使用しているテンプレートは以下のようになっています。 一般的なテンプレートからほぼ変えていないですが、Decisionを上に持ってきています。 これは、決定した内容がファーストビューに表示され、ぱっと見でわかるようにしているためです。 また、過去経験の蓄積という性質を重視したいことから、Consideration(比較検討や議論)とReferences(参考情報)を項目として追加しています。 # Status - Draft: 記述中またはレビュー中 - Accepted: レビュー承認され、現在有効である - Rejected: レビュー非承認となった - Deprecated: 内容が古くなり破棄された - Superseded: 他のADRにより内容が更新された # Decision 実際に決定した内容 # Context 決定を必要とする背景 # Consideration 比較検討や議論内容 # Consequences この決定によって予測される結果 # References 参考情報 運用について 一度作成されたADRを変更しない運用としています。 新規作成時 設計判断が必要となったらADRをDraftとして作成し、チームメンバーに承認を求める 議論・修正ののち、承認された場合 ステータスをAcceptedに移す Design Docに関連の深い機能がある場合は、Design Docからリンク 議論の結果、判断提案自体を非承認とする場合 ステータスをRejectedに移す 更新時 新しくADRを作成し、Referencesに更新元ADRへのリンクを付ける 承認された場合 新ADR ステータスをAcceptedに移す Design Docに関連の深い機能がある場合は、Design Docからリンク 旧ADR ステータスをSupersededに移す 置換先として新ADRをリンクさせる Design Docからリンクされていた場合はリンクを削除する 非承認の場合は新規作成時同様、Rejectedとする 実際に運用してみてのいろいろ さて、実際に運用してみてしばらく経っていますが、メリットと課題を並べてみます。 まず、実際にADRを運用してみて感じたメリットは以下のようになっています。 意思決定が文書化されているので、プロジェクトのアーキテクチャに関する重要な情報が透明になり、新規参入者がプロジェクトに参画しやすくなる 変更の経緯や背景を知ることで、特定のアーキテクチャの選択がなされた理由を理解しやすくなる ADRの項目に議論した内容を記録する項目を入れています。よって、意思決定の際には必ずコミュニケーションを挟むようになり、チーム内のコミュニケーションが活性化される 意思決定の際に検討した代替手段が文書化されるため、将来の変更や改善のためにこれらの選択肢を再評価することが簡単 運用する上で課題と感じている部分は以下のようになっています。 とにかく書くのが面倒 意思決定した際に文書化しないといけないので開発スピードは落ちる (良いように言うと、慎重に決めてから開発できる) 現状だと意思決定が発生する場合は大小問わず全てADRを執筆するようになっているので、細かいライブラリのインストールなども全て執筆するようになっている 「本当は便利なライブラリだけどADR書くの面倒だから入れなくていいかぁ」ってなる 全部書いてるとそのうち内容が疎かになりそう やはり長い文章を書くのは少し億劫ですよね。。。 ADRのメリットを最大限に引き出すためには、文書作成の手続きを極力効率化し、プロジェクトの規模や状況に合わせて柔軟に適用することが重要だと感じました。 これによって、慎重かつ迅速な意思決定と、プロジェクト全体の透明性を両立させることが可能になると思います。 もっとシンプルにかつ現状のクオリティを損なわない方法を検討中です! まとめ ADRを作成することで、設計の背景や流れを記録することにより技術的負債なく未来へプロジェクトを託すことができます。 また、ADRプロジェクトの長期的な成功に貢献する有用なツールであり、慎重に導入し、適切に活用することで、ソフトウェア開発プロセスをより効果的かつ効率的に進める手助けとなります。 みなさんもADRを導入してみてはいかがでしょうか! 明日は、fu9shima さんが何か書かれるそうです。 お楽しみに! ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
アバター
こんにちは!NIFTY engineeringブログ運用チームのいかりがわです! 明日から12月が始まり、今年もあっという間に残りわずかとなりました。クリスマスが迫り、アドベントカレンダーの季節が到来しましたね! ニフティグループでは毎年、アドベントカレンダーに積極的に参加しており、今年でなんと8回目の開催となります! (結構長くやってますね…笑) 去年からは、ニフティのエンジニアブログである「NIFTY engineering」に記事を掲載することになり、ますますイベントの盛り上がりが増しています。 日頃の知見をアウトプットする機会として、ニフティグループのエンジニアが楽しみながら成長していくことができています。 今年も皆様に新たな技術や知識をお届けできることを楽しみにしています! アドベントカレンダーとは? 元々はクリスマスまでの日数をカウントダウンするために使われていたカレンダーで、12月1日からはじまり、25個ある「窓」を毎日1つずつ開けて中に入っている小さなお菓子やプレゼントを楽しむものです。 このカレンダーにならい、定められたテーマに従い参加者が持ち回りで自身のブログやサイトに記事を投稿する、リレー形式のブログ執筆イベントです! ニフティグループ アドベントカレンダー ニフティグループではフリーテーマとなっておりますので、好きに執筆してもらっています。 また、記事に関してはQiita様のアドベントカレンダーページにて登録させていただき、 公開自体はNIFTY engineeringや ニフティライフスタイル Tech Blog 、Qiitaに公開していく予定です!(ニフティ、ニフティライフスタイル、セシールでそれぞれ投稿場所が異なります) ニフティグループ AdventCalendar 2023 カレンダーは徐々に埋まってきており、2枚目に突入しております!!! アドベントカレンダーでは、ニフティグループのエンジニアが日頃の業務で培ったノウハウを共有していきますのでぜひチェックしてみてください! ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
アバター
NIFTY Tech Day 2023 運営スタッフの島です。 先日11月18日にNIFTY Tech Day 2023を開催しました。 たくさんのご視聴、ご参加ありがとうございました! 当日、コンテンツの一環としてエンジニア謎解きを出題していたので、その正解を発表したいと思います。 Q1  正解は・・・ 「AWS」でした! ◯の部分がアルファベット順になっているので、番号の振ってある箇所を解いていくと「AWS」になります。 ※こちらの問題については、当日映していた内容に誤りがありました。お詫びして訂正させていただきます。 Q2 正解は・・・ 「ニフティ」でした! この図はスマホのフリック操作になっていて、例題の通りにスマホのキーボードをフリック操作してみると「スマホ」「コオリ」となるので、同じように順番に操作してみると「ニフティ」という文字が打てるはずです。 以上、エンジニア謎解きの正解発表でした! ニフティでは、社長の前島が謎解きが好きということもあって、社長から謎解きが出題されることがあります! 早く解けた人には社長からギフトがプレゼントされることもあり、みなさん真剣にチャレンジしています! そんなユニークな企画も行なっているニフティであなたも働いてみませんか? We are hiring! ニフティでは、さまざまなプロダクトへ挑戦するエンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトよりお気軽にご連絡ください! ニフティ株式会社採用情報 カジュアル面談も受け付けています! カジュアル面談 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering ニフティ株式会社 – connpass
アバター
こんにちは!会員システムグループでエンジニアをしている山田です。 今回はNIFTY Tech Day 2023で掲示していたコードレビュー問題の解答編になります。 問題は こちら で公開していますので、まだ見てないよ!という方は是非チャレンジしてみてください。 出題内容 出題コードをおさらいしてみます。 AutoComplete.tsx /* eslint-disable */ import React, { useEffect, useState } from 'react'; import { AutoCompleteList } from '../common/AutoCompleteList'; import { AutoCompleteContainer } from '../common/AutoCompleteContainer'; import { AutoCompleteItem } from '../common/AutoCompleteItem'; import StatefulTextBox from './StatefulTextBox'; const api = '/api/autoSuggest'; export const AutoComplete = () => { const [value, setValue] = useState(''); const [suggestion, setSuggestion] = useState<string[]>([]); useEffect(() => { const fetchFn = async () => { const response = await fetch(`${api}?q=${value}`); const responseJson = await response.json(); setSuggestion(responseJson.items); }; fetchFn(); }); const AutoCompleteWindow = () => { let listElements: JSX.Element[] = []; for (let suggestionItem of suggestion) { listElements.push( <AutoCompleteItem value={suggestionItem} onClick={setValue} /> ); } return <AutoCompleteList>{listElements}</AutoCompleteList>; }; return ( <AutoCompleteContainer> <StatefulTextBox onValueChange={(value) => setValue(value)} /> <AutoCompleteWindow /> </AutoCompleteContainer> ); }; StatefulTextBox.tsx /* eslint-disable */ import { useEffect, useState } from 'react'; import { TextBox } from '../common/TextBox'; type Props = { onValueChange: (value: string) => void; }; export default function ({ onValueChange }: Props): JSX.Element { const [value, setValue] = useState(''); useEffect(() => { onValueChange(value); }, [value, onValueChange]); return <TextBox value={value} onChange={setValue} />; } これはテキストボックスへの入力値を元に、入力補完の機能(オートコンプリート)を実現するコンポーネントになっています。「common」からimportしているコンポーネントは既成のもの、という想定です。 テキストボックスの入力値をstateで保持するStatefulTextBoxがあり、その値変化をAutoCompleteコンポーネントで受け取って、useEffectで補完内容をfetchし、表示するという流れです。 一見問題なく動くように見えますが、大きく区分すると以下のような問題・改善点を抱えています。 バグ挙動 エラー処理・キャンセルの未考慮 パフォーマンス上の問題 コードの統一性の問題 設計上の改善点 仕様上の改善点 バグ挙動 無限レンダリング AutoCompleteコンポーネントではuseEffectの処理の中でsetSuggestion関数を呼んでいます。これによりstateが更新されますが、これは再レンダリングを引き起こすため、再びuseEffectが呼ばれることになります。つまり、このコンポーネントは無限にレンダリングが走り、無限にfetchを呼ぶことになります。 useEffectを使用する場合は第二引数(dependencies)でスキップ条件を記載するのが適切です。 useEffect(() => { ... }, [value, setSuggestion]); 一部文字列での補完不具合 fetchをする際に文字列を直接URLに埋め込んでしまっているため、URLで使用する文字列では不具合を起こします。例えば「a&b」など「&」を含む入力を渡した場合、「&」以降の文字列が別パラメータになってしまうため、補完に使われなくなってしまいます。 URLに渡すパラメータは適切にURLエンコードをすべきです。要件によってはさらにサニタイズ・ノーマライズを行ったほうが良いでしょう。 const params = new URLSearchParams({ q: value }); const response = await fetch(`${api}?${params.toString()}`); 補完内容がなくても枠が表示される 補完内容を表示しているモーダル部分は、中身が0件でも表示されてしまいます。このため、無駄な枠が表示されたままになってしまいます。 補完内容がない場合は非表示にしておくのが良いでしょう。 return ( <AutoCompleteContainer> ... { suggestionData.length !== 0 && ( ... ) } </AutoCompleteContainer> ); エラー処理・キャンセルの未考慮 fetch時の異常系未考慮 fetchを実行する際に異常系の考慮をしていないため、以下の問題を抱えています。 fetchが例外を返した場合にクラッシュする 404、500などの異常系応答を返した場合に異常値が入る 200応答でも想定外の値が返ってきた場合、異常値が入る 2つ目に関して、fetchは異常系応答でも例外を返さないという特徴があるので注意が必要です。 fetchを利用する際はレスポンスの検証とエラーハンドリングを行いましょう。 const ResponseSchema = z.object({ items: z.array(z.string()); }); ... try { const response = await fetch(...); if (!response.ok) { // 異常系応答時のハンドリング } const responseJson = await response.json(); // zodでのバリデーション const parsedResponse = ResponseSchema.parse(responseJson); ... } catch (e) { // 例外ハンドリング } 上記例ではバリデーションライブラリとして zod を利用し、レスポンスのJSONオブジェクトが期待した型であるかどうかをチェックしています。 異常系応答に関しては、 ky や axios などのサードパーティHTTPクライアントを利用することで、例外として処理するようにすることもできます。 非同期処理のキャンセル useEffectでは非同期関数を実行していますが、これは完了前にコンポーネントが再レンダリングされたとしても実行され続けます。これはメモリリークやRace Conditionを引き起こす問題になります。 fetchはAbortControllerを利用することでキャンセル処理が可能です。ただしSafari12.0以下など、古いブラウザでは対応していないことには注意が必要です。 useEffect(() => { const abortController = new AbortController(); const response = await fetch(..., { signal: abortController.signal }); ... return () => { abortController.abort(); }; }); ただしこの場合でも、後続のJSONパース処理などはキャンセルできません。厳密にRace Conditionを防ぐのであれば、JSONのパースを同期処理にする、フラグで制御するようにするなどが必要になってくるでしょう。 そもそもuseEffectを使わず、 SWR や Tanstack Query などを使うようにするという方法もあります。 パフォーマンス上の問題 コンポーネント内でのコンポーネント定義 AutoCompleteコンポーネント内でAutoCompleteWindowコンポーネントを作っています。AutoCompleteが再レンダリングされると、AutoCompleteWindowは再度定義されることになり、以前のものとは別のコンポーネントになります。したがって毎回再生成されてしまうので、パフォーマンス上不利になります。また今後AutoCompleteWindowが拡張されてstateやフォーカスを持つようになった場合、再レンダリングごとに状態がリセットされてしまうことにもなります。 このため、コンポーネント内でコンポーネントを定義してはいけません。別コンポーネントとして切り出すか、今回のような規模であれば同じreturnの中でまとめてしまって良いかもしれません。 return ( <AutoCompleteContainer> ... <AutoCompleteList> {suggestionData.map((suggestion) => ( <AutoCompleteItem value={suggestion} onClick={setValue} key={suggestion} /> ))} </AutoCompleteList> </AutoCompleteContainer> ); forでコンポーネントを作るのはReactではあまり一般的ではないので、上記例ではmapを利用した形に直しています。 stateが二重管理になっている テキストボックスへの入力値をAutoCompleteとTextBoxの両方でstateとして保持しています。 このような場合は上位コンポーネントに状態を移動(リフトアップ)するのが良いでしょう。今回の場合はStatefulTextBoxコンポーネントがそもそも不要で、AutoCompleteからTextBoxを直接呼ぶような形で良いかもしれません。 コードの統一性の問題 ESLintを無効化している 両ファイルとも、冒頭でESLintを完全に無効化してしまっています。ESLintを導入しているということはチーム内で守るべきルールが定められているということなので、これを無視してはいけません。実際、本コードの問題の多くはESLintによって指摘されます。 ESLintには従うことが原則であり、例外的に無視せざるを得ない箇所は理由を添えておくのが良いでしょう。無視コメントが増えるようなら、ESLintの設定を見直すときかもしれません。 // <無視する理由> // eslint-disable-next-line <無視ルール> コンポーネント定義方法に違いがある AutoCompleteとStatefulTextBoxでは、コンポーネント定義に以下の違いがあります。 named exportかdefault exportか 関数定義がアロー関数かfunctionか 戻り値型(JSX.Element)を明示するかしないか それぞれどちらを使うかはチームの方針によりますが、同一プロジェクト内で分かれていると不要な混乱を呼びます。特にexportの方法が異なると、importする際にどちらを使うべきかを調べる必要が生まれるので、どちらかに統一されているべきです。 また変数名を省略したdefault export(anonymous default export)が使われていますが、これは以下のような問題を引き起こすので、使用しないほうが良いです。 React Developer Toolでコンポーネント名が「default」になる IDE上での自動importが効かない デバッグ実行でのFast Refreshが動作しない default exportを使う場合は変数名をつけてからexportするようにしましょう。 const StatefulTextBox = ... export default StatefulTextBox; 設計上の改善点 HTTPアクセス部分の分離 HTTPアクセス部分がコンポーネントにベタ書きなので、ユニットテストがしづらい状態になっています。 msw などのHTTPモックを使うという手もありますが、別関数に分離することでテストしやすくなる可能性があります。HTTPクライアントが変わったり、API仕様が変わった時にも対応しやすいでしょう。 const getSuggestion = async (query: string): Promise<string[]> => { const response = fetch(...); ... return suggestion } 補完ロジックの分離 テスト設計方針にもよりますが、補完部分のロジックをカスタムフックに分離することで、ロジック部分のみでテストできるようにしたほうが良いかもしれません。 const useSuggestion = (value: string): string[] => { const [suggestion, setSuggestion] = useState<string[]>([]); useEffect(() => { ... }); } 仕様上の改善点 これらは入力補完というものに固有の問題で、気づきにくいものです。当日のオフライン会場でも、ヒントなしでこの可能性に気付かれた方は0名でした。 仕様にも関わるのでプロダクトオーナーへの確認も必要となるでしょう。 空文字列に対する補完は不要 入力補完は入力された文字列を元に候補を表示するので、文字列が空の場合は候補を出すことは通常困難であるはずです。 API側の仕様確認が必要ですが、空文字列の場合に補完を停止することで不要なアクセスを抑えることができます。 useEffect(() => { if (value !== '') { ... } }); 入力のたびに補完は不要 入力文字1文字1文字に対してまで補完を実行するのはほとんどの場合過剰であるはずです。ある程度の文字を打ち込み、入力が止まったタイミングで表示するような挙動で問題ないでしょう。 以下のような遅延(debounce)の処理を入れることで、ユーザが連続入力している間は補完処理を止めることができるようになります。こうすることでAPI側の負荷を大きく下げることができます。 const useDebounce = <T>(value: T, delayMs?: number): T => { const [debouncedValue, setDebouncedValue] = useState<T>(value); useEffect(() => { const timer = setTimeout(() => { setDebouncedValue(value); }, delayMs ?? 500); return () => { clearTimeout(timer); }; }, [value, delayMs]); return debouncedValue; }; const [value, setValue] = useState(""); // debounceした値を補完のクエリとして使う const debouncedValue = useDebounce(value); ただしこれは補完結果の表示が遅れることに繋がるので、チーム内で仕様調整が必要になるでしょう。 その他の改善点など Reactのimportは不要 昔はJSX記法を利用した場合、トランスパイル時に React . createElement ( ) に変換される仕様だったため、必ず import React from 'react' ; の記載が必要でした。 React 17以降はJSX Transformと呼ばれる形の変換に変更となり、 import . . . from 'react/jsx-runtime' ; の形式のimport文が自動で挿入されるようになったので、この記載は不要になりました。 変数・定数の命名 変数名を具体的にしたほうがわかりやすい箇所があります。 例えば変数「api」が挿すのはURLなので、固定値であることも含めると const SUGGEST_API_URL = "/api/autoSuggest"; のようにしたほうがわかりやすくなります。 コンポーネント名 事前に想定はしていなかったのですが、何名かの方から「AutoComplete」というコンポーネント名を変えた方が良いのでは、というレビューをいただきました。 一般的にコンポーネント名は名詞ですが、AutoCompleteは動詞です。またAutoCompleteという名前は機能を表しているので、「テキストボックスと補完ウィンドウのセット」というUIのイメージが湧きづらい命名になっています。 MUIなどのコンポーネントライブラリでは「Autocomplete」の名称で提供されているので一概には言えませんが、これもより具体的な名前に変更した方がわかりやすくなる箇所でしょう。 絶対importか相対importか これも事前に想定はしていなかったのですが、何名かの方から「絶対importの方が良いのでは?」というレビューをいただきました。このような形のものですね。 // 絶対import import { AutoCompleteList } from 'components/common/AutoCompleteList'; // エイリアスを利用した絶対import import { AutoCompleteList } from '@/components/common/AutoCompleteList'; 相対パスだとディレクトリ構造がわかりにくいので、絶対パスにした方が可視性が高い、という利点があります。もちろんプロジェクト内で統一する必要はあります。 ただし今後やってくるであろうES Modulesへの対応では注意が必要です。 CommonJSと異なり、ES Modulesの仕様では絶対パスという仕様がありません。トランスパイル先をES Modulesにした場合には、すべて相対パスで記載することが基本になります。webpackなどのバンドラーが変換してくれることもありますが、それらの設定を再確認する必要があります。 以上、それほど長くはないコードだったのですが、よく読むと問題があるようなコードになっていました。 当日の様子 当日はこのような形で掲示していました。 初の試みで参加いただけるか不安もあったのですが、多くの方にレビューをいただき、 useEffect()の第二引数忘れてる ロジックは分離して単体テストできるようにした方が良さそう APIのURLはベタ書きしちゃってもいいのでは (上コメントに対して)いやそれはどうだろう… 関数の改行が見づらい など、多くのレビューをいただくことができました。 まとめ 問題点に気づくことができましたでしょうか。コードレビューに正解はありませんので、記載した方法以外のやり方があるかもしれませんし、それ以外の場所にも何かが眠っているかもしれません。私はこう思うよ!というようなものがありましたら、ぜひX(Twitter)などで教えていただければ幸いです。 このような取り組みはニフティとしても初めてだったのですが、参加いただいた方には概ね好評だったようで喜ばしく思っています。一方でフロントエンド領域、かつReactに強く依存した問題でしたので、バックエンドエンジニアの方ですと参加が難しくなってしまった、というのが反省点です。 次回も同様の問題をお見せできるようなことがあれば、Pythonなどの問題も用意できるようにしていきたいと思っておりますので、今回参加できなかった方もぜひ挑戦いただければ幸いです。 We are hiring! ニフティでは、さまざまなプロダクトへ挑戦するエンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトよりお気軽にご連絡ください! ニフティ株式会社採用情報 カジュアル面談も常時受け付けています! カジュアル面談 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering ニフティ株式会社 – connpass
アバター
こんにちは! 今日は NIFTY Tech Day 2023 の開催日です!オンラインでセッションを視聴することもできるので、ご登録がまだの方は奮ってご参加ください! さて、NIFTY Tech Day 2023では、オフライン会場にてReactを使ったコードレビュー問題を提示しています。しかし折角の問題なのでオフラインだけでは勿体無い……! ということで、今回はオンライン参加の方々にもぜひ挑戦していただきたいです! 回答については、後日エンジニアブログにて掲載予定です。 出題 次のような検索サジェストを表示するコンポーネントをReactで作成しました。 ぱっと見だと動いているし問題なさそう!……本当に? React有識者、レビュー求む! おかしな箇所を見つけたら #nifty_tech_day #コードレビュー問題 のハッシュタグをつけてXに投稿して教えてください! src/components/AutoComplete/AutoComplete.tsx /* eslint-disable */ import React, { useEffect, useState } from 'react'; import { AutoCompleteList } from '../common/AutoCompleteList'; import { AutoCompleteContainer } from '../common/AutoCompleteContainer'; import { AutoCompleteItem } from '../common/AutoCompleteItem'; import StatefulTextBox from './StatefulTextBox'; const api = '/api/autoSuggest'; export const AutoComplete = () => { const [value, setValue] = useState(''); const [suggestion, setSuggestion] = useState<string[]>([]); useEffect(() => { const fetchFn = async () => { const response = await fetch(`${api}?q=${value}`); const responseJson = await response.json(); setSuggestion(responseJson.items); }; fetchFn(); }); const AutoCompleteWindow = () => { let listElements: JSX.Element[] = []; for (let suggestionItem of suggestion) { listElements.push( <AutoCompleteItem value={suggestionItem} onClick={setValue} /> ); } return <AutoCompleteList>{listElements}</AutoCompleteList>; }; return ( <AutoCompleteContainer> <StatefulTextBox onValueChange={(value) => setValue(value)} /> <AutoCompleteWindow /> </AutoCompleteContainer> ); }; src/components/AutoComplete/StatefulTextBox.tsx /* eslint-disable */ import { useEffect, useState } from 'react'; import { TextBox } from '../common/TextBox'; type Props = { onValueChange: (value: string) => void; }; export default function ({ onValueChange }: Props): JSX.Element { const [value, setValue] = useState(''); useEffect(() => { onValueChange(value); }, [value, onValueChange]); return <TextBox value={value} onChange={setValue} />; } We are hiring! ニフティでは、さまざまなプロダクトへ挑戦するエンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトよりお気軽にご連絡ください! ニフティ株式会社採用情報 カジュアル面談も常時受け付けています! カジュアル面談 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering ニフティ株式会社 – connpass
アバター
記事の対象者 インナーソースに興味がある インナーソースを導入してみたいと思っている インナーソースを実際に導入するまでにやったことを知りたい 記事の内容 1. インナーソースとは 2. インナーソース導入のきっかけと目的 2.1. インナーソース導入前の状態 2.2. きっかけ 2.3. 目的 3. インナーソースお試し導入でやったこと 3.1. 情報収集 3.2. 社内のエンジニア全体会でインナーソースについて発表 3.3. 社内用ツールでインナーソースのテストプロジェクト 3.3.1 テストプロジェクトをやるために準備したこと 3.4. テストプロジェクトの評価とアンケート実施 3.4.1. テストプロジェクトの評価 3.4.2. アンケート 3.4.3. アンケート結果からの考察 4. 後の話はその②の記事に書きます We are hiring! こんにちは!会員・認証基盤システムの開発・運用をしている基幹システムグループの小松です。 社内にインナーソースの導入を進めています。 インナーソース導入活動の様子、実際にやったことなどをブログで共有していきます。 今回はその①、インナーソースを導入活動発足から、1つのリポジトリでインナーソースお試し導入をしてアンケートを取るまでの範囲を紹介します。 その②では、その後の全社展開のために活動した内容紹介予定です。 1. インナーソースとは 世界最大コミュニティ InnerSource Commonsによる、インナーソースの定義は以下です。 「組織という限られた環境において、ソフトウェア開発におけるオープンソースの原則とプラクティスを活用すること」 簡単にイメージを言うと、「企業内部限定のオープンソース活動」です。 これにより組織内の異なるチームや部門が効果的にコラボレーションし、知識やコードを共有することが可能になります。 インナーソースによるメリットは以下です。 コード再利用による開発効率の向上 コラボレーションの強化 透明性の向上 2. インナーソース導入のきっかけと目的 2.1. インナーソース導入前の状態 エンジニアが約160名。 基幹システムグループ、会員システムグループ、インフラシステムグループにわかれ、それぞれ各チーム、サブチームにわかれます。 一部協力会社に開発・保守を任せるシステムもありますが、基本的に内製による開発・保守を行っています。 担当システムの人が開発・保守を行い、他システムにも機能修正が必要な場合は、他チームのシステム担当に依頼し、修正をしてもらう形になります。 他チームからの修正依頼に追われるチームもありました。 多くのチームはGitHubを使っているのですが、気軽にグループ間でソース共有するような文化もあまりありませんでした。 2.2. きっかけ 最初のきっかけは、私の上司である芦川が管理職レイヤーで事業計画を考えている際に、他チームの開発をできるようになればもっと柔軟に開発ができるのではないかと思っていました。 せっかくなら、社内独自ルールを作るよりも一般的なOSSコントリビュート方法にならう形がいいと思い、調べているとインナーソースを知ったそうです。 私は、後述するテストプロジェクトにちょうどいい社内用ツールのリポジトリを管理していたので、そこから一緒に広めようということになりました。 2.3. 目的 ニフティでの導入の目的は、将来的にサイロ化を破壊し、開発リソースの有効活用することです。 ですが、現在のインナーソース導入期間での目的は、Developer Experienceの向上です。 Developer Experienceとは、簡単に言うと「エンジニアが気持ちよく開発・保守できる環境」を指します。 InnerSource Commons Japanの運営メンバーである服部 佑樹さんが、インナーソースを導入する目的は2種類あると言っていました。 「Developer Experience and InnerSource – エンジニアの共創は結局大企業では無理なのだろうか」の 36ページ から書かれています。 「Developer Exprerienceのため」と「競争戦略のため」の2種類。 競争戦略のためは主に製造業などで目的とされるそうです。 ニフティでは、まずDeveloper Exprerienceの向上を目指します。 Developer Exprerienceのために導入する場合の得られる効果を資料から引用させてもらいます。 透明でコラボティブな開発文化育成 技術や知識共有によるスキルレベル向上 従業員満足度向上と離職率の低下 車輪の再発明の防止で生産コスト削減 https://speakerdeck.com/yuhattor/developer-experience-and-innersource-ensinianogong-chuang-hajie-ju-da-qi-ye-tehawu-li-nanotarouka?slide=37 3. インナーソースお試し導入でやったこと 3.1. 情報収集 InnserSouce Commonsという世界で最も大きいコミュニティがインナーソースに関する情報や、ベストプラクティスなどを紹介しています。 InnerSource Commons Japanもあり、ドキュメントの翻訳や、connpassでのオンラインイベント開催などをしてくれています。 まずは インナーソースパターンブック というベストプラクティスをまとめたドキュメントがあるので、そちらを読みました。 導入のフェーズごとにわかれているので、まずBeginの箇所から読みました。 またInnserSouce Commonsの ラーニングパス もあるので、一通り読みました。 ここは初学者にとっては少し難しいかもしれません。 ここで説明されているプロダクトオーナーは、他チームのリポジトリとコラボレーションを考えたり、テスト要件、コーディング要件までドキュメント化するなどの役割があり、エンジニアでないと難しいと思います。 ニフティではプロダクトオーナーがビジネス職の場合もあります。その場合はトラステッドコミッターに技術的な範囲の権限を委任するという形をとっています。 他には InnerSource Commons Japanがconnpassでイベントを開催 しているので、参加できるものは参加しました。 過去のイベント内容も YouTubeチャンネル にアップロードされています。 3.2. 社内のエンジニア全体会でインナーソースについて発表 月に1回エンジニア全体会を行っており、LTコーナーがあるので発表しました。 インナーソースの説明と、導入するとこんなメリットがあるというまず簡単な共有をしました。 インナーソースという言葉を初めて聞く人がほとんどだったと思いますが、以下のようなポジティブな反応が多かったです。 「別チームのリポジトリにプルリクだしてみたい」 「〇〇ツールはみんなで触れるのを理想としていた」 「OSSコントリビュートの練習にもなりそう」 3.3. 社内用ツールでインナーソースのテストプロジェクト これは、エンジニアが誰でも知っていて、そこまでコントリビュートにハードルがない社内用ツールでやってみようということになりました。 ちなみ社内用ツールとは、私が作った「もじこえ」というツール。 オンライン会議で気軽なリアクションができるようにと、作った読み上げ機能つきのチャットツールです。 リモート会議のリアクションわかりづらい問題を解消する「もじこえ」を作ってみた 全社的に知られており、中身はシンプルで機能追加などもしやすいので、このリポジトリでテストプロジェクトをやることにしました。 3.3.1 テストプロジェクトをやるために準備したこと Slackチャンネル作成 社内ツール(もじこえ)専用チャンネル こちらで機能追加の相談などを受けます。 インナーソース専用チャンネル こちらはインナーソースについての連絡や相談の場として使います。 test, lint環境を整える 様々なチームの人がコントリビュートすることになるため、コードの品質向上、一貫性を保つためにもテスト、リントを用意しておく必要があります。 README.mdの作成 コントリビュートについては、CONTRIBUTING.mdを参照するように誘導しました。 InnerSource Commonsが テンプレート を公開しています。 CONTRIBUTING.md作成 コントリビュートするための詳細な手順を載せます。 コントリビュートしたいと思った人は、まずCONTRIBUTING.mdをみることになります。 InnerSource Commonsが テンプレート を公開しています。 用意ができたら、エンジニア全員が見れる場所で連絡します。 事前に、コントリビュートしてくれそうな人、チームがあれば、直接メンションを飛ばします。 継続的に宣伝したり、参加してくれる人には手厚いサポートをしながら地道に進めていきます。 Goog First Issueも何個か用意しておき、初めての人でもコントリビュートしやすい環境を用意しました。(実際にコントリビュートしてもらえました) 3.4. テストプロジェクトの評価とアンケート実施 テストプロジェクトは約2ヶ月で一区切りとして評価しました。 3.4.1. テストプロジェクトの評価 Issueにアサインしてくれた人が7名、実際にマージされたPRが3件でした。 新卒入社5年目までの若手が参加してくれました。 インナーソースを体験してみたいと、CONTRIBUTING.mdの修正をしてくれる人がいたり、機能追加のリクエストをくれる人もいました。 皆、メイン業務の合間にやることになるので、機能追加系のIssueにアサインしたものの、テストプロジェクトの間ではマージまでできませんでした。 機能追加にしても、なるべくサイズを小さくするのがよさそうです。 Goog First Issueも1件マージしてもらうことができました。 やはり、インナーソースを体験してみたいという需要はありそうです。 3.4.2. アンケート 実際に参加した人だけでなく、エンジニア全体を対象としてアンケートを取りました。 なるべく、回答率を上げるため、質問数は絞って簡単に答えてもらえるようにしました。 エンジニアの約3割の方が回答してくれました。 部署ごとの割合もある程度バランス良く回答もらえました。 以下はアンケート内容と結果です。 1.インナーソースに対する理解度はどの程度ですか? ちょうど半分くらい、理解がある人と、ない人でわかれていました。 2.インナーソースの取り組みに関するハードルがありますか?  (複数選択可) どんな些細なハードルでも記入していただくと助かります。 「時間やリソースの制約」が一番多かったです。他チームのリポジトリへのコントリビュートはメイン業務と調整して行うことになるので、予想通りハードルが高そうです。 まずハードルを改善できそうなところでいうと、「理解がない」「コミュニケーションの課題」になりそうなので、まずここにアプローチしていくことになりそうです。 3.インナーソースに参加する上でどのようなサポートが必要だと感じますか?  (複数選択可) 「社内でのインナーソースに関する情報共有やプロモーション」「ガイドライン」が必要であることを改めて感じました。 3番めに多い「インナーソースの専門知識やトレーニング」、4番目に多い「メンターやサポート役」から、インナーソースを普及・サポートする活動も継続的に行っていく必要があると感じました。 4.今後、インナーソースを他のプロジェクトや部署に展開することについてどのように考えますか?    反対の方はいませんでした。 中立の方で多かった理由は、そもそもインナーソースの理解がないから判断できないというものでした。 5.上記(4番の質問)の回答理由をざっくばらんに記入してください。一言でも結構です。 回答をまとめてみると以下のようになりました。 賛成 エンジニア間の横展開が活発になりそう さまざまな視点からコードをいいものにしていくことができそう リソースを効率的に使える 技術力向上 ライン業務以外で技術に触れることができる 属人化解消 情報がオープンになる 他チームのシステムを修正したいとき、担当者のスケジュールを気にしなくてよくなり、優先順位で後回しにされることも無くせるのでよい 中立 判断できるまで理解がない 取り入れる工数があるか不安 賛成では多くのメリットを上げてもらえ、インナーソースを知っている人には、メリットが伝わっているのだと感じました。 6.もじこえインナーソースに参加、参加しようとしてくださった方のみ必須 こちらの質問は、実際にもじこえインナーソース実験に参加、参加しようとした方を対象 「チーム間のコラボーション向上」が最も多く、実際にグループの垣根を超えて参加してくれる方がいました。私も普段関わらない人とコミュニケーションが取れてコラボレーション向上は大きく感じました。 「ナレッジ共有の促進について」も高く、他チームのおすすめCIツール設定や他者の面白いアイデアなどがリポジトリに反映されるので、私自身も大きな可能性を感じました。 「コードの品質向上」については、README.mdやCONTRIBUTING.mdなどのドキュメント系が整備されたのがよかったですが、今回お試し対象になったツールは画面UIを修正することがメインになるシンプルなツールだったため、テストコードが増えませんでした。 「プロジェクトの進捗速度向上」については、メイン業務で使われず、独立したツールのため、開発速度を求められておらず、メイン業務の隙間に開発する人が多かったので、低くなったと思います。 7.インナーソースの取り組みに関して、感想、改善すべき点や提案があればご記入ください。どんな些細なことでもOKです。 質問の回答を一部抜粋します。 感想 コーディングが得意でなくても気軽に取り組めるものだと嬉しい 楽しい 皆自分のチームの仕事があると思うので、1つのIssueが小さければ小さいほど参加しやすそうだなと思いました 仕組みが整えば開発だけでなく社内ナレッジに関するドキュメント整備にも役に立ちそうな気がしました。 提案 おそらく、「インナーソースって何?」、「どんなメリットがあるんだろう?」と思っている方が私を含めている気がします。なので、インナーソースの基礎的な知識を共有する場があったら嬉しいです! Fork解放するかどうかの検証をしてほしい ドキュメントとレポジトリの共通化を強く意識したほうがよいと思います。 そのへんが共通であれば、全社感はとても出るので。 参加するメリットや会社的な評価がわからない 長く続けるとソースの管理者がいなくなったときの想定も必要かも。 実際にインナーソースお試しに参加してくれた方は、楽しいと感想をもらえたり、ドキュメントの共通化に関する感想をもらえたりと、インナーソースが普及していくとDeveloper Exprerience向上につながるのではないかと思いました。 評価の話やトラステッドコミッターの引き継ぎの話など、深いところまで言及してくる方もいました。 実際にインナーソースを普及するためにも必要な話になってくるので、徐々に考えていきたいと思います。 3.4.3. アンケート結果からの考察 インナーソースの全社展開に関しては、賛成が多く、反対は0%でした。 コラボレーション向上、透明性向上、技術力向上などのメリットは伝わっているのだと思いました。 ただ時間やリソースの制約が課題になりそうです。 まずは、理解不足の人をなくしていくのと、コミュニケーションの課題などから解決していきたいです。 コミュニケーションの課題については、もっと具体的な課題を質問すればよかったと思いました。 今後考えることは多いと思いますが、需要はありそうなので、全社展開に向けて活動していくことにしました。 4. 後の話はその②の記事に書きます インナーソースのお試し後も社内で普及活動を続け、2023年11月時点では、社内向けインナーソースガイドライン、インナーソースポータルなどを公開し、インナーソースリポジトリが少しづつ増えてきている状態です。 詳しい内容は別記事で紹介する予定なので、気になる方はぜひご覧ください! We are hiring! ニフティでは、さまざまなプロダクトへ挑戦するエンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトよりお気軽にご連絡ください! ニフティ株式会社採用情報 カジュアル面談も受け付けています! カジュアル面談 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering ニフティ株式会社 – connpass
アバター
はじめまして会員システムグループのkiqkiqです。 みなさんはリレーショナルデータベース以外のデータベースについてはご存知でしょうか? データベースの中にはRDB以外にも時系列型やキーバリュー型、カラム指向型などいくつかの種類のデータベースがあります。このブログではこれらのデータベースの中でもグラフデータ型のデータベースについて、SNSなどのソーシャルグラフを題材に、グラフDBに関連する技術やSNSなどで見る基本的な機能の実装を紹介しようと思います。 グラフデータベースについて グラフデータベースはノードとエッジ、その2つに関する属性の3つの要素でノード間の関係性を扱うことに特化したデータベースで、以下の図のようなグラフ構造を扱うデータベースです。 このグラフデータベースは地図などに用いられる道路網の経路探索やSNSなどの友人関係、ECサイトにおける購買情報などを表現するために用いられており、グラフ構造の機械学習モデルであるGNN(グラフニューラルネットワーク)と組み合わせて使用されることもあります。また、グラフデータのデータ分析における基盤としても活用されています。 このグラフデータベースはノードとエッジの集合としてデータを扱うもので、NoSQLの1つに分類されます。 このブログではグラフデータベースの1つのであるNeo4jとそのクエリ言語のCypherを用いて、SNSのよくある機能を実装していきます。 Cypher Neo4jではCypherというクエリ言語が用いられます。 Cypherは宣言型のクエリ言語で、グラフデータベース用のSQLに相当する言語です。CypherではSQLの SELECT に対して MATCH 句を用いてデータの検索を行います。基本的な記述方法は、解のように記述します。 MATCH (識別子:ノードのラベル)-[:エッジのタイプ]-(:ノードのラベル) RETURN 識別子 ノード部分は ( : ノードのラベル ) エッジ部分は [ : エッジのタイプ ] (リレーションシップのタイプ)と記述します。無向グラフは - 、有向グラフは < - 、 -> で接続を表現し、 RETURN 句の後の識別子を返します。識別子は変数のようなもので、ノードやエッジの : の前に宣言して使用することができます。 これに WHERE 句で条件を追加したり、 ORDER BY 句で並び替えることができます。複数の処理を組み合わせるときは WITH 句で識別子を引き継いで、処理を増やすことができます。 あとはノード・エッジの追加や削除には CREATE 、 DELETE 、ノードやエッジに対する属性の登録、更新、削除には SET 、 REMOVE などが基本的なクエリです。 ソーシャルグラフのよくある機能を実装 環境 Python Neo4j FastAPI 実行環境としては、dockerでFastAPIとneo4jのコンテナを立てた環境になります。 共通処理 まずはこれから説明するAPIで共通して必要になる処理について説明します。 主にDBへの接続処理についてですが、この処理を1つのメソッドにまとめています。 from neo4j import GraphDatabase def connection(): uri = "bolt://neo4j_container:7687" driver = GraphDatabase.driver(uri, auth=("neo4j","password")) return (driver) 一点注意としては GraphDatabase . driver で指定するURLは bolt : //コンテナ名:ポート番号 で指定するそうです。docker環境の場合コンテナ名はcompose.yamlの container_name を指定してください。 auth = はcompose.yamlの環境変数でIDとパスワードとして設定しておいたものを指定してください。 また、今回は初期データとして下の図のようなグラフデータを用意しました。これから紹介する各機能はこのグラフデータを基に行なっていきます。 機能1:ユーザーの登録 1つ目のAPIはユーザーの登録機能について説明します。 登録はとてもシンプルで、 CREATE 句でノードを追加するだけで、登録することができます。クエリは下記のようなものになります。 CREATE (:USER{name:$user_name, age:$age}); 今回はノードのプロパティ(ユーザー情報)には名前と年齢の2つのプロパティを定めました。 それぞれのプロパティに対応した $ user_name と $ age はFastAPIでリクエストされた時のパラメータを格納するために使います。 最終的なAPIの実装は下記のようになりました。 @app.get("/add_user") def add_user(user_name: str, age: int): try: query = """ CREATE (:USER{name:$user_name, age:$age}); """ driver = connection() session = driver.session() session.run(query,user_name=user_name,age=age) session.close() return {"ok"} except: return {"error"} session . run で $ user_name と $ age に変数を指定して、それぞれの値でノードを作成します。 これをFastAPIの / docs で実行してみます。30歳のHさんを追加する場合、実行されるクエリは下記のようなクエリになります。 CREATE (:USER{name:"H", age:30}); neo4jの / browser で結果を確認すると下の図のようにノードが追加されていることが確認できます。 機能2:フォロー機能 次に紹介するのはユーザー間でのフォロー機能です。 フォロー機能もノード間のエッジを追加するだけなのでシンプルに実装できます。 基本的には指定したノードを検索し、その2点を接続するためのエッジを CREATE 句で作成するだけで実装できます。クエリとしては、下記のようなものになります。 MATCH(u1:USER{name: $start_user_name}), (u2:USER{name: $end_user_name}) CREATE (u1)-[:FOLLOW]->(u2); API全体の処理はユーザーの登録機能とほとんど変わらないので省略します。 このAPIでHさんがCさんをフォローするようにAPIをリクエストする場合、実行されるクエリは下記のようなクエリになります。 MATCH(u1:USER{name: "H"}), (u2:USER{name: "C"}) CREATE (u1)-[:FOLLOW]->(u2); 結果を確認するとHさんからCさんに向けてエッジが貼られていることがわかります。 機能3:フォロー(フォロワー)一覧表示 次はフォロー(フォロワー)一覧表示機能を説明します。 フォロー(フォロワー)一覧表示では、 MATCH 句でグラフのノードとエッジのラベル、タイプを指定し、 WHERE 句で特定のノードに絞るようなクエリで実現できます。実際のクエリは下記のようなものになります。 フォロー一覧表示 MATCH (u1:USER) -[:FOLLOW]-> (u2:USER) WHERE u1.name= $user_name RETURN u2 フォロワー一覧表示 MATCH (u1:USER) -[:FOLLOW]-> (u2:USER) WHERE u2.name=$user_name RETURN u1 API全体では、下記のようになりました。 @app.get("/search_follower") def search_follower(user_name: str): driver = connection() session = driver.session() query = """ MATCH (u1:USER) -[:FOLLOW]-> (u2:USER) WHERE u2.name=$user_name RETURN u1 """ result = session.run(query,user_name=user_name) user_list = [] for i in result: user_list.append(i['u1']) session.close() return user_list フォロー一覧表示のAPIでAさん指定してリクエストする場合は下記のクエリが実行されます。 MATCH (u1:USER) -[:FOLLOW]-> (u2:USER) WHERE u1.name= "A" RETURN u2 レスポンスとしては以下のデータが返されます。 [ { "name": "C", "age": "40" }, { "name": "E", "age": "60" }, { "name": "F", "age": "70" } ] このようにAさんに対応したノードがエッジを向けている3つのノードが返されているのがわかります。 機能4:フレンドのレコメンド機能(発展) 最後に発展としてSNSなどでよくみる簡単な友達のレコメンド機能をNeo4jのグラフdbで実装してみようと思います。考え方としては「友達から一番接続数(エッジ)の多い友達の友達」を返すようなAPIを実装します。この実装では、クエリがややこしくなってしまうので、これまでと違い 無向グラフ としての実装になります。実装内容に関しては友達と友達の友達間でのエッジの本数を”友達の友達”単位でカウントする形になります。 クエリとしては下記のようになります。 MATCH (n:USER { name: $user_name})-[:FOLLOW*2..2]-(friend_of_friend:USER) WHERE NOT (n:USER { name: $user_name})-[:FOLLOW]-(friend_of_friend:USER) WITH friend_of_friend WHERE NOT friend_of_friend.name = $user_name WITH friend_of_friend MATCH (:USER { name: $user_name})-[:FOLLOW]-(friend:USER) WHERE NOT friend.name = $user_name WITH friend_of_friend,friend MATCH (friend:USER)-[r:FOLLOW]-(friend_of_friend:USER) RETURN friend,friend_of_friend このクエリで注意する点としては、1行目のエッジ [ : FOLLOW* 2..2 ] の部分で、FOLLOWタイプのエッジを深さ2(半径2のエゴセントリックネットワーク)の範囲まで含めるという記述になります。あとは、 WITH 句で必要な識別子を引き継いでいくような記述になります。(もっと簡潔な書き方があるかもしれないです) API全体の実装としては下記のようになります。 @app.get("/recommend_friend") def recommend_friend(user_name: str): driver = connection() session = driver.session() edge_query = """ MATCH (n:USER { name: $user_name})-[:FOLLOW*2..2]-(friend_of_friend:USER) WHERE NOT (n:USER { name: $user_name})-[:FOLLOW]-(friend_of_friend:USER) WITH friend_of_friend WHERE NOT friend_of_friend.name = $user_name WITH friend_of_friend MATCH (:USER { name: $user_name})-[:FOLLOW]-(friend:USER) WHERE NOT friend.name = $user_name WITH friend_of_friend,friend MATCH (friend:USER)-[r:FOLLOW]-(friend_of_friend:USER) RETURN friend,friend_of_friend """ edge_result = session.run(edge_query,user_name=user_name) edge_list = list(set([(i[0]["name"],i[1]["name"]) for i in edge_result])) node_list = [i[1] for i in edge_list] result = collections.Counter(node_list) result = sorted(result.items(), key=lambda x:x[1], reverse=True) session.close() return result このAPIでCさんを指定してリクエストすると以下のレスポンスが返されます。 [ [ "E", 2 ], [ "D", 1 ], [ "F", 1 ] ] レスポンスの内容としては友達からの接続数とそのノードを表しており、Eさんが2人の友たちとも接点があるという意味を示しています。そのためCさんにはEさんをレコメンドするのが最適だとわかります。 まとめ グラフデータベースであるNeo4jを用いてSNSのよくある機能の実装を紹介しました。グラフデータはRDBに比べて直感的で分かりやすいので、アイデアがあればさまざまな分野に応用できると思います。また、このブログではグラフデータベースのNeo4jを紹介しましたが、グラフデータベース以外にもキーバリュー型のデータベースであるRedisや時系列のデータベースであるInfluxDBなどもあるので、興味がある方は調べて見てください。 We are hiring! ニフティでは、さまざまなプロダクトへ挑戦するエンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトよりお気軽にご連絡ください! ニフティ株式会社採用情報 カジュアル面談も受け付けています! カジュアル面談 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering ニフティ株式会社 – connpass
アバター
はじめましての方は始めまして!ニフティ株式会社の仲上です。 この記事は先日参加したSRE NEXT 2023のことについてレポートです。 SRE NEXT 2023とは SRE(に限らず信頼性を向上させるための)活動をしている方が集まって、意見を交換する場です。 公式ページには以下のように書いてありました。 信頼性に関するプラクティスに深い関心を持つエンジニアのためのカンファレンスです。 同じくコミュニティベースのSRE勉強会である 「SRE Lounge」 のメンバーが中心となり運営・開催されます。 SRE NEXT 2023は「Interactivity」「Diversity」「Empathy」という3つの価値観を掲げ、「双方向性のある意見交換の場にすること」「スタートアップから大企業まで、幅広い業種・領域・フェーズでのSRE Practice の実践を集約すること」「ビジネスサイド含めSRE以外の職責も含めて裾野を広げること」を意識して運営していき、より多様なSREの実践が普及することを目指します。 SRE NEXT 2023 HOME画面(https://sre-next.dev/2023/) セッションの他に、ゴールドスポンサー以上のブース出展もあります。ニフティもゴールドスポンサーとして参加したので、ブースを出展していました。 現地の様子 会場は九段会館テラスで、とても立派な建物でした。 ジョブボードもあり、会社アピールの場として活用されていました。 ニフティの紹介は右下のココ!SRE募集中です! ジョブボード書きました。 カジュアル面談の応募お願いします! #srenext pic.twitter.com/zDAjQMOmQA — NIFTY Developers (@NIFTYDevelopers) September 29, 2023 スペースが足りなくなったのか、途中から2枚目が追加されていました。 こちらは参加への調査ボードです。SREのイベントだけあって、SRE本読んでいる人が多いですね。 SRE本に次いで読まれているのは 入門 監視 。私もおすすめの1冊です。 ブース紹介 ニフティ ノベルティとしてエコバッグとどら焼きを配ってました。(どらやき美味しかったです。) プロモーション映像として、ニフティの光0円CMや去年のSRE NEXT登壇映像、TechTalkの映像などを流していました。 SRE NEXT 2023 ニフティブースです。 Room F(306/柳)でお待ちしております。 どらやきがたくさんあります!!! #srenext pic.twitter.com/NI0Y0pe1q2 — NIFTY Developers (@NIFTYDevelopers) September 29, 2023 どら焼きは会場入口でもお配りしていたので、入場者のほぼ全員に渡せたと思います笑 ワキヤコーヒーさんにどらやき置いてもらいました! ありがとうございます!! ニフティブースにもお越しください! #srenext pic.twitter.com/3rMhzHmR38 — NIFTY Developers (@NIFTYDevelopers) September 29, 2023 TechTalkの過去回やSRE NEXTの登壇映像はこちらから見ることができます! 私も当日スタッフとして参加しており、ブースにいらっしゃった方とお話させていただきました。 現地ではスタンプラリーを行っていたので、それをきっかけにブースに来ていただいた方が多かった印象です。 SREをされている方が多く、皆さん自分より経験豊富な方々でした。「SREとバックエンド」や「SREとインフラ」のように兼業している方が多かった印象です。 new relicさん 監視のSaaSサービスを提供している会社です。会社の雰囲気や提供しているサービスについて紹介してもらいました。 ニフティのブースにもnew relicの方が何人か見に来てくれました。 DMM.comさん おそらく名前を知らない人はいないであろう大手コンテンツ配信会社。 会社の紹介や、使用しているインフラサービスなどについて教えてもらいました。 ニフティと同じくマルチクラウドを実践しているので、その上でのメリットや悩みなどについてお聞きしました。 セッション紹介 ここからはSRE NEXTで自分が見たセッションを何個か紹介しようと思います。 ギークがイオンに飛び込んだ結果がやばい〜Reliabilityと経営〜 最初の基調講演で、イオンネクスト株式会社の樽石さんが話されていました。 マイクロ・カンパニーズ・アーキテクチャという考え方を話しており、組織論的な観点でとても興味深かったです。 ソースコードが見れる環境になるまで数週間かかったそうです。 ニフティは自社開発の企業なのでこういった問題に遭遇することは少ないですが、外注していたり企業が巨大だったりするとこういった問題も出てきて大変そうです。 マルチプロダクト運用におけるSREのあり方 1つの会社で複数のプロダクトを運用するにあたり、実践していることや気付きについてのセッションです。発表者はリンクアンドモチベーションの 岸本さんと篠原さんの2名でした。 共通のterraformモジュールを作成して、全社的にそのモジュールを使用してインフラを作るそうです。ニフティでは1つのチームで複数のプロダクトを持っていることが多いので、この構成は非常に参考になりました。 マルチプロダクトになるとスイッチングコストが増加することについても話されていました。リンクアンドモチベーション社では、作業時間を増加させ、できるだけスイッチングコストを減らす取り組みをしていました。 まとまった時間を取ったほうが集中できますよね。最近読んだ「人月の神話」にも似た話が書かれてました。 信頼性目標とシステムアーキテクチャー このイベント最後の講演です。Googleの山口さんの発表でした。 こちらは特に画像などは撮っていないのですが、「 信頼性は会話です 」という言葉が印象的でした。「手が止まったときは、一旦紙とペンを持ち出そう」という言葉は、自分の経験上もそのとおりだと感じました。 r9y.dev というロードマップも公開されており、SLI/SLOを決める上で参考になりそうでした。 まとめ 今回はSRE NEXTに参加してのレポートでした。他社で行っているSRE活動についてはなかなか知る機会がないので、今回活動内容を聞けてよかったです。何点か自分のチームにも活かせそうな活動もあったので、今後のSRE活動の参考にしようと思いました。(共通の基盤を作ったり、作業の手が止まったら、紙とペンを持ち出してみたり…) また、私個人としては入社して初めてのオフラインイベントだったので、とても新鮮な気持ちで参加できました。イベントを開いてくれた運営の皆さん、そして業務としてイベントに参加させていただいた会社の方には感謝の気持ちでいっぱいです。 来年以降も開催に前向きだったので、また参加してみたいと思いました。今回は本当にありがとうございました。 We are hiring! ニフティでは、さまざまなプロダクトへ挑戦するエンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトよりお気軽にご連絡ください! ニフティ株式会社採用情報 カジュアル面談も受け付けています! カジュアル面談 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering ニフティ株式会社 – connpass
アバター
ニフティのN1! Machine Learning Product Engineer 中村です。 最近はAmazon Bedrockを活用した生成AIをプロダクト実装することにハマってます。 NIFTY Tech Book #1を無料配布します! 完成したNIFTY Tech Book #1 2023年11月12日に池袋サンシャインシティ 展示ホールDの技術書典15でNIFTY Tech Book #1という、有志のエンジニアで集まって自分達が書きたいことを自由に書いた技術書を出版します! https://techbookfest.org/product/e8er3JPEd6kgUAtLjPkbjy 企業が書いた技術書というと、エンジニアの広報目的だったりするのではないか?と思いがちですが、今回のNIFTY Tech Book #1は完全に有志で集まって自由に好きなことを書きました! 執筆の経緯 広報目的ではないといいつつ、実はNIFTY Tech Day 2023というイベントが、直後の2023年11月18日に開催されます。 https://techday.nifty.co.jp/2023/ このイベントはオンライン/オフラインのハイブリッドで行うのですが、どうせならオフラインで来ていただいた方に満足感を持って欲しいなと思った時に「書籍をプレゼントする」ということを思いつきました。 そして、どうせなら既製品ではなく、ニフティのエンジニア自身で書いたオリジナルの書籍を用意したいと考え、今回のNIFTY Tech Book #1につながります。幸いにも快く受け入れてくれたエンジニアがたくさんおり、120ページの大ボリュームの作品に仕上がりました。 内容 本当に「自由に書いてもいいよ」というオーダーでエンジニアのみなさんに書いてもらったので、本当に自由な内容です。実際の内容については、(無料ですので)本書を読んでいただくことにして、ここでは簡単に編集長の自分から紹介します。 1章:新人に1日でWEB技術の全体像を教える技術 新人エンジニアの教育や、新しい人のオンボーディングって大変ではないですか? ニフティではエンジニア定例と呼ばれる初期研修を通して、新人エンジニアに基礎スキルをつけてもらうことになっていますが、その時にどのような思想や設計を行うことで、WEB技術の”全て”を教えているかを説明します。 2章:EchoへのOpenTelemetry導入をローカル環境で試してみる SREが叫ばれるようになり、システムで何が起きているかを理解することは重要になりました。 OpenTelemetryを使うことでシステム内部の挙動を観測できるようになりますが、実際にどのような使い方をするのかをローカル環境で試していきます。この章を片手に実際にやってみよう。 3章:この本の表紙ができるまで NIFTY Tech Book #1はこれまでに刊行されてこなかった新しいニフティのエンジニアの書籍です。そのために0から探り探りで実行することになりました。 どのように表紙が出来上がったのか?最後の最後まで大変だった書籍表紙はどのように完成したのかをお送りします。 4章:ランニングを続ける技術 運動したいな〜と思いながら、運動できてない日々が続いていませんか?リモートワークで運動不足気味ではないですか? この章ではランニングを続けてきたコツを説明しながら、ちょっと嫌だけどやらなければいけないことをどのようにすればコツコツを続けられるのかを解説していきます。 5章:Redis・go-redis速習 インメモリデータストアとして人気なRedis。 RedisとGo言語を組み合わせた時にどのように使えばいいのかを、実際にプロダクション環境にも実装したエンジニアが丁寧に解説していきます。これを読めば明日からGoとRedisを使いたくなるはず。 6章:QRコードをC言語標準ライブラリだけで作ってみよう 車輪の再開発?そんなことは知らん。 QRコードって何気なくスマートフォンでスキャンしているけれど、実際にはどういう仕組みになっているんだろう?その仕組みを調べて実際に実装まで行ってみた記録を、新卒1年目エンジニアコンビがお届けします。 7章:QRコード生成をRustで書いていたとしたら? 新卒1年目エンジニアコンビは更なる高みを目指します。 C言語の辛さをRustはどのように乗り越えているのか?Rustだったらこうやって書けるのにな〜という思考を辿ることで、Rustの真の理解に到達できるはず。 8章:GitHubのIssueを自動でProjectに追加する方法3選 GitHubでタスクを管理しているユーザーは必見!細かいTipsはなかなかネット上には転がっていない! GitHubの細かいながらも有益なTipsを、オートメーションのスペシャリストがお届けします。 ぜひ会場でお会いしましょう! 技術書典15は2023年11月12日 池袋サンシャインシティ 展示ホールDでオフライン開催されます。 NIFTY Tech Book #1は電子版も無料で出版予定ですが、ぜひ会場で紙でできた本を受け取っていただきたいです。ニフティのエンジニアの魅力がたくさん詰まった本書籍、よろしくお願いします! https://techbookfest.org/product/e8er3JPEd6kgUAtLjPkbjy さらに、NIFTY Tech Day 2023も開催予定です!こちらもぜひよろしくお願いします! (オフラインにたくさん人が来てくれると楽しいです) https://techday.nifty.co.jp/2023/ We are hiring! ニフティでは、さまざまなプロダクトへ挑戦するエンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトよりお気軽にご連絡ください! ニフティ株式会社採用情報 カジュアル面談も常時受け付けています! カジュアル面談 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering ニフティ株式会社 – connpass
アバター
AWS コミュニティビルダーを知っていますか AWSには技術コミュニティでの活動や知識の共有を熱心に行うエンジニア向けのプログラムがあります。 応募者の中から審査で合格者が選ばれプログラムに参加できる仕組みになっています。 詳しくは公式サイト https://aws.amazon.com/jp/developer/community/community-builders/ をご覧ください。 当社エンジニアがコミュニティビルダーに選出 この度、ニフティのN1! エンジニア(SRE)の浅見がコミュニティビルダーに選出されました。ニフティ内では初期からAWSを利用しており、オンプレからAWSへのシステム移行、社内でのAWS教育、社内AWS Game Dayの実施、re:Inventへの参加など、ニフティでのAWSの活用に対して多大な貢献をしているエンジニアです。 AWS Community Builders Directory 意気込みを聞いてみました とにかく良質なアウトプットを増やすことを意識していきます。 また、新しいイベント開催などしていこうと思います。 益々の活躍を期待しています! インタビュー記事もあります 浅見はSREチームのリーダーとして活躍中です。どのような活動をしているか、SREチームのインタビュー記事で紹介しておりますのでぜひご覧ください。 【インタビュー】ニフティのSREに聞く!インターネット黎明期から安心・安全を体現してきたニフティが2023年に取り組んでいるSREとは?【SRE前編】 【インタビュー】ニフティのSREはどんな人?【SRE後編】 We are hiring! ニフティでは、さまざまなプロダクトへ挑戦するエンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトよりお気軽にご連絡ください! ニフティ株式会社採用情報 カジュアル面談も常時受け付けています! カジュアル面談 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering ニフティ株式会社 – connpass
アバター
はじめに こんにちは、基幹システムグループの瀧山です。 この記事では、初心者エンジニアにおすすめの本を紹介します。 今回紹介する本で得られる知識は、特定のプログラミング言語によらない知識であるため、どのような会社であっても使う機会が多いです。 対象読者 少なくとも1つのプログラミング言語について、基本的なプログラミングができる方(if文やfor文が使える) プログラミング自体やったことがない場合は、まずは何かしらのプログラミング言語に関するやさしい入門書を1冊読みましょう。 Javaの場合は 「スッキリわかるJava入門」 がおすすめです。 プロになるためのWeb技術入門 ――なぜ、あなたはWebシステムを開発できないのか 1冊目は「プロになるためのWeb技術入門 ――なぜ、あなたはWebシステムを開発できないのか」です。 現在のシステム開発は、Webアプリケーションと呼ばれるものが主流になっています。 Webアプリの開発には、サーバー、データベース、HTTP、GET/POST、Cookie、セッション、セキュリティ等々、様々な知識が必要になります。 Webフレームワークを使用した開発では、このあたりの知識がなくても開発できてしまったりもしますが、裏側の仕組みがわかっていないと壁に突き当たることも多く、仕組みを理解することは大切です。 この本は2010年出版と古い本ですが、Web技術の根幹は当時から変わっておらず、一通り学ぶことができます。 また、Web黎明期からどのような技術の歴史があったかを知ることができ、読み物としても面白いです。 古い本ながら、WebフレームワークやO/Rマッパーといった内容にも触れられています。 Web技術の全体像を把握するという意味で、とてもおすすめの本です。 SQL ゼロからはじめるデータベース操作 2冊目は、「SQL ゼロからはじめるデータベース操作」です。 システムにはデータベース(DB)というものがあり、そこに様々なデータを蓄積していきます。 その中でもリレーショナルデータベース(RDB)というDBが広く用いられており、SQLはRDBを操作するために必要な言語です。 どのようなプログラミング言語でシステムを開発するとしても、SQLはかなりの確率で使うことになります。 この本はSQLの構文が一通り網羅されており、サブクエリやウィンドウ関数などの少し難しい構文も記載されています。 DBの製品による違いや豆知識が書かれているのも参考になります。 まずは一通り目を通して、実際にSQLを書くときには、辞書的に使うのがおすすめです。 新しいLinuxの教科書 3冊目は、「新しいLinuxの教科書」です。 企業のシステムは、サーバーというコンピューターの中で動いています。 サーバーのOSは、LinuxというOSが広く用いられています。 Linuxは、WindowsやMacのようなアイコンやウィンドウが表示された画面(GUI)ではなく、CLIと呼ばれる、映画などでハッカーが使っているような「黒い画面」でコマンドという文字列を打ち込んで操作する必要があります。 (注:LinuxでもGUIを使用することはできますが、サーバー操作にはCLIを使用することが多いです。) この本ではLinuxのコマンド操作が詳しく解説されています。 最初はコマンド操作には慣れないと思いますが、少しずつ覚えていきましょう。 Linuxでよく使われる「Vim」「正規表現」や、コマンド操作の自動化ができる「シェルスクリプト」の作成方法も載っていて、この1冊でLinuxの操作方法はかなり押さえることができ、まさに「Linuxの教科書」といった内容になっています。 おわりに 今回は、プログラミング以外のIT技術書を3冊ご紹介しました。 今回ご紹介したように、エンジニアになるにはプログラミング以外の知識も必要になってきます。 覚えることが多くて大変に感じるかもしれませんが、プログラミングも含めて幅広く知識を習得していきましょう。 We are hiring! ニフティでは、さまざまなプロダクトへ挑戦するエンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトよりお気軽にご連絡ください! ニフティ株式会社採用情報 カジュアル面談も受け付けています! カジュアル面談 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering ニフティ株式会社 – connpass
アバター
はじめに こんにちは、新卒一年目の平野です。今回は練習のためにタイトル通りの機能をAWS上に構築してみました。 Amazon S3 に置かれたファイルを比較的安全に共有します。 TerraformとPythonを使って実装しました。 背景 ニフティでは実弾演習場と呼ばれるAWSの練習用アカウントが存在します。これを使ってAWSに慣れてみようということで本機能を作成しました。 構成 Amazon S3上にファイルが置かれたら、 Amazon EventBridge がそれを検知し、 AWS Lambda を発火させます。 アーキテクチャ図は以下のようになっています。 ディレクトリ構成は以下のようになりました。 . ├── main.tf ├── send_url.py ├── send_url.zip 以下main.tfのコードです。 provider "aws" { region = "ap-northeast-1" # 東京リージョン profile = "send-s3-url" # ~/.aws/credentials } resource "aws_s3_bucket" "my_bucket" { bucket = "任意のユニークなバケット名" # S3バケットの名前 } resource "aws_lambda_function" "send_url" { function_name = "SendUrl" # Lambda関数の名前 handler = "send_url.lambda_handler" # ハンドラーメソッド runtime = "python3.8" # ランタイム role = aws_iam_role.lambda_role.arn # Lambda実行ロールのARN timeout = 5 # タイムアウト時間(秒) filename = "send_url.zip" # ZIPファイルに圧縮されたLambda関数のコード } resource "aws_iam_role" "lambda_role" { name = "LambdaS3ExecutionRole" # Lambda実行ロールの名前 assume_role_policy = jsonencode({ Version = "2012-10-17", Statement = [ { Action = "sts:AssumeRole", Principal = { Service = "lambda.amazonaws.com" }, Effect = "Allow", Sid = "" } ] }) } # Lambda関数に関連付けるIAMロールポリシーの作成 resource "aws_iam_role_policy" "lambda_policy" { name = "LambdaS3Policy" # ロールポリシーの名前 role = aws_iam_role.lambda_role.id policy = jsonencode({ Version = "2012-10-17", Statement = [ { Action = [ "s3:GetObject", # S3オブジェクトの取得アクション ], Effect = "Allow", Resource = [ "arn:aws:s3:::send-s3-url-bucket/*" # 特定のS3バケットへのアクセス許可 ] }, { Action = [ "logs:CreateLogGroup", # CloudWatch Logsのロググループ作成 "logs:CreateLogStream", # CloudWatch Logsのログストリーム作成 "logs:PutLogEvents", # CloudWatch Logsへのログイベントの送信 "ssm:GetParameter" # Systems Managerのパラメータの取得 ], Effect = "Allow", Resource = "*" # リソースの許可(全てのリソースに対する許可) } ] }) } # S3バケット通知の設定 resource "aws_s3_bucket_notification" "bucket_notification" { bucket = aws_s3_bucket.my_bucket.bucket # 通知を設定するS3バケットの指定 # Lambda関数のトリガーイベントを設定 lambda_function { lambda_function_arn = aws_lambda_function.send_url.arn # トリガーとして使用するLambda関数のARN events = ["s3:ObjectCreated:*"] # トリガーとなるイベント(S3オブジェクト作成時) } } resource "aws_lambda_permission" "allow_bucket" { statement_id = "AllowS3BucketNotification" action = "lambda:InvokeFunction" function_name = aws_lambda_function.send_url.function_name # アクセス許可を付与するLambda関数の名前 principal = "s3.amazonaws.com" source_arn = aws_s3_bucket.my_bucket.arn # アクセス許可を付与するS3バケットのARN } send_url.pyのコードです。 import boto3 import json from urllib.request import Request, urlopen def lambda_handler(event, context): # SSMクライアントを作成 ssm_client = boto3.client("ssm") # SSM Parameter StoreからSlack Webhook URLを取得 response = ssm_client.get_parameter( Name="/webhook/url/slack/dm", WithDecryption=True ) webhook_url = response["Parameter"]["Value"] # イベントからS3バケットとオブジェクトキーを取得 bucket_name = event["Records"][0]["s3"]["bucket"]["name"] object_key = event["Records"][0]["s3"]["object"]["key"] # S3クライアントを作成 s3_client = boto3.client("s3") # 署名付きURLを生成 signed_url = generate_signed_url(s3_client, bucket_name, object_key) # slackに通知 post_slack(signed_url, webhook_url, object_key) return {"statusCode": 200, "body": "Notification sent to Slack"} def generate_signed_url(s3_client, bucket_name, object_key, expiration=3600): url = s3_client.generate_presigned_url( "get_object", Params={"Bucket": bucket_name, "Key": object_key}, ExpiresIn=expiration, ) return url def post_slack(message: str, webhook_url: str, object_key: str): headers = { "Content-Type": "application/json", "Content-Disposition": f'attachment; filename="{object_key}"', } data = {"text": message} request = Request( webhook_url, data=json.dumps(data).encode(), headers=headers, ) urlopen(request) 下準備 .gitignoreの追加 githubでコードを管理する場合、.gitignoreファイルを他のファイルと同じ階層に作成し、 **/.terraform/* を記入。 Pythonファイルを圧縮 Pythonのファイルをzip圧縮。windowsの場合、zipコマンドをインストールするかファイルを右クリックして圧縮を行なってください。 zip send_url.zip send_url.py AWSアクセスキーとシークレットアクセスキーの取得 AWSアクセスキーとシークレットアクセスキー外部に流出しないように大切に保管してください。 AWS Management Consoleにサインイン。 サービスメニューから「IAM(Identity and Access Management)」を選択。 左側のメニューから「ユーザー」を選択し、「ユーザーの追加」をクリック。 ユーザーに適切なアクセス権を付与し、アクセスキーとシークレットアクセスキーを生成。 Terraformで読み込むクレデンシャルの設定 ~/.aws/credentialsに以下のように記述。 [send-s3-url] aws_access_key_id = アクセスキーID aws_secret_access_key = シークレットアクセスキー AWSのパラメータストアにSlackのwebhook URLを格納 Slackのwebhook URLを取得したら、 AWS Management Consoleにサインイン。 Systems Managerパラメータストアに移動。 左側のメニューから「パラメータストア」を選択し、「パラメータの作成」をクリック。 Nameを”/webhook/url/slack/dm”とし、Valueにwebhook URLを入力してパラメータを作成。 構築 terraform plan terraform apply を実行してデプロイ。 動作確認 コンソールからAmazon S3にtest.txtをアップロード。 するとSlackに長い署名付きURLの通知が来ました。 アクセスすると、 アップロードしたファイルを見ることができました。 めでたしめでたし。 おわりに 今回は実弾演習場を使ってAWS LambdaからAmazon S3に置かれたファイルの署名付きURLをslackに通知しました。自分で作ると理解が深まりますね。 改良すれば運用を自動化したりなど夢が広がります。 We are hiring! ニフティでは、さまざまなプロダクトへ挑戦するエンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトよりお気軽にご連絡ください! ニフティ株式会社採用情報 カジュアル面談も受け付けています! カジュアル面談 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering ニフティ株式会社 – connpass
アバター