こんにちは!ファインディでFindy Team+開発チームのEMをしている浜田です。 この記事はFindy Advent Calendar 2024 6日目の記事です。
今年の上旬、フロントエンジニア向けにバックエンド勉強会を開催しました。この記事ではバックエンド勉強会を開催した目的や内容、効果について紹介します。
バックエンド勉強会を開催した背景
私のチームが開発しているFindy Team+はWebアプリケーションとして提供しており、フロントエンドはReact/TypeScript、バックエンドはRuby on Railsを使用しています。
ファインディでは、エンジニア個人の志向に合わせて、特定の技術領域を深く理解することでバリューを発揮している方や複数の技術領域を幅広く扱ってバリューを発揮している方など様々な方がいます。 そのため、自分の得意領域以外への挑戦を推奨しています。
ただ、現在では1チームに6〜8名ほどのエンジニアで構成され、各自が得意領域を担当することで効率よく開発が進む状況です。 このような状況では、得意領域以外にチャレンジしたい気持ちがあったとしても、最初の一歩が踏み出しづらいものです。
そこで、今回はフロントエンドを主軸としているエンジニアがバックエンドに挑戦する一歩を後押しするためにバックエンド勉強会を実施しました。
バックエンド勉強会の概要
- 期間: 2024年2月〜8月
- 頻度: 毎週30分
- 回数: 23回
- 講師: 私
- RubyもReact/TypeScriptも業務で使った経験あり
- 参加者: 3名
- バックエンドほぼ未経験、または数年触っていない。React/TypeScriptはバリバリ書いている
- カリキュラム
- RubyやRails、DBの基礎
- 業務で使うコードを使ったライブコーディング
バックエンド勉強会の内容
バックエンド勉強会では、前半はRubyやRails、後半はDBについて学習をしました。 業務で使っているコードを使ってAPI開発の基礎知識を学ぶことで、簡単なAPIの作成や修正ができることを目指しました。
RubyやRailsの学習
VS Codeのプラグイン設定
まずはエディタを整備しました。 参加者は全員VS Codeを使っていたので次のプラグインをインストールしました。
Rails console / dbconsoleを使ってみる
開発環境には全員バックエンド環境も構築しているので環境構築は不要です。 RubyやSQLをサクッと試すことができるように、Rails console / dbconsoleの使い方を紹介しました。
ruby-lang.orgを読む
ruby-lang.orgの一部を読み合わせしました。 読み合わせした箇所をいくつか紹介します。
普遍の真理
Rubyでは、nilとfalseを除くすべてのものは真と評価されます。 CやPythonを始めとする多くの言語では、0あるいはその他の値、空のリストなどは 偽と評価されます
JavaScriptを書いている人にとって0
はFalsyですが、Rubyだと0
はTruthyでありハマりやすいので強調しておきました。
不思議なメソッド名
Rubyでは、メソッド名の最後に疑問符(?)や感嘆符(!)が使われることがあります。
珍しいルールなのでこちらも詳しく説明しました。
?はbooleanを返すメソッドにつけることが多く理解しやすいです。
# 偶数かどうかを判定するメソッド def odd?(n) n % 2 == 1 end
!は破壊的メソッドに付けることが多く、破壊的メソッドと同じ役割だが破壊的ではないメソッドがある場合には破壊的メソッド側に!が付けます。ただし、破壊的メソッドしかない場合は!をつけません。
# !あり・なしメソッドがあるパターン str = "string" str.upcase! str # => "STRING", 元の値がstrが上書きされている str = "string" upcased_str = str.upcase str # => "string", 元の値が上書きされていない upcased_str # => "STRING", upcaseの結果がupcased_strに代入されている # 同名の非破壊メソッドがないため、破壊的メソッドだけど!がつかない str = "string" str.replace "STRING" str # => "STRING", 元の値が上書きされている
また、Railsだと例外を発生させるかどうかで!を付けたり付けなかったりすることがあります。
hoge.save # 保存に失敗した場合falseを返す hoge.save! # 保存に失敗した場合例外を発生させる
「存在しなかった」メソッド
Rubyはメッセージに対応するメソッドを見つけられなかったとしても諦めません。 その場合は、見つけられなかったメソッド名と引数と共に、 method_missingメソッドを呼び出します。 method_missingメソッドはデフォルトではNameError例外を投げますが、 アプリケーションに合うように再定義することもできます。
Rubyではメソッドが見つからなかった場合の処理を簡単に上書きできます。 アプリケーションコードで多用することはないですが、ライブラリではよく使われている仕組みなので知っておくと良いです。
class Hoge; end hoge = Hoge.new hoge.fuga # undefined method `fuga' for an instance of Hoge (NoMethodError) # method_missingを上書き def hoge.method_missing(name, *args) puts "メソッドが見つかりませんでした: #{name}" end hoge.fuga # メソッドが見つかりませんでした: fuga
演算子は糖衣構文(シンタックスシュガー)
Rubyにおけるほとんどの演算子は糖衣構文です。 いくつかの優先順位規則にもとづいて、メソッド呼び出しを単に書き換えているだけです。 たとえば、Integerクラスの+メソッドを次のようにオーバーライドすることもできます。
class Integer # できるけれど、しないほうがいいでしょう def +(other) self - other end end
Rubyでは"+"もメソッドです。面白い仕組みなので紹介しました。
メソッドなので次のような呼び方ができます。
1 + 2 # => 3 # 1(Integerのインスタンス)の+メソッドの引数に2を渡している 1.+(2) # => 3
Ruby で扱える全ての値はオブジェクトです。
「全ての値はオブジェクトです」はRubyの特徴だと思うので説明しました。
クラスをインスタンス化したものがオブジェクトなのは理解しやすいですが、数値や文字列などのリテラルもオブジェクトであり、クラス自体もオブジェクトです。
全てオブジェクトなのでメソッド呼び出しや既存メソッドの挙動を上書きなどを行えます。
Ruby 3.2 リファレンスマニュアル > クラス/メソッドの定義
ここでは次の項目について説明しました。
- クラス定義
- モジュール定義
- クラスとの違いがわかりづらいので説明
- インスタンス化できず、共通の振る舞いを複数のクラスで共有したい場合などに使うことが多い
- メソッド定義
- 引数がなければ()を省略できる
- 特異クラス定義 / 特異メソッド定義 / クラスメソッドの定義
- Railsのモデルで多用するので説明
if文など基本的な制御構造やunlessなどRubyの特徴的なところを紹介しました。 また、forは他の言語では多用するがRubyではあまり使わずeachを使うことが多いということも説明しました。
よく使うローカル変数、インスタンス変数、定数を説明しました。
さらっと全体を眺めつつ「シンボル」「ハッシュ式」「ヒアドキュメント」「%記法」は初見だと戸惑うポイントなので詳しく説明しました。
ハッシュ式
キーとバリューのペアを保持するオブジェクト。 キーには数値・文字列・シンボルが使える。キーにはシンボルを使うことが一般的。
{ 1 => 2, 2 => 4, 3 => 6} { "a" => "A", "b" => "B", "c" => "C" } { :a => "A", :b => "B", :c => "C" } { a: "A", b: "B", c: "C" } # シンボルの場合、このような書き方ができる。この書き方が推奨。
ヒアドキュメント
複数行の文字列を表示するときに使う。
<< <<-: 終端子にインデントをかける <<~: インデントをいい感じに削ってくれる
Railsガイドの紹介
Railsガイドについてはこの勉強会では紹介だけ行いました。 Railsの機能が網羅されており、最新バージョンに追従しているとてもよいドキュメントなので、困ったらまずはRailsガイドを読むことをおすすめしました。
Railsの構成を説明
実際に開発しているバックエンドのディレクトリ構成を説明しました。
- Gemfile / Gemfile.lock
- package.jsonみたいにライブラリを管理しているもの
- Dockerfile / Dockerfile.dev / compose.yml
- .devは開発環境用
- compose.ymlはdocker composeの設定ファイル
- config
- 各種設定ファイル
- db
- データベーススキーマやマイグレーションファイル
- log
- ログファイル
- dockerで実行している場合、ここに出力されないので
docker compose logs -f web
で確認
- lib
- 本体とは切り離して使えるライブラリが格納されている
- taskにコマンド実行するタスク(ジョブ)が格納されている
- app
- admin
- 管理画面用のgem
activeadmin
で使うクラス
- 管理画面用のgem
- controllers
- MVCのC
- enums
- localeやメトリクスのenum情報
- graphql
- GraphQLのgem
graphql-ruby
のquery/mutation/typeなど
- GraphQLのgem
- interactors
- interactor gemを使ってCUDのビジネスロジックを実装
- Controller -> Interactor -> Modelの依存関係
- jobs
- 非同期処理
- mailers
- メール送信処理
- models
- MVCのM
- DBアクセスクラスが一般的だが、分析クラスや外部接続クラスなども入っている
- views
- MVCのV
- admin
バックエンドのライブコーディング
RubyやRailsの基礎的な部分は抑えたので、ここからは実際にバックエンドのコードを使ってライブコーディングを行いました。
既存のGraphQLのQueryにfieldを追加
フロントエンドを開発しているときにバックエンドを触りたくなる最も多いケースは、APIのインターフェースの変更だと思います。 そこで、既存のGraphQLのQueryにfieldを追加するというテーマでライブコーディングを行いました。
GraphQLの新規作成
次はよりがっつりバックエンドを触るケースとして、次の仕様を満たすGraphQLをライブコーディングしました。 このテーマでAPIを作成するときに必要となるDBのテーブル作成・Model作成・GraphQL作成の一連の流れを学びました。
- userに紐づくメモを保存する機能
- ユーザーごとにメモは複数作成可能
- 1つのメモは100文字まで
- 空のメモはNG
- メモは1ユーザー10件まで
- ユーザーIDに紐づくメモを取得できる
- テキスト検索できる
- メモIDに紐づくメモを取得できる
- メモを新規登録できる
- メモを更新できる
- 自分のメモしか更新できない
DBの基礎
バックエンドの学習を進めるうち、データベースの知識を底上げする必要があると感じたため、ここからはデータベースについて学びました。
SQL実習
既存のテーブルを使って、お題を取得するSQLを書いてもらいました。
- 単一テーブルのSELECT
- JOINを使ったSELECT
- 複数のテーブルをJOINするSELECT
- INNER JOIN、LEFT JOINの使い分け
- GROUP BY、HAVINGを使った集計
SLQ実習で書いたSQLをActiveRecordで書く
Railsで開発する場合、生のSQLを書かずActiveRecord経由でDBにアクセスするので、SQL実習で書いたSQLをActiveRecordで書いてもらいました。
N+1問題
N+1問題について説明し、RailsでN+1を回避するプラクティスを紹介しました。
- preload / eager_load / includes
- 結合が不要な場合はpreload、joinするときはeager_load(left outer join)
- includesはpreload or eager_loadを自動判断する。
- 意図せずクエリーが変わってしまうことがあるので使わない方が無難
- 経験上、includesで発行するSQLが変わって困ったことはあってもincludes禁止で困ったことはない
- Rails始めた人が絶対辿り着く記事
- GraphQLではpreloadやeager_loadを使った先読みが適さないこともあるので注意
正規形
リレーショナルデータベースを使う場合、第3正規形までは必修だと思うので非正規形と第1〜3正規形を説明しました。
インデックス、実行計画
テーブル設計をするようになったらインデックスの検討も必須なので説明しました。
インデックスはとても良いスライドがあるのでこちらを共有しました。
また、発行したSQLがどのインデックスを使っているのか判断する手法としてEXPLAIN
を使う方法を紹介しました。
トランザクションとロック
トランザクションは更新系の処理を実装する時に必須なのでRailsでトランザクションを使う方法を説明しました。
transaction do # この中の更新処理は1つでも失敗したら全部ロールバックされる # データの整合性を保つために更新系の実装では考慮が必要 end
Railsを使ってWebアプリケーションを作っている場合、明示的にロックを意識することは少ないのですが、概念として楽観的ロックと悲観的ロックについて軽く説明しました。
外部キー
外部キーについてもMySQLのドキュメントをなぞりながら軽く触れました。 特にON DELETEは便利なので活用していきたい機能の1つです。
まとめ
バックエンド勉強会で実施した内容をざっくりではありますが紹介しました。
勉強会に参加したメンバーから「勉強会後からバックエンドのコードがスムーズに読めるようになった」などの感想をいただくことができました。さらに次の機能開発でバックエンドに挑戦するメンバーもいたので開催して本当に良かったと感じています。
次の画像は参加したメンバーのバックエンドのプルリクをFindy Team+で表示したものです。GraphQLのQueryやMutationの追加や、フィールドの追加・修正などを行なったことがわかります。
ファインディでは一緒に会社を盛り上げてくれるメンバーを募集中です。興味を持っていただいた方はこちらのページからご応募お願いします。