UP.tech.blog

UP Parters GroupのSOT consulting IT事業部のTechブログです

指示を与えてゲームを作れ! Amazon Q CLIを使ってwordleを作成

こんにちは。開発チームの松下です。現在、Amazon Q CLI でゲームを作ろう Tシャツキャンペーン というキャンペーンが開催されています。今回このキャンペーンに参加し、Amazon Q CLIでゲームを作成しました。この記事では、作成したゲームや作成手順、作成した感想をお伝えします。

aws.amazon.com

ゲーム作成

ゲームを作成するにあたってどんなゲームを作成するかを決める必要があるのですが、最近はwordle*1というゲームに個人的にハマっていたので、これをアレンジしたゲームを作成しました。 どんなアレンジを加えるかは考えていなかったので、それをチャットに質問しました。

> Webのゲームを作ります。wordleのようなゲームです。ただ、まったく同じゲームでは面白くありません。どんなアレンジをするといいでしょうか? アイデアをください。

すると、こんな回答が返ってきました。

Wordleのようなゲームをアレンジするアイデアをいくつか提案します。オリジナルの要素を活かしながら、新しい魅力を加えることで差別化できるでしょう:

1. テーマ別Wordle
   • 特定のジャンル(映画、音楽、料理など)に関連する単語だけを使用
   • 日本語版(ひらがな、カタカナ、漢字の組み合わせ)

2. ゲームメカニクス変更
   • 時間制限の追加(緊張感アップ)
   • 複数の単語を同時に解く(難易度アップ)
   • 文字数が変化する(例:最初は3文字、徐々に増える)
   • ヒントシステム(ただし使用するとスコアが下がる)

3. マルチプレイヤー要素
   • 対戦モード(同じ単語を早く当てた方が勝ち)
   • 協力モード(プレイヤー同士でヒントを出し合う)
   • 日替わりチャレンジ(全プレイヤーが同じ単語に挑戦、ランキング表示)

4. 視覚・聴覚要素の追加
   • 音声ヒント(単語に関連する音)
   • 画像ヒント(単語に関連する画像が徐々に明らかに)
   • アニメーションエフェクト(正解時の派手な演出)

5. 学習要素の追加
   • 正解後に単語の意味や豆知識を表示
   • 難易度別の単語セット(初心者から上級者まで)
   • 語彙力向上のための統計やアチーブメント

どのような方向性に興味がありますか?特定のアイデアについてさらに詳しく知りたい場合は、お知らせください。

Amazon Qが提案してくれた「正解後に単語の意味や豆知識を表示」という案が気にいったので、これを採用することにしました。

> 英語が学習できるといいですね。回答したら、その単語にリンクが表示され、英和辞典のページに飛ぶようにしましょう。一旦そんなゲームをWebで作ってください。

このようなやりとりをチャット形式で進めます。Amazon Qがコードを書いてくれて、ゲームができあがります。たまにレイアウトが崩れたりするので、適宜修正もチャットで指示します。

最終的には以下のようなゲームができあがりました。

  • 単語リストはマシンにあった /usr/share/dict/words を使用
  • 本当に正解がわからなくなったときのために、ギブアップボタンを用意
  • 回答が終わったら、その単語を調べるためのボタン・もう一度プレイするボタンを表示
  • 単語を調べるためのボタンを押すと、新しいタブで英和辞典のページを開く

正解は「zayin*2」という謎単語だった

英単語リストに収録されている単語は難しいものが多いみたいなので、結果的に本家wordleより難易度は高めになってしまいました。

まとめ

今回はAmazon Q CLIを使ってゲームを作成しました。相手はAIなので、単にコーディングだけではなく、アイデア出しなどいろんなことをやってくれます。コードを一切書かず、チャットであれこれ指示や相談をしながら無料枠内でゲームが作れたのは楽しかったです。

Web開発者のためのFlutter入門

はじめに

こんにちは。UP Parters Group IT事業部開発チームの松下です。

開発チームでは定期的に勉強会を行なっております。 弊チームの勉強会では記事を紹介したり、参加した社外の勉強会の内容を共有したりすることで、知識を共有しています。

