年明け初回の勉強会です。
今回はまぁ、なんというか、通常回となります。
ただ、徐々に発表者も視聴者も増え、着実に盛り上がり始めています。
嬉しいことですね。
私は未だ出向中なので、今回も主催は代理にお願いしています。
そろそろ忘れられていそう。
今回の発表内容
出向先で覚えたgitコマンド
発表者:Kamada.S
出向先でいくつかgitコマンドを覚えたので共有します。
出向先で習得したgitコマンド
いくつかgitコマンドを習得したので備忘録的に共有します。
git blame
指定したファイルの各行と最終変更情報を出力します。
git blame FileName
実際に実行すると以下のような出力になります。
~/.../react-hook-sample/app $ git blame routes.ts
828a8995 (ShogoKamada 2026-01-01 10:10:17 +0900 1) import { type RouteConfig, index, route } from "@react-router/dev/routes"
828a8995 (ShogoKamada 2026-01-01 10:10:17 +0900 2)
828a8995 (ShogoKamada 2026-01-01 10:10:17 +0900 3) export default [
828a8995 (ShogoKamada 2026-01-01 10:10:17 +0900 4) index("routes/home.tsx"),
828a8995 (ShogoKamada 2026-01-01 10:10:17 +0900 5) route("/counter", "routes/counter.tsx"),↲
92c8d020 (ShogoKamada 2026-01-02 21:16:16 +0900 6) route("/effect-counter", "routes/effectCounter.tsx"),
828a8995 (ShogoKamada 2026-01-01 10:10:17 +0900 7) ] satisfies RouteConfig
出力が画面よりも長い場合はlessで表示されます。
ーL オプション
出力する行を指定できます。
~/.../react-hook-sample/app $ git blame -L 4,6 routes.ts
828a8995 (negi on Xperia 5 V 2026-01-01 10:10:17 +0900 4) index("routes/home.tsx"),
828a8995 (negi on Xperia 5 V 2026-01-01 10:10:17 +0900 5) route("/counter", "routes/counter.tsx"),
92c8d020 (negi on Xperia 5 V 2026-01-02 21:16:16 +0900 6) route("/effect-counter", "routes/effectCounter.tsx"),
git branch –show-current
現在のbranchを出力できます。
~/.../react-hook-sample/app $ git branch --show-current
feature/implement_page-transition
pipeを組み合わせるといろいろと便利に使えます。
git branch --show-current | xargs git push -u origin
現在のbranchで初めてpushを行うときの--set-upstream を、branch名をタイプせずに実行できます。
git branch --show-current | xargs echo -n | xclip -sel c
現在のbranch名をclipboardにコピーできます。
git bracnh --show-current | sed "s/$/--child_branch/" | xargs git checkout -b
例えば現在のbranchから子branchを切る運用の場合、sed コマンドと組み合わせてbranch名のtypoを防ぐことができます。
-D オプション
ブランチを強制的に削除できます。-d オプションではmerge済みのbranchしか削除できないところ、 -D では削除できます。
結局使わなかったローカルbranchや、push済みで長らく用がないbranchを削除するために使います
git clean -f
git未追跡のファイルを一括で削除します。git mergetool で生成された .orig ファイルなどを削除するために使います。
git commit –allow-empty
stageされたファイルがなくてもcommitを作成します。
Pull Request作成時のconflict解消のためのローカルmergeの前後などでメモ用に使うと便利です。
git push origin :
リモートのbranchを削除します。
ブランチ名をtypoしたブランチを消す場合などに使います。
git push origin :feature/implement_page_transiton--fix_typo
git checkout
現在のbranchの保存していない変更をリセットすることができます。
特に、mergeによってconflictが発生したときに便利です。package-lock.json のような自動生成ファイルや、どちらかの内容で上書きすることが決まっている場合に使います。
- –ours オプション
conflict全箇所で現在のbranchの内容を選択します。 - –theirs オプション
conflict全箇所で取り込むbranchの内容を選択します。
git revertとgit reset
過去の編集をなかったことにする方法として revert と reset があります。
- revert
指定したcommitの 逆の差分をcommit して相殺し、編集をなかったことにします。
追加した内容に対しては削除する編集を、削除した内容に対しては追加する編集を行います。 - reset
指定したcommit時点まで巻き戻します。
現在のcommitから指定したcommitの直後のcommitまでの 履歴が削除 されます。
merge commitを扱うときの挙動について
branchがmergeされるとmerge commitが作成されます。
merge commitに紐づくかたちでmerge元のbranchが持つcommitがmerge先に追加され、 時系列順 に並べられます。
実際に出向先で発生したケースを元に解説します。
同時並行で進められている2つの実装AとBをdevにmergeする必要がありました。
2つの実装の内容は似ており編集箇所が被ることがありました。
そのため、Bの実装ブランチにAの実装を取り込み、conflictを解決してからdevにmergeすることになりました。
dev ---m---m ←ここでAの内容とconflict
A --/ /
B -----/
dev --------m ←ここでmergeしよう
A --\ /
B ---m--/ ←一旦conflictを解消して
その際、Aをdevに取り込んだときのmerge commitをrevertしてしまいました。
これにより、Aを取り込んだBをdevにmergeしたときに、一部の実装が反映されなくなってしまいました。
原因は revertがcommitを作るという挙動です。
revert前後のどちらの方法でも、devにAの実装ブランチで作られた同じハッシュのcommitを取り込むことになります。
そのため、時系列で後ろのrevert commitに打ち消されてしまいました。
同じ状況になった場合、対策は2つあります。
ひとつは devをBに取り込んでrevert commitをrevertする方法です。
この方法では一度mergeした内容が消えることはありませんが、commit logからbranchの状態が分かりづらくなります。
もうひとつは バックアップbranchを作ってresetする方法です。
この方法ではcommit logが整頓されますが、操作を誤ると実装が消えてしまう可能性があります。merge commitをresetすると、それに紐づくcommitが全て取り除かれます。
今回のケースではresetを用いるのが良かったと考えています。
git運用上、devブランチの復元が難しくなかったことと、これ以上revet commitを増やしたくないためです。
そもそもですが、devからAの対応を一旦取り除こうとしたことが誤りだったと考えています。
あまり多いケースだとは思えませんが、もし同じ状況になった場合はdev反映用の統合branchを作るのが最もスマートです。
みんなで読もうSQLアンチパターン
発表者:H.Mさん
会社の書籍購入制度を使って「SQLアンチパターン(4290円)」を買ったので、その内容について共有します。
この本はSQLを用いたシステム開発における失敗例がたくさん掲載されています。
実装の落とし穴に落ちないためにもおすすめの一冊です。
(お断りしておきますが、SQLに関してはあまり上級者ではないです)
今回は「ジェイウォーク(信号無視)」というアンチパターンについて触れていきます。
ジェイウォークとは
カラムにカンマ区切りのリストを格納することをジェイウォークと呼びます。
例として、rolesカラムをVARCHAR(100)型で定義して"admin,editor,viewer"という文字列を格納するなどが挙げられます。
| users | 型 |
|---|---|
| id | INT PK |
| name | VARCHAR(50) |
| role | VARCHAR(100) |
なぜアンチパターンなのか
根本的な理由は「このカラムにどんな値が入るか想定できないこと」にあります。
具体的には以下の3つの理由からアンチパターンとされています。
- インデックスが効かない
→パフォーマンスが低下する - 外部キー制約が使えない
→データ整合性が保証されない(存在しない値が入れれたりできてしまう) - クエリが書きにくい
→値をピンポイントで特定しようとしたら正規表現が必要
解決策
交差テーブル(中間テーブル)を作ることで解決できます。
今回の例で具体的に解説すると、以下のようなテーブル構成にすることで解決できます。
| users | 型 |
|---|---|
| id | INT PK |
| name | VARCHAR(50) |
| user_roles | 型 |
|---|---|
| user_id | INT PK FK |
| role_id | INT PK FK |
| PRIMARY KEY | (user_id, role_id) |
| roles | 型 |
|---|---|
| id | INT PK |
| name | VARCHAR(50) |
パフォーマンスの問題は、user_idとrole_idにインデックスが張れるため解決します。
データ整合性の問題は、user_idとrole_idに外部キー制約が使えるため解決します。
クエリの書きづらさは、正規表現が不要になるため解決します。
まとめ
カンマ区切りのリストを格納するカラムはジェイウォークと呼ばれるアンチパターンとされています。
交差テーブルで回避できないか検討しましょう。
ただし、ジェイウォークが常に間違いではありません。
交差テーブルに代えるのがよいとは限らないパターンもあるため注意が必要です。
今回が好評であれば今後もSQLアンチパターンを取り上げていきたいです。
チーム開発報告:歩数計アプリ
発表者:N.Aさん
3人チームで歩数計アプリを作ってみました。
そこで経験したことについて共有します。
アプリの概要
アプリトップ画面ではキャラクタと歩数を表示します。
Apple HealthKitから歩数を取得しています。
歩数詳細画面では歩数の統計が確認できます。
DAY表示では3時間毎の歩数を棒グラフにして表示します。
3時間あたり3000歩以上歩くと棒グラフが黄色で強調表示されます。
WEEK表示では月曜始まりで1日の累計歩数を一覧で表示します。
歩数1歩につき1pointが付与されるようになっています。
pointは1回1000pointのガチャで利用できます。
ガチャではキャラクタのコスチュームが排出されます。
今回のデバッグ環境はmacOS上のエミュレータです。
ヘルスケアアプリの値を直接操作して数値の反映を確認しています。
アプリ開発で学んだこと
今回のチーム開発での反省点は以下のようになります。
- こまめにcommit
よくあるミスですが、1つのcommitが大きかったため、過去の実装を見ようとしたときに戻りづらくなってしまいました。 - 階層構造を決めておく
フロントエンドとバックエンドの両方を開発するという経験が初めてだったため、繋ぎ込みをするときのことを考えずにディレクトリ構造を決めてしまいました。 - エラーの理由と解決策をメモ
最初の頃に発生したエラーが中盤で発生したときに調べ直しが発生してしまい、自身の学びに繋げられていないと感じました。 - APIのURLをRESTに倣って統一
今回は8画面という規模なのでなんとかなりましたが、APIエンドポイントに規則性がなく拡張しづらい設計にしてしまいました。 - 予定日数と作業量の見積もり
全体的に作業日数の見積もりが甘かったです。
1週間で見積もった作業が半月かかってしまい、また他の事情もあってだんだんと作業時間が減っていきました。 - サンプルIDとパスワード管理
サンプルで使っていたサンプルのIDとパスワードの管理が雑で、忘れてしまったり結合時に吹っ飛んでしまったりしていちいち作り直すのが手間を取ってしまいました。
今回のチーム開発で勉強になった点は以下のようになります。
- 下書きを書き出して共有
最初に手書きで設計を行い、認識を揃えました。 - フロントとバックの結合
作ってみるまで、ログイン後の操作をするAPIにトークンを要求させるという方法に気づきませんでした。 - APIの作り方(MCRを使う)
MVCとは異なりViewが関わらないため、ModelとRouterを繋げるControllerという構図を意識して実装しました。 - Apple HealthKit APIの使用方法
今回使用したReactNativeの検証環境であるExpoGoではApple HealthKit APIを使用することができませんでした。
そのため、エミュレータにアプリをインストールしてデバッグを行いました。 - 困ったときは小さく作って比較する
Apple HealthKit APIとの繋ぎ込みがどうしてもうまくいかなかったため、別のアプリを作成して繋ぎ込みだけを試し原因の調査を行いました。
要件定義について〜準備の大切さ〜
発表者:L.Gさん
最近アプリ制作に挑戦するなかで要件定義に取り組む機会がありました。
その準備の大切さについて共有します。
なぜ「要件定義・準備」が大切なのか
あなたは旅行に行くときどちらのタイプですか?
- しっかり準備する
- とりあえず行ってみる
例えば家族と旅行に行くとして、あらかじめ行きたい場所があればの行き方や費用などの計画を練るでしょう。
到着してから行きたい場所への計画を考えようとすると、今からでは間に合わないことがたくさんあるでしょう。
私は今、アプリ制作に挑戦しています。
単に趣味だけではなく、実用的なものを作りたいと考えています。
そのために、社内でアンケートを取り、要望の多い機能は取り入れると決めました。
これは、まだアプリが要件定義の段階にあったからできたことです。
もし、実装フェーズに入ってから新しい機能の追加を決めてしまうと、そのために変えなければいけない箇所や新しく作らなければいけないものがたくさんできてしまいます。
旅行先についてから行きたいところを決めるのと同じです。
作り始める前にしっかり準備することはとても大切です。
なぜでしょうか?
それは、初期段階ほど仕様の変更コストが低いからです。
しかし、すべてを完璧に準備することはできません。
旅行のように天気に左右されてしまうようなこともあります。
ですから、たった1つのことに決めてしまわず、計画の段階でできるだけ多くの可能性を考えておくことが重要です。
今月もおわり
1月はなんとか中旬に書き上がりましたね…。
先月は月末でしたから。
以前よりも読みやすくなっているんじゃないでしょうか。
私はというと、出向先にだいぶ慣れてきて、そろそろ”動き出そう”かとしています。
また、来月会いましょう。