Goにおける変数定義とスコープのレビュー観点
スコープは単なる範囲ではない──設計意図を問うレビュー観点
Goでは変数のスコープ設計が、可読性・責務分離・副作用・安全性に直結します。
とくにレビュー時には「この変数、なぜこのスコープで定義されているのか?」を問い直すことで、見えづらい設計ミスや再設計ポイントが浮かび上がってきます。
良い実装:スコープ設計が明確な処理フロー
スコープ設計が明確な実装例
func ValidateAndFormat(name string) (string, error) {
if name == "" {
return "", errors.New("name is required")
}
cleaned := strings.TrimSpace(name)
if len(cleaned) < 3 {
return "", errors.New("name too short")
}
formatted := strings.ToUpper(cleaned)
return formatted, nil
}
解説
cleaned
,formatted
のスコープは最小限に抑えられており、変数定義が責務と一致している- 一連の処理がフェーズ分割されており、スコープと責務の対応関係が読みやすい
- エラー処理が前倒しされており、スコープが自然に狭まっている
スコープが広すぎるケースとレビュー指摘
func Process(input string) error {
var err error
@Reviewerこの変数はifブロックの中でしか使われていません。関数の先頭で定義せず、使用箇所の近くで定義しましょう。過剰なスコープは将来の保守性を下げます。 if input == "" {
err = errors.New("empty input")
return err
}
return nil
}
スコープシャドウイング:意図せぬ再定義の罠
var token string = "initial"
func UpdateToken() {
token := "updated"
@Reviewer外側の変数と同名のローカル変数を`:=`で定義しており、意図せぬスコープシャドウイングが発生しています。外部変数の更新が目的であれば`=`を使いましょう。 fmt.Println(token)
}
クロージャとループ変数のバグ予兆
func BuildCallbacks(inputs []string) []func() {
callbacks := []func(){}
for _, input := range inputs {
callbacks = append(callbacks, func() {
fmt.Println(input)
})
@Reviewerクロージャがループ変数inputを参照しており、ループ終了後の値をすべての関数が共有する問題があります。ループ内で変数を束縛し直すか、Go 1.22以降の仕様であることを明示してください。 }
return callbacks
}
ポインタによるスコープ外副作用
func UpdateName(user *User) {
user.Name = "updated"
@Revieweruserはポインタで渡されており、関数外の状態を変更しています。関数の見た目上のスコープは限定されていますが、副作用の波及範囲を意識したレビューが必要です。}
定数と変数の使い分け
var version = "v1.2.3"
@Reviewer書き換えのない値であるため、`const`を使うべきです。不変性の意図が明確になり、コードの意味がより正確に伝わります。
スコープと命名の不一致(エクスポートされない型)
type user struct {
ID int
}
func NewUser() *user {
return &user{ID: 1}
@Reviewer非エクスポート型に対してエクスポートされた関数名が提供されています。意図的でないならば型名・関数名の整合性を再検討してください。}
複数の責務がスコープに混在している関数
func ProcessData(data string) (string, error) {
var cleaned string
var validated bool
var converted string
@Reviewer`cleaned`, `validated`, `converted`はいずれも独立した処理フェーズを担っています。1関数に多責務が詰め込まれており、スコープと処理単位の対応が曖昧です。関数分割を検討してください。
cleaned = strings.TrimSpace(data)
validated = len(cleaned) > 0
if !validated {
return "", errors.New("empty data")
}
converted = strings.ToUpper(cleaned)
return converted, nil
}
スコープレビュー用チェックリスト
チェック項目 | 質問例 |
---|---|
スコープの広さ | この変数は本当にこの関数全体で使われる必要があるか? |
意図の明確性 | スコープの広さは読み手に何を伝えているか? |
副作用の有無 | ポインタやグローバル変数が他に影響していないか? |
可視性と一致性 | 外部公開・内部限定の整合が取れているか? |
責務の混在 | 同一スコープに無関係な処理が混在していないか? |
命名と一致性 | エクスポートと非公開の命名が一致しているか? |
const/varの使い分け | 不変の値に対してconstを使っているか? |
あとがき:設計意図の解像度を上げるスコープレビュー
変数のスコープは、単なる「使える範囲」ではありません。
それは、設計者の意図を読み手に伝える構造的な記号です。
レビューでは以下を意識することで、実装者と設計者の意図をより正確に読み解けます:
- スコープと責務が一致しているか
- スコープ外に波及する副作用はないか
- スコープに見合った命名と可視性が保たれているか
スコープの設計を読み解く視点を持つことで、コードの品質は一段階高いレベルに引き上げられます。