先日の勉強会ではFlutterの概要を紹介する発表を行ないました。 近年クロスプラットフォーム開発フレームワークとして注目を集めているFlutterですが、弊チームでは主にWeb開発が中心のためアプリ開発の経験はありません。 しかし、今後受託案件などでアプリ開発の知識が必要になる可能性を視野に入れ、予備知識としてFlutterの概要を共有することにしました。

本記事では、弊チームでよく採用しているReactとの違いをまじえながら、勉強会で話した内容をまとめて共有しようと思います。

特徴

Flutterは、Googleが開発したオープンソースのUIフレームワークです。 一つのコードベースからiOSAndroid、Web、デスクトップアプリケーションを開発できる点が最大の特徴です。 Dartという言語でアプリケーションを構築します。

また、Reactと同様に、コードの変更をすぐに確認できるホットリロード機能が搭載されています。 UIの微調整やバグ修正がリアルタイムで反映されるため、開発サイクルが大幅に短縮されます。

ReactではUIのパーツのことを「コンポーネント」と呼びますが、Flutterでは「ウィジェット」と呼びます。 ひとつのウィジェットはクラスとして定義され、利用側ではそれをコンストラクタで呼び出し、ネストさせてツリー状に組み立てることでUIを実装します。 例えばカウンターウィジェットは以下のように書くことができます。

class Counter extends StatefulWidget {
  const Counter({super.key});

  @override
  State<Counter> createState() => _CounterState();
}

class _CounterState extends State<Counter> {
  var count = 0;

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisSize: MainAxisSize.min,
      children: [
        Text('Count: $count'),
        FilledButton(
          onPressed: () => setState(() => count++),
          child: const Text('+1'),
        ),
      ],
    );
  }
}

このカウンターウィジェットは別の「Column」「Text」「FilledButton」ウィジェットを組み合わせて実装されているのがわかるかと思います。 Webで実行してみると、こんな感じで動きます。

状態管理

フロントエンドにおいて、状態管理はアプリケーションの規模や複雑さが増すにつれて非常に重要になるトピックです。 Flutterにおける状態管理では、様々な方法が用意されています。 標準的な状態管理であるsetStateや、React学習者のためにuseStateuseEffectと似た書き方ができるflutter_hooksというパッケージも公式で用意されています。 上で紹介したコードでは、setStateを使ってcount変数を更新しています。

以下はuseStateとuseEffectを使った例ですが、他にもたくさんのフックが存在します。

const age = useState(25);

useEffect(() {
  print("再描画時に呼び出される");
  return () => print("dispose時に呼び出される");
}, [deps]);

Flutter Web

Flutterで実装したUIは、Webページとしてビルドすることもできます。 単一のコードベースからWebアプリも開発できるのは大きな利点です。

ただし、初回読み込みが遅いなど、完全にWebをFlutter Webで置き換えてよいという段階には至っていません。 Web FAQでは「現時点では、Flutterはテキストリッチなフローベースのコンテンツを含む静的ウェブサイトには適していない」とあります。 インタラクティブな体験が必要な場合にはFlutter Webが良い選択肢になります。

まとめ

Flutterは多くの可能性を秘めたフレームワークであり、特にクロスプラットフォーム開発において強力なツールです。 Web開発者にとっては、モバイルアプリ開発への入口としても、あるいはFlutter Webを通じて新しいアプローチでのWeb開発の選択肢としても興味深いものです。

今後の技術動向を見据え、知識の幅を広げる意味でも、Flutterの基本的な知識を持っておくことは有益であると考えます。

Rails7.1アップグレード対応で必要になった変更点のまとめ

以前こちらの記事で紹介した、Railsのアップグレード作業で、Railsのデフォルトの挙動が変わったことにより対応が必要になりました。 本記事ではその中から、実際に対応したものをいくつかピックアップしてご紹介します。

config.enable_reloading

Rails7.1にて追加された設定で、Railsアプリケーションのファイルが変更されたとき、クラスやモジュールを自動的に再読み込みするかどうかの設定です。 開発環境ではtrue、運用環境ではfalseが良いと思われます。

# Rails7.0 : 以下のように記述していました
config.cache_classes = false
config.eager_load = false

# Rails7.1 : 等価ではないが上記とほぼ同じ挙動となる
config.enable_reloading = true

config.action_controller.raise_on_missing_callback_actions

