[Go 再学習] Go の interface を理解する

Go の再学習をしている最中なのですが、学習当初 Go Interface は「なんとなく分かるが使いこなせない」という感覚を持っていました。自分のコードでも使っているのですが、どのようなパターンで使えるのかを網羅的には知っていない状態だったのでこれを機に調べてみました。この記事では基本的な定義から実務でよく登場するパターンまでをサンプルコードと共に整理しました。 コードは以下のレポジトリにあります。 https://github.com/jedipunkz/go-tips interface とは interface はメソッドのシグネチャの集合を定義する型です。ある型が interface に定義されたメソッドをすべて持っていれば、自動的にその interface を満たします。 この設計により、既存のコードを変更せずに後から interface に適合させることができます。 パターン1: 基本的な interface 最もシンプルな interface の定義と利用です。Shape interface を定義し、Circle と Rectangle がそれを実装します。 使い所 図形の面積や周長を計算する処理を書く場合、Circle や Rectangle ごとに別々の関数を用意すると、新しい図形が増えるたびに呼び出し側の修正が必要になります。Shape interface を定義して関数が Shape を受け取るようにすると、新しい図形を追加しても既存の関数はそのまま使え、拡張が容易になります。 package main import ( "fmt" "math" ) // Shape インターフェースを定義する // メソッドセットを持つ型はこのインターフェースを満たす type Shape interface { Area() float64 Perimeter() float64 } type Circle struct { Radius float64 } func (c Circle) Area() float64 { return math.Pi * c.Radius * c.Radius } func (c Circle) Perimeter() float64 { return 2 * math.Pi * c.Radius } type Rectangle struct { Width, Height float64 } func (r Rectangle) Area() float64 { return r.Width * r.Height } func (r Rectangle) Perimeter() float64 { return 2 * (r.Width + r.Height) } // インターフェース型を引数に取ることで、どの Shape 実装でも受け付ける func printShapeInfo(s Shape) { fmt.Printf("面積: %.2f, 周長: %.2f\n", s.Area(), s.Perimeter()) } func main() { c := Circle{Radius: 5} r := Rectangle{Width: 4, Height: 6} fmt.Print("Circle: ") printShapeInfo(c) fmt.Print("Rectangle: ") printShapeInfo(r) } printShapeInfo は Shape を受け取るだけで、Circle か Rectangle かを意識しません。新たに Triangle を追加したとしても、Area() と Perimeter() を実装するだけで既存コードの変更なく動きます。 ...

2026-04-13 · 5 分 · jedipunkz

[Go 再学習] Go の goroutine と channel を理解する

再び Go の学習を再開していて、以前理解が曖昧だった点を重点的に学び直しています。そのうちの1つ goroutine, channel について記事にしょうと思います。 Go の並行処理は goroutine と channel という2つの仕組みを中心に設計されています。channel を通じてデータをやり取りすることで安全な並行処理を実現できます。この記事ではサンプルコードを交えて解説します。 コードは以下のレポジトリにあります。 https://github.com/jedipunkz/go-tips goroutine とは goroutine は Go のランタイムが管理する軽量なスレッドです。go キーワードをつけて関数を呼び出すだけで並行実行できます。 通常の関数呼び出しは呼び出し元が処理完了を待ちますが、go をつけると呼び出し元はすぐに次の処理へ進み、関数は別の goroutine で並行して動きます。 channel とは channel は goroutine 間でデータを安全にやり取りするためのパイプです。make(chan T) で作成し、<- 演算子で送受信します。 送信: ch <- value 受信: value := <-ch channel を使うと goroutine 間で双方向にデータをやり取りできます。 channel にはバッファなしとバッファありの2種類があります。バッファなし channel は送受信が揃うまでブロックするため、goroutine 間の同期点として機能します。バッファあり channel はバッファに空きがある限り送信側はブロックせず先へ進めます。 パターン1: 基本的な channel の使い方 まずは最もシンプルなパターンです。producer goroutine が値を送信し、main goroutine が受信します。 使い所 Web から複数のファイルをダウンロードして DB に保存するような処理を考えると、ネットワーク待ちや DB への書き込み待ちがほとんどで CPU はほぼ遊んでいます。こういった I/O バウンドな処理では、1件ずつ順番に処理するより goroutine と channel でパイプライン化するほうが効率的です。たとえば「URL リストを読み込む goroutine」と「その URL から HTTP GET する goroutine」を分離し、channel でつなぐことで取得と後続処理を並行して進められます。 ...

