2025/11/16
Contents Prefer pure functions that return results rather than causing side effects. Use pointers only when necessary (e.g., large slices or shared transactions).
(副作用を起こすのではなく、結果を返す純粋関数を優先する。ポインタは必要な場合のみ使用する(例:大きなスライスや共有トランザクション))
解説 純粋関数は、同じ入力に対して常に同じ出力を返し、外部状態を変更しません。これにより、関数の振る舞いが予測可能になり、テストが容易になります。副作用を持つ関数は、呼び出し順序や状態に依存するため、デバッグが困難です。ポインタを多用すると、意図しない状態変更が発生しやすくなるため、必要な場合のみ使用すべきです。純粋関数は並行処理でも安全です。
具体例 // 悪い例(副作用を持つ関数)
var globalCounter int
func ProcessItem (item * Item ) {
item .Price *= 1.1 // 引数を直接変更(副作用)
globalCounter ++ // グローバル状態を変更(副作用)
}
func CalculateTotal (items []* Item ) float64 {
total := 0.0
for _ , item := range items {
ProcessItem (item ) // 元のデータが変更される
total += item .Price
}
return total
}
// 良い例(純粋関数)
func ApplyTax (price float64 ) float64 {
return price * 1.1 // 新しい値を返す
}
func CalculateTaxedPrice (item Item ) Item {
// 元のitemを変更せず、新しいItemを返す
return Item {
Name : item .Name ,
Price : ApplyTax (item .Price ),
}
}
func CalculateTotal (items []Item ) float64 {
total := 0.0
for _ , item := range items {
taxedPrice := ApplyTax (item .Price )
total += taxedPrice
// 元のitemsは変更されない
}
return total
}
// ポインタが必要な例(大きなデータ構造の効率的な処理)
type LargeDataSet struct {
Data []byte // 数MBのデータ
}
func ProcessLargeData (data * LargeDataSet ) error {
// 大きなデータのコピーを避けるためポインタを使用
return processBatch (data .Data )
}
// トランザクション内での共有が必要な例
func UpdateWithTransaction (tx * sql .Tx , user User ) error {
// トランザクションは共有する必要がある
return tx .Exec ("UPDATE users SET name = ?" , user .Name )
}
参考リンク core
coding-standard
2025/11/16
Contents Prefer block statements over deep nesting where possible.
(可能な限り、深いネストよりもブロック文を優先する)
解説 深いネストは、コードの可読性を著しく低下させ、ロジックの理解を困難にします。早期リターン(ガード節)を使用することで、ネストを浅く保ち、正常系のフローを明確にできます。ネストが深くなるほど、エラー処理や条件分岐が複雑になり、バグの混入リスクが高まります。フラットなコード構造は、コードレビューやデバッグを容易にします。
具体例 // 悪い例(深いネスト)
func ProcessOrder (order Order ) error {
if order .ID != "" {
if order .Amount > 0 {
if order .Status == "pending" {
if err := validateOrder (order ); err == nil {
if err := saveOrder (order ); err == nil {
if err := sendConfirmation (order ); err == nil {
return nil
} else {
return err
}
} else {
return err
}
} else {
return err
}
} else {
return errors .New ("invalid status" )
}
} else {
return errors .New ("invalid amount" )
}
} else {
return errors .New ("invalid ID" )
}
}
// 良い例(早期リターンでフラット化)
func ProcessOrder (order Order ) error {
if order .ID == "" {
return errors .New ("invalid ID" )
}
if order .Amount <= 0 {
return errors .New ("invalid amount" )
}
if order .Status != "pending" {
return errors .New ("invalid status" )
}
if err := validateOrder (order ); err != nil {
return err
}
if err := saveOrder (order ); err != nil {
return err
}
if err := sendConfirmation (order ); err != nil {
return err
}
return nil
}
参考リンク core
coding-standard
2025/11/16
Contents Do not reuse a variable within the same scope.
(同一スコープ内で変数を再利用しない)
解説 同一スコープ内で変数を異なる目的に再利用すると、変数の意味が曖昧になり、バグの原因となります。変数の値が途中で別の意味を持つようになると、コードを読む人が混乱し、意図しない値の参照が発生する可能性があります。各変数は一つの明確な目的を持つべきです。MUST項目として、変数のライフサイクルを明確にし、コードの予測可能性を保証します。
具体例 // 悪い例
func processData () {
result := fetchUserCount () // ユーザー数を取得
fmt .Println (result )
result = calculateTotal () // 合計金額を計算(同じ変数を再利用)
fmt .Println (result )
}
// 良い例
func processData () {
userCount := fetchUserCount ()
fmt .Println (userCount )
totalAmount := calculateTotal ()
fmt .Println (totalAmount )
}
参考リンク core
coding-standard
2025/11/16
Contents Replace switch/if-elseif branching with interfaces and polymorphism when appropriate.
(適切な場合、switch/if-elseif分岐をインターフェースとポリモーフィズムで置き換える)
解説 型や状態による分岐が多数存在すると、新しい型を追加する際にすべての分岐を更新する必要があり、変更が困難になります。インターフェースとポリモーフィズムを使用することで、Open-Closed原則に従った拡張可能な設計が実現されます。新しい型を追加する際、既存のコードを変更せずに済むため、バグの混入リスクが減少します。ただし、単純な分岐まで無理に抽象化する必要はありません。
具体例 // 悪い例(型による分岐が多数)
func CalculateShipping (orderType string , weight float64 ) float64 {
if orderType == "standard" {
return weight * 10
} else if orderType == "express" {
return weight * 20
} else if orderType == "overnight" {
return weight * 30
} else if orderType == "international" {
return weight * 50
}
return 0
}
func GetDeliveryDays (orderType string ) int {
if orderType == "standard" {
return 5
} else if orderType == "express" {
return 2
} else if orderType == "overnight" {
return 1
} else if orderType == "international" {
return 10
}
return 0
}
// 良い例(ポリモーフィズム使用)
type ShippingMethod interface {
CalculateShipping (weight float64 ) float64
GetDeliveryDays () int
}
type StandardShipping struct {}
func (s StandardShipping ) CalculateShipping (weight float64 ) float64 {
return weight * 10
}
func (s StandardShipping ) GetDeliveryDays () int {
return 5
}
type ExpressShipping struct {}
func (e ExpressShipping ) CalculateShipping (weight float64 ) float64 {
return weight * 20
}
func (e ExpressShipping ) GetDeliveryDays () int {
return 2
}
type OvernightShipping struct {}
func (o OvernightShipping ) CalculateShipping (weight float64 ) float64 {
return weight * 30
}
func (o OvernightShipping ) GetDeliveryDays () int {
return 1
}
// 新しいタイプを追加しても既存コードは変更不要
type InternationalShipping struct {}
func (i InternationalShipping ) CalculateShipping (weight float64 ) float64 {
return weight * 50
}
func (i InternationalShipping ) GetDeliveryDays () int {
return 10
}
// 使用例
func ProcessOrder (method ShippingMethod , weight float64 ) {
cost := method .CalculateShipping (weight )
days := method .GetDeliveryDays ()
fmt .Printf ("Cost: %.2f, Days: %d\n" , cost , days )
}
参考リンク core
coding-standard
2025/11/16
Contents Even if a file exceeds 200 lines, do not place multiple classes (structs) in it. If you have multiple classes in a file over 200 lines, split them into separate files.
(ファイルが200行を超えても、複数のクラス(構造体)を配置しない。200行を超えるファイルに複数のクラスがある場合は、別々のファイルに分割する)
解説 1ファイル1クラスの原則に従うことで、ファイルの責任が明確になり、目的のクラスを素早く見つけられます。複数のクラスが1ファイルにあると、ファイルが肥大化し、コードレビューやマージコンフリクトの解決が困難になります。特に200行を超える大きなファイルでは、クラスを分離することで可読性と保守性が大幅に向上します。ファイル名とクラス名を一致させることも推奨されます。
具体例 // 悪い例(1ファイルに複数のクラス)
// user_service.go (300行)
package service
type UserService struct {
db Database
}
func (s * UserService ) CreateUser (name string ) error {
// ... 100行の実装
}
type OrderService struct {
db Database
}
func (s * OrderService ) CreateOrder (items []Item ) error {
// ... 100行の実装
}
type PaymentService struct {
gateway PaymentGateway
}
func (s * PaymentService ) ProcessPayment (amount float64 ) error {
// ... 100行の実装
}
// 良い例(クラスごとに分離)
// user_service.go (100行)
package service
type UserService struct {
db Database
}
func (s * UserService ) CreateUser (name string ) error {
// ... 実装
}
// order_service.go (100行)
package service
type OrderService struct {
db Database
}
func (s * OrderService ) CreateOrder (items []Item ) error {
// ... 実装
}
// payment_service.go (100行)
package service
type PaymentService struct {
gateway PaymentGateway
}
func (s * PaymentService ) ProcessPayment (amount float64 ) error {
// ... 実装
}
参考リンク core
coding-standard
2025/11/16
Contents Don’t use getters unnecessarily.
(不必要にゲッターを使わない)
解説 すべてのフィールドに機械的にゲッターを用意することは、カプセル化の本質を見失った形式的な実装です。ゲッターは、外部に公開する必要がある情報に対してのみ提供すべきです。不必要なゲッターは、内部実装の詳細を露出させ、クラスの責任を不明確にします。本当に必要な場合のみゲッターを作成することで、適切なカプセル化が実現され、変更の影響範囲が限定されます。
具体例 // 悪い例(すべてのフィールドにゲッターを用意)
type Order struct {
id string
customerID string
items []Item
subtotal float64
tax float64
shipping float64
createdAt time .Time
updatedAt time .Time
}
// すべてのフィールドに機械的にゲッターを用意
func (o * Order ) ID () string { return o .id }
func (o * Order ) CustomerID () string { return o .customerID }
func (o * Order ) Items () []Item { return o .items }
func (o * Order ) Subtotal () float64 { return o .subtotal }
func (o * Order ) Tax () float64 { return o .tax }
func (o * Order ) Shipping () float64 { return o .shipping }
func (o * Order ) CreatedAt () time .Time { return o .createdAt }
func (o * Order ) UpdatedAt () time .Time { return o .updatedAt }
// 良い例(本当に必要なゲッターのみ提供)
type Order struct {
id string
customerID string
items []Item
subtotal float64
tax float64
shipping float64
createdAt time .Time
updatedAt time .Time
}
// 外部に公開が必要な情報のみゲッターを提供
func (o * Order ) ID () string {
return o .id
}
func (o * Order ) CustomerID () string {
return o .customerID
}
// 計算されたtotalのみ公開(内訳は公開しない)
func (o * Order ) Total () float64 {
return o .subtotal + o .tax + o .shipping
}
// ItemsのゲッターではなくItemの数のみ公開
func (o * Order ) ItemCount () int {
return len(o .items )
}
// さらに良い例(ドメインメソッドとして提供)
func (o * Order ) IsExpired () bool {
return time .Since (o .createdAt ) > 30 * 24 * time .Hour
}
func (o * Order ) CanBeCancelled () bool {
return time .Since (o .createdAt ) < 24 * time .Hour
}
// 内部計算フィールドは完全に隠蔽
func (o * Order ) calculateSubtotal () float64 {
total := 0.0
for _ , item := range o .items {
total += item .Price * float64(item .Quantity )
}
return total
}
func (o * Order ) calculateTax () float64 {
return o .subtotal * 0.1
}
参考リンク core
coding-standard
2025/11/16
Contents Leave no TODOs or partially implemented code.
(TODOや部分的に実装されたコードを残さない)
解説 TODOコメントや未実装のコードは、不完全な状態でコードがマージされていることを示します。これらは時間とともに忘れられ、技術的負債として蓄積します。コードレビュー時にもノイズとなり、本当に重要な問題を見逃す原因となります。すべての機能は完全に実装され、テストが通った状態でコミットされるべきです。やむを得ず後回しにする場合は、課題管理システムに登録し、コード内には残しません。
具体例 // 悪い例
func ProcessOrder (order Order ) error {
// TODO: バリデーション追加
if err := saveOrder (order ); err != nil {
return err
}
// TODO: メール送信実装
// sendConfirmationEmail(order.Email)
return nil
}
// 良い例
func ProcessOrder (order Order ) error {
if err := validateOrder (order ); err != nil {
return err
}
if err := saveOrder (order ); err != nil {
return err
}
if err := sendConfirmationEmail (order .Email ); err != nil {
return err
}
return nil
}
func validateOrder (order Order ) error {
if order .Amount <= 0 {
return errors .New ("invalid amount" )
}
return nil
}
func sendConfirmationEmail (email string ) error {
// 完全に実装済み
return emailClient .Send (email , "Order Confirmed" )
}
参考リンク core
coding-standard
2025/11/16
Contents Never store sensitive information (e.g., API keys) in plain text within the project. Fetch them at runtime from environment variables or external services such as SSM Parameter Store. Encrypted storage is acceptable, but do not bundle the decryption key in the project.
(機密情報(APIキーなど)をプロジェクト内にプレーンテキストで保存しない。実行時に環境変数やSSM Parameter Storeなどの外部サービスから取得する。暗号化ストレージは許容されるが、復号化キーをプロジェクトに含めてはいけない)
解説 APIキーやパスワードなどの機密情報をソースコードに直接記述することは、重大なセキュリティリスクです。コードリポジトリが漏洩した場合、即座に悪用される可能性があります。環境変数や外部の秘密管理サービスを使用することで、機密情報とコードを分離し、アクセス制御が可能になります。暗号化する場合も、復号化キーを同じリポジトリに含めては意味がありません。
具体例 // 悪い例
const APIKey = "sk_live_1234567890abcdef" // ハードコード
func callExternalAPI () {
client := NewAPIClient (APIKey )
client .Request ()
}
// 良い例
func callExternalAPI () {
apiKey := os .Getenv ("API_KEY" ) // 環境変数から取得
if apiKey == "" {
log .Fatal ("API_KEY not set" )
}
client := NewAPIClient (apiKey )
client .Request ()
}
// さらに良い例(AWS SSM使用)
func getAPIKey () (string , error ) {
sess := session .Must (session .NewSession ())
svc := ssm .New (sess )
param , err := svc .GetParameter (& ssm .GetParameterInput {
Name : aws .String ("/myapp/api_key" ),
WithDecryption : aws .Bool (true ),
})
if err != nil {
return "" , err
}
return * param .Parameter .Value , nil
}
参考リンク core
coding-standard
2025/11/16
Contents Do not write comments that merely restate the code (e.g., int a // declare integer a).
(コードを単に言い換えるだけのコメントを書かない(例:int a // 整数aを宣言))
解説 コードをそのまま言い換えただけのコメントは、情報を追加せず、コードのノイズとなります。コメントは、コードから読み取れない意図や理由、背景を説明するべきです。自明な内容をコメントで繰り返すことは時間の無駄であり、コメントのメンテナンスコストを不必要に増加させます。意味のあるコメントのみを書くことで、コードの可読性が向上します。
具体例 // 悪い例(自明な内容の繰り返し)
// increment i by 1
i ++
// create a new user
user := User {}
// check if name is empty
if name == "" {
return errors .New ("name is required" )
}
// loop through all items
for _ , item := range items {
// process item
process (item )
}
// 良い例(意図や理由を説明)
// ユーザーIDは1から開始(0は予約済み)
i := 1
// デフォルト値で初期化(後でAPIレスポンスで上書き)
user := User {Status : "pending" }
// 空の名前は無効(ビジネスルール)
if name == "" {
return errors .New ("name is required" )
}
// すべてのアイテムに対して在庫を減算
// エラーが発生してもロールバックしない(べき等性保証)
for _ , item := range items {
process (item )
}
// コメント不要な例(コード自体が明確)
func calculateTax (price float64 ) float64 {
return price * 0.1
}
if user .IsActive () {
sendNotification (user )
}
参考リンク core
coding-standard
2025/11/16
Contents Do not swallow errors.
(エラーを握りつぶさない)
解説 エラーを無視したり握りつぶしたりすると、問題の発生源を特定できず、デバッグが著しく困難になります。エラーは適切にログに記録するか、呼び出し元に返すべきです。エラーを無視することは、システムの健全性を監視できなくなり、予期しない動作やデータ破損につながる可能性があります。すべてのエラーは、少なくともログ記録または上位層への伝播のいずれかで処理されるべきです。
具体例 // 悪い例(エラーを握りつぶす)
func ProcessData () {
data , err := fetchData ()
if err != nil {
// エラーを無視
}
_ = saveData (data ) // エラーを無視
}
func SendEmail (to string ) {
err := emailClient .Send (to , "Hello" )
// errをチェックせずに無視
}
// 良い例(適切なエラー処理)
func ProcessData () error {
data , err := fetchData ()
if err != nil {
return fmt .Errorf ("failed to fetch data: %w" , err )
}
if err := saveData (data ); err != nil {
return fmt .Errorf ("failed to save data: %w" , err )
}
return nil
}
func SendEmail (to string ) error {
if err := emailClient .Send (to , "Hello" ); err != nil {
return fmt .Errorf ("failed to send email to %s: %w" , to , err )
}
return nil
}
// ログ記録する場合
func BackgroundTask () {
if err := doSomething (); err != nil {
log .Printf ("background task failed: %v" , err )
// 呼び出し元に返せない場合はログに記録
}
}
参考リンク core
coding-standard