こちらもRails7.1で導入された設定です。コールバックで指定されたアクションがコントローラーに存在しないときに例外を発生させるかどうかの設定です。 デフォルト値は開発環境ではtrue、運用環境ではfalseです。 実装漏れを防止する意味で有効なのですが、concernで下記のようにexcept:を使っている場合は、対応しづらいです。

included do
  after_action :set_token, except: [:sign_out]
end

config.action_dispatch.show_exceptions

例外発生時の挙動を設定するもので、truefalseに加えて:rescuableが指定可能となりました。 これはテスト環境で例外が発生したとき、rescue_fromが有効になる設定で、運用環境での挙動に近くなります。 特にテスト環境での request spec における例外処理の検証に便利です。

config.autoload_lib

Zeitwerk(ツァイトヴェルク)のオートロードに関する設定です。

lib = root.join("lib")
config.autoload_paths << lib
config.eager_load_paths << lib

こんな感じでautoload_pathsやeager_load_pathsに手作業でパスを追加していたのを、設定なしにlib配下についてオートロードしてくれます。 除外するパスを指定できるので、不要なファイルを読み込まないようにもできます。

# lib/assets、lib/tasksは読み込まれない
config.autoload_lib(ignore: %w(assets tasks))

lib配下に置くモジュールは、ファイル名とクラス名についてZeitwerkのローディングルールに従っておく必要があります。 ルールに従わない(従えない)場合は除外をおこなって、手動でrequireをします。

ActiveRecordのbelongs_to

belongs_toの関連はデフォルトでNULL許容しないようになりました。 なので、belongs_toの関連先でNULLがあり得る場合は明示的にoptional: trueが必要になります。

# 関連先がなくてもOKにする場合
belongs_to :user, optional: true

ActiveRecordの型安全性向上

Rails7.1 では、モデルの属性に代入された値が、より厳密に型変換されるようになりました。 たとえば、整数型のカラムに小数を代入しようとすると、自動的に整数に変換されます。

# schema.rb
create_table "users" do |t|
  t.integer "age"
end
user = User.new(age: 25.7)
user.age
# => 25

ActiveRecordのクエリで集計を行った結果について、後段で調整をかける処理があったのですが、今まで通っていたテストが通らなくなりました。 これも上記の影響だったのですが、データベースから返されるカラムの型に変換されるような動きとなっていました。 対処としては、クエリ自体を修正し、例えば小数として取得するようキャストを行うなど、取得時点で意図した型に整える対応が必要になります。

まとめ

Rails7.1 では、開発体験や安全性向上のために多くの改善が導入されましたが、それに伴いこれまで動いていたコードが動かなくなるケースもあります。 アップグレードの際は、公式のリリースノートや変更点のドキュメントに目を通すのはもちろん、自分たちのプロジェクトでどのような影響が出るかを丁寧に確認することが大切です。

CursorでRuby on Railsのアップグレードを試す

IT事業部の池谷です。 Ruby on Railsのアップグレード作業は、多くの開発者にとって慎重を要するプロセスと思います。 Cursorのエージェントでどこまで自動化できるか確認したかったので、Ruby on Railsのアップグレードにチャレンジしました。 本記事では、Cursorを使ってアップグレード作業を行った際に、発生した問題と改善ポイントを紹介します。

最小限のプロンプトで進めたら

Rails 7.0 から 7.1 にアップグレードして」とだけCursorに指示したところ、いくつかの問題が発生しました。

主な問題点

  • アップグレード作業の全体像が見えないまま作業が始まった
  • Gitブランチを切らずに作業を始めてしまった
  • Docker環境なのに、ローカル前提のコマンド操作が提案された
  • rails app:update 実行時に、各ファイルの上書き確認で処理が進まず、ツールのタイムアウトとなった

反省点を踏まえてプロンプトを具体化

この経験をふまえ、次のチャレンジでは 環境や方針をプロンプトに明確に記述したところ、Cursorの動作が改善しました。

伝えたポイントと改善内容

1. アップグレード計画を提示すること

Railsのリリースノートおよびアップグレードガイドを参照して、アップグレード計画を立てること」をプロンプトに追加したところ、フェーズ分けした計画が提示されるようになりました。 それに沿って進めようとするので現在位置の把握がしやすくなりました。またリリースノートやアップグレードガイドを読ませることで、考慮すべきポイントが計画に入るようになりました。