2026-04-04 · 5 分 · jedipunkz

複数の AI コーディングエージェントを1つのターミナルから起動・監視するツールを作った

jedipunkz です。 複数の AI コーディングエージェントを、それぞれ独立した git worktree で並列に走らせ、その状態を1つのターミナルからまとめて見る。そのための CLI ツール agx を Go で作りました。この記事では作った動機と、使い方、内部の設計について書きます。 リポジトリはこちらです: https://github.com/jedipunkz/agx なぜ作ったか Claude Code を日常的に使う中で、複数のエージェントを同時に動かしたいというシーンが増えてきました。例えば複数の独立したタスクを並列で走らせたいとき、それぞれのエージェントが今どんな状態にあるかをターミナル1つで把握したいと思っていました。 以前Zellij を使って Claude Code のマルチエージェント並列・直列実行環境を作った という記事を書いたのですが、これは1つのレポジトリに対して複数の機能開発・リファクタ・その他修正を並列でそれぞれ別の Claude Code で行い最終的にそれぞれの git worktree 上の差分をマージする統合管理 Claude Code を実現するものでした。これは Claude Code 純正の機能 Sub Agents とは異なり複数の Claude Code をオーケストレーションするモノとして利用していたのですが、対象が1つのレポジトリに限定されること、また最終的にそれぞれの差分をマージするのであれば Sub Agents で事足りる事も多くなってきていました。 それに対して最近の要件としては下記のように変化してきました。 複数のレポジトリの修正を行いたい それぞれのエージェントのオーケストレーションよりも、それぞれの可視化と管理をしたい つまり必要なのはオーケストレーターではなく、並列に動いている多数のエージェントを一望できるダッシュボードでした。そこで作ったのが agx です。 agx とは agx は「1つのターミナルから複数の AI コーディングエージェントを起動・監視する」ための CLI ツールです。できることは大きく3つです。 隔離された環境でエージェントを起動する — git リポジトリ内で実行すると専用の git worktree を作り、その中でエージェントを動かします。互いの変更もメインの作業ツリーも汚しません 全エージェントの状態を一望する — TUI ダッシュボードで、どれが処理中で、どれが入力待ちで、どれが終わったのかをリアルタイムに表示します 別のターミナルから扱う — 実行中のエージェントのログ追従・差分表示・終了待ち・入力送信を、起動した端末とは別の場所から行えます 対応しているエージェントは Claude Code / Codex CLI / Gemini CLI / OpenCode の4つで、起動時に -a で選びます。 ...

2026-03-14 · 4 分 · jedipunkz

Go によるコマンド履歴・ファイル・Git ブランチ/Worktree/コミットを検索する Fish Plugin 開発

jedipunkz です。 今回は自作した Fish シェルの Plugin である fuzz.fish を紹介します。 fuzz.fish は Fish Shell の コマンド履歴・ファイル・Git ブランチ・Git Worktree・Git コミット をインクリメンタルサーチできる Fish Plugin です。Go と Bubble Tea で TUI を実装していて、fzf や fd、ripgrep、bat といった外部ツールは一切不要です。プラグイン本体と単一の Go バイナリだけで動きます。 リンク ソースコード: https://github.com/jedipunkz/fuzz.fish 公式サイト: https://jedipunkz.rocks/fuzz.fish/ スクリーンショット コマンド履歴・ファイル・Git ブランチ検索を切り替えている様子です。 開発動機 幾つか小さなツールはこれまでも作ってきましたが、利用する機会が減るとメンテナンスも怠りがちなことに気が付き、自分自身がよく使うツールを作ろうと思ったのがきっかけです。そして普段から Fish をシェルとして使っていますが、コマンド履歴検索や Git ブランチの切り替えをより効率的に出来れば何より自分にとって便利なツールになる予感がありました。 主な機能 外部ツールが不要。fzf / fd / ripgrep / bat をインストールしなくても、プラグインと Go バイナリだけで完結する キーバインドは ctrl+r の 1 つ。起動後は ctrl+s ctrl+w ctrl+g ctrl+x で閉じずにモードを切り替えられる 全モードにプレビューがある。履歴は実行時刻・ディレクトリ・前後のコマンド、ファイルはシンタックスハイライト付きの中身、Git 系はそれぞれの文脈を表示する 履歴は frecency で並ぶ。マッチ品質を最優先にした上で、log1p(頻度) を最終実行時刻で減衰させるため、実際によく叩くコマンドが上に来る Git はブランチだけでなく Worktree とコミットも扱える。コミット検索はハッシュと件名の両方にマッチし、git show / git diff / git revert / git cherry-pick をプロンプトに置く(実行はしない) インストール方法 必要なのは Fish Shell 3.0+ のみです。Fisher を使ってインストールします。 ...