2. Gitブランチを作って作業すること

「作業用のGitブランチを作成すること」をプロンプトに追加したところ、最初にブランチを作ることを前提とした手順が提示され、差分確認や巻き戻しが容易になりました。

3. 開発環境がDockerであること

「開発環境はDockerで構築しているので、コンテナに対してコマンド実行すること」をプロンプトに追加したところ、docker compose exec を前提としたコマンドが提示され、実行可能な手順が得られました。

4. 設定ファイルは新しい内容で上書きすること

rails app:updateでは、上書き確認に対してはすべて上書きで処理すること」をプロンプトに追加したところ、一括で上書きする方針に沿った操作方法が提示され、作業がスムーズになりました。 差分はGitで確認することになります。

まとめ

Cursorは非常に優れた開発支援ツールですが、その能力を最大限に活かすには、過去の失敗まで含めて状況を正しく伝えることが重要です。 .cursor/rulesに環境や方針をあらかじめ記述しておけば、都度プロンプトで伝えなくても済む可能性があります(今回は未検証です)。

今回のアップグレード作業は、Cursorのエージェントのみで完結するものではなく、手作業も必要でした。 問題が発生して計画通りに進まず、やり直しも何度か発生しました。 とはいえ、一定の作業を正確にこなせたこと、作業の再現性が得られたことは、十分な成果と感じています。

Nginxでレスポンスヘッダの一部を隠蔽する

こんにちは、リードエンジニアの大坪です。 先日、レスポンスヘッダの一部のヘッダー削除する実装を行いました。あまり普段触らない箇所でしたので少なくない学びがありましたので簡単にではありますが紹介しようと思います。

なぜ実装したのか

先日、外部のセキュリティ監査を依頼したところ、レスポンスヘッダーに含まれるServerヘッダーがセキュリティリスクとなりうるという指摘を受けました。 ServerヘッダーにはNginxのバージョン情報が含まれており、攻撃者にとって脆弱性情報を特定する手がかりになる可能性があるとのことでした。

そこで、セキュリティ強化の一環として、以下の対応を行うことにしました。

  1. Nginxのバージョンを最新版にアップデート
  2. レスポンスヘッダーからServerヘッダーを削除

今回はこの2の話です。

対応

今回は、Dockerコンテナ上で動作するNginxの設定変更を行います。

Dockerfileの修正

Dockerfileを修正し、NginxのバージョンアップとServerヘッダー削除のための設定を追加します。