2026-01-17 · 2 分 · jedipunkz

Go 初学者が学ぶクリーンアーキテクチャ

自分は Go の初学者なのですがクリーンアーキテクチャを学ぶために幾つかの書籍を読んでみたもののなかなかしっくりと理解が出来ていない状態でした。 そこで AI に極力シンプルなコードを書かせて理解するという事をやってみたのですが、なかなかいい感じに理解が進んだのでここで記事にしたいと思っています。 クリーンアーキテクチャとは? クリーンアーキテクチャは、Robert C. Martin によって提唱されたソフトウェア設計原則で最も重要な特徴は依存関係の方向性にあります。 従来のアーキテクチャとは異なり、内側の層は外側の層を知らないという原則に基づいています。これにより以下のメリットが得られます。 テスタビリティ: ビジネスロジックを単体でテスト可能 柔軟性: データベースや Web フレームワークの変更が容易 保守性: 関心の分離により変更の影響範囲を限定 実装するシステムの全体像 今回 AI に実装させたのは RESTful API を提供するユーザー管理システムです。 コードは下記のレポジトリにあります。 https://github.com/jedipunkz/go-clean-architecture-playground システムは以下の4つの層で構成されています: Entity レイヤ - ビジネスの核となるルール Interface レイヤ - 抽象化による疎結合 Use Case レイヤ - ビジネスロジックの実装 Infrastructure レイヤ - 外部システムとの連携実装 Controller レイヤ - HTTP APIの提供 各層の詳細実装 1. Entity レイヤ - ビジネスの核となるルール 最も内側の層から実装を始めます。エンティティ層は他のどの層にも依存しない純粋なビジネスロジックです。 // entity/user.go package entity import ( "errors" "time" ) type User struct { ID int `json:"id"` Name string `json:"name"` Email string `json:"email"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` } func NewUser(name, email string) (*User, error) { if name == "" { return nil, errors.New("名前は必須です") } if email == "" { return nil, errors.New("メールアドレスは必須です") } now := time.Now() return &User{ Name: name, Email: email, CreatedAt: now, UpdatedAt: now, }, nil } func (u *User) UpdateInfo(name, email string) error { if name == "" { return errors.New("名前は必須です") } if email == "" { return nil, errors.New("メールアドレスは必須です") } u.Name = name u.Email = email u.UpdatedAt = time.Now() return nil } この User エンティティは、クリーンアーキテクチャの最も内側に位置する部分です。ここで重要なのは、このエンティティが外部の何にも依存していないことです。データベースがMySQLなのかPostgreSQLなのか、WebフレームワークがGinなのかEchoなのか、そういった技術的な詳細は一切知らないのが特徴になっています。 ...

2025-08-01 · 4 分 · jedipunkz

ECS コンテナにログインする CLI を Go 言語で作った話

こんにちは @jedipunkz 🚀 です。 今回は Go 言語で ECS コンテナにログインする CLI を作った話を書きます。 開発の経緯 自分はまだ Go 言語の初学者で学習のために開発するアイデアを探していた状態でした。そこで自分の勤めている会社で ECS Execute 機能を使ったコンテナログインの機能を開発者に提供していた事を思い出し色々調べていて「もう少し手間が省けないか?」と思い立った、という経緯で開発をはじめました。 awscli を使った ECS Execute 機能によるコンテナログイン 手間が多いと書きましたが実際に awscli を使う場合どの程度の手間があるのか簡単に記します。まず下記のコマンドを実行して $ aws ecs list-tasks --cluster <クラスタ名> --service <サービス名> taskArn が得られるので Arn から task ID を拾って、その task ID を使って $ aws ecs execute --cluster <クラスタ名> \ --task <task ID> \ -- container <コンテナ名> \ --interfactive \ --command "sh" とコマンドを実行することでコンテナにログイン出来ます。が手間が少し多いのと task ID を拾い出す作業も辛いので改善したい…。 操作画面 ということで miniecs という CLI を作ったのですが、 まずは操作している様子を貼り付けます。😷 Fuzzy Finder なインクリメンタルサーチが出来る CLI になっていて、ECS クラスタ名・ECS サービス名・コンテナ名を一部入力するとログインしたい環境が選択出来るツールになっています。 ...

2022-02-05 · 2 分 · jedipunkz