# headers-moreモジュールをビルドする
RUN set -x \
    # 現在インストールされているnginxのバージョンを取得
    && NGINX_VERSION=$(nginx -v 2>&1 | sed -E 's/^nginx version: nginx\/([0-9]+\.[0-9]+\.[0-9]+).*/\1/') \
    && echo "Detected Nginx version: ${NGINX_VERSION}" \
    # ビルドに必要なパッケージをインストール
    && apk add --no-cache --virtual .build-deps \
        gcc \
        libc-dev \
        make \
        openssl-dev \
        pcre-dev \
        zlib-dev \
        linux-headers \
        curl \
    # 作業ディレクトリに移動
    && cd /tmp \
    # nginxのソースコードをダウンロード
    && curl -fSL https://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz -o nginx.tar.gz \
    # headers-moreモジュールのソースコードをダウンロード
    && curl -fSL https://github.com/openresty/headers-more-nginx-module/archive/v0.38.tar.gz -o headers-more.tar.gz \
    # nginxのソースコードを展開
    && tar -xzf nginx.tar.gz \
    # headers-moreモジュールのソースコードを展開
    && tar -xzf headers-more.tar.gz \
    # nginxのソースディレクトリに移動
    && cd /tmp/nginx-${NGINX_VERSION} \
    # 現在のnginxの設定引数を取得して同じ設定でビルドできるようにする
    && NGINX_ARGS=$(nginx -V 2>&1 | grep 'configure arguments:' | sed 's/configure arguments: //') \
    # 互換モードで動的モジュールとしてheaders-moreをビルドするように設定
    && ./configure --with-compat --add-dynamic-module=/tmp/headers-more-nginx-module-0.38 \
    # モジュールのビルドを実行
    && make modules \
    # モジュールのインストール先ディレクトリを作成
    && mkdir -p /usr/lib/nginx/modules \
    # ビルドしたモジュールを適切な場所にコピー
    && cp /tmp/nginx-${NGINX_VERSION}/objs/ngx_http_headers_more_filter_module.so /usr/lib/nginx/modules/ \
    # ビルドに使用したパッケージを削除してイメージサイズを削減
    && apk del .build-deps \
    # 一時ファイルを削除してイメージサイズを削減
    && rm -rf /tmp/*
  • headers-more-nginx-module のビルド: レスポンスヘッダーを操作するために headers-more-nginx-module を動的モジュールとしてビルドする処理を追加。
    • Nginxのソースコードheaders-more-nginx-moduleソースコードをダウンロード。
    • Nginxの設定引数を取得し、headers-more-nginx-module を動的モジュールとして組み込む設定でconfigureを実行。
    • モジュールをビルドし、所定のディレクトリにコピー。
  • 後処理: ビルド用パッケージの削除と一時ファイルの削除により、イメージサイズを削減。

Nginx設定ファイルの修正

Nginxの設定ファイル (nginx.conf など) に、Serverヘッダーを削除する設定を追加します。

load_module /usr/lib/nginx/modules/ngx_http_headers_more_filter_module.so;

http {
    more_clear_headers Server;
    # ... (既存の設定) ...
}

load_module ディレクティブで先ほどビルドした headers-more-nginx-module を読み込み、more_clear_headers Server; ディレクティブでServerヘッダーをレスポンスヘッダーから削除します。

この状態でデプロイすればレスポンスヘッダーから Serverが消えてるはずです。

まとめ

今回のNginxのアップデートとServerヘッダー削除により、外部に露出するバージョン情報を排除し、セキュリティリスクを低減することができました。
今後もセキュリティ向上に向けてチーム全員で取り組んでいきたいと思います。

おまけ

ちなみに、弊社ではCursorを開発チーム全員で活用しており、今回の実装は以下のサイクルで進めました。

  • コード生成:AIにコードを生成してもらい、実装の基本方針を確認。
  • 解説・検証:生成されたコードの解説を受け、理解と納得を得た上で試行。
  • フィードバック・修正:動作確認後に発生したエラーをAIに修正してもらい、最適な実装へと仕上げた。

このプロセスにより、インフラ設定などの作業が迅速かつスムーズに進行し、普段腰が重くなりがちな作業でも効率良く対応できたと実感しています。AIやそれに関連するツールは本当に日進月歩に進化しますね。


AWS Summit Tokyo 2023に行ってきました

IT事業部でリードエンジニアをしております、大坪と申します。

開発チームは先日、業務で使用しているAWSの最新情報を取得すること・AWSをさらに効果的に活用するための新しい視点を得ることを目的に、AWS Summit Tokyo 2023に行ってきました。

せっかく行ってきたので今回はその参加レポートを簡単に書きたいと思います。

AWS Summit のオフライン開催も久しぶりということでしたが、私自身もオフラインのこうした大規模イベントへの参加は久しぶりだったのでかなり楽しみにしてました。

結果、有意義な時間を過ごせて参加してとてもよかったなという感想です!

セッション

参加した開発チームのメンバー各自の興味に合わせたセッションを選んで参加しました。 (たまたまなのか忖度があったのかはわかりませんが)ほとんどそれぞれが別々のセッションに参加したので、幅広い情報収集がチームとして行えました。この辺は個人ではなくチームで参加するメリットですね。

すべてのセッションがとても興味深かったのですが、個人的に印象に残っているのは、

AWSでゼロトラストを実現するためのアプローチ

ゼロトラストについては、リードエンジニアというよりもIT事業部のメンバーとしての視点で聞いてました。 AWSを使ってのセキュリティについては社内インフラを任される立場として、非常に興味深かったです。

社内インフラにAWSを活用する事例は他にもいくつか聞くことができました。 AWSを主に開発したアプリケーションのインフラとして考えていたので、その視点を広げる機会になりましたね。

AWS DeepRacer ワークショップ

自分が組んだプログラムでレーシングカーを動かせる、という体験はシンプルに面白かったです。 今回はさすがに実機にプログラムをインストールして走らせてみる、というところまではできませんでしたが機会を見つけてやってみたいなぁと思いました。

ABEMA の大規模・高画質ライブ配信を支える AWS クラウド動画配信システム

サッカーW杯、私はずっと見てましたのでインフラどうなってたんだろうという興味から参加しました。 W杯見てた人やっぱ多かったんでしょうね、セッションに参加してるひとがとても多くて立ち見でした。 あれだけの規模の配信を大きなトラブルなく終わらせたっていうのは本当にすごいです。

会社に戻ってから、それぞれのセッションに参加した時にとったメモを結合しました。 全部結合するとかなりの量になりましたので、やっぱりそれだけどのセッションも興味深い・勉強になる話だったということなんだなと。

セッション以外だと、企業ブースでの情報収集もできました。 こちらも知らなかったサービスを知れたり、今使ってる・使おうか検討してるサービスの開発者・担当者に直接話を聞ける機会を持てて非常によかったです。

まとめ

新しい情報をいろいろゲットできましたし、久しぶりの大規模なオフラインイベントに参加できて単純に楽しかったです。 あと新しい技術やサービスの話を聞くととても刺激になりました。 帰りの道中、みんなで「あのサービス使えばxxできるのでは?」「あれはどう?」みたいな話で盛り上がりました。

Summitへの参加はチーム全体のモチベーションアップにもなったし、今後もこういった勉強会などあれば積極的に参加してきたいと思ってます。

railsでのAuroraのフェイルオーバーへの対応

UP Parters GroupのIT事業部 開発チームでリードエンジニアをしております、大坪です。

最近はインフラにAWSを採用することがかなり多くなってきてます。 そんな中、データベースとして元々PostgreSQLを採用することが多かったのですが、Auroraを選択することも増えてきました。 データベースの堅牢性とスケーラビリティ、パフォーマンスの向上を期待できるというのがざっくりしたAuroraを選択する理由です。

しかし一方でrailsとAuroraのフェイルオーバーへの対応が課題となりました。

そこで、我々がどのようにこの課題へ対応しているかをご紹介します。

RailsとAuroraの相性

まず、RailsとAuroraは相性抜群!とは現時点では言えないかなと思います。 基本的には、RailsとAuroraはうまく連携できますが、Auroraのフェイルオーバー時にRailsのコネクションプールが問題を引き起こすことがあります。

Auroraのフェイルオーバーとは

Auroraのフェイルオーバーとは、プライマリインスタンスがダウンした際に、自動的にリードレプリカをプライマリインスタンスに昇格させる機能のことです。 これにより、ダウンタイムを最小限に抑えることができます。

Railsのコネクションプールとは

Railsのコネクションプールとは、データベースへの接続を管理する仕組みのことです。 しかし、Auroraのフェイルオーバーが発生した場合、コネクションプールに接続情報が残ったままになり、アプリケーションが正常に動作しなくなることがあります。

この問題を解決するため、

dev.classmethod.jp

を参考にさせていただきました。

class ActiveRecord::ConnectionAdapters::PostgreSQLAdapter
  def active?
    if super
      is_read_only = ActiveRecord::Base.connection
                                       .execute("SHOW transaction_read_only")
                                       &.first
                                       &.fetch("transaction_read_only")
                                       &.upcase == "ON"
      if is_read_only
        ActiveRecord::Base.clear_all_connections!
        false
      else
        true
      end
    end
  rescue Exception => e
    false
  end
end

ここでは、ActiveRecord::ConnectionAdapters::PostgreSQLAdapter を拡張して、接続するデータベースが読み取り専用になっている場合は一度接続を破棄して、再度接続しなおしています。 つまり、フェイルオーバーが発生し、データベースへの接続が切れた場合、コネクションプールを破棄し、新しい接続を確立することで問題を解決しています。

まとめ

色々書いてきましたが、実はこの問題についてAurora導入を決めた当初、恥ずかしながら私は全く知らず、チームのメンバーから「こういう問題があるっぽいよ」と教えていただいたことで初めて知りました。

教えていただいた後、フェイルオーバーやコネクションプールについて改めて調べてみて色々と勉強になりました。

まだまだ知らないことがいっぱいあるなぁ。。。 頑張って勉強します。