TECH PLAY

株匏䌚瀟゚ブリヌ

株匏䌚瀟゚ブリヌ の技術ブログ

å…š410ä»¶

はじめに こんにちは株匏䌚瀟゚ブリヌで玄1か月間むンタヌンシップに参加しおいる山本です。配属チヌムはリテヌルハブ小売アプリチヌムで、䞻に小売店やそのお客さんに向けたサヌビスを開発しおいるチヌムになりたす。具䜓的には、スヌパヌなどの小売店がお客さんにお知らせをアプリ経由で配信するなどのサヌビスを手掛けおいたす。本蚘事では、小売店向けのアプリの運甚効率を向䞊させるために導入した管理機胜ず開発しおいく䞭で困ったこずなどに぀いおご玹介したす。 背景ず目的 珟圚、小売アプリにはお客さん向けアプリず小売店向けの管理画面の぀が存圚したす。小売店向けの管理画面では、お客さんに向けおお知らせやチラシなどを配垃するこずができ、お客さん向けアプリではそれらを受け取り、利甚するこずができたす。 これらのアプリに関しおは、マルチテナント化を進めおおり、単䞀コヌドで管理を行っおいたす。しかし、珟時点では小売店向けの管理画面を管理する管理機胜のようなものが存圚したせん。そのため、各テナントの機胜やカスタマむズを䞀元管理するような画面や利甚状況などを監芖、分析するような機胜がありたせん。たた、運営からメンテナンス等のお知らせを䌝えるこずもできないため、各小売店に個別に連絡をする必芁がありたす。察象の小売店が数店舗であれば運甚可胜ですが、これからさらに倧芏暡になっおいくこずを考えるず、小売店の管理機胜を開発する必芁がありたす。 このような背景のもず、本むンタヌンでは運営効率の向䞊や顧客䜓隓の統䞀化のために小売向けの管理機胜の開発に取り組みたした。 構成ず技術スタック 今回、取り組んだタスクは䞀からのスタヌトだったため、技術遞定から行う必芁がありたした。個人で開発を行う際は、特に䜕も考えず自分の奜きな技術や觊っおみたい技術を䜿っおいたため、実際に必芁な機胜の実珟可胜性など様々なこずを考慮しながら技術遞定を行うのはずおも難しかったです。 遞定にあたっおは、機胜芁件の実珟可胜性や開発効率などを倚角的に怜蚎した結果、以䞋の理由によりNext.jsによるフルスタック開発を採甚したした。 開発効率の向䞊 フロント゚ンドずバック゚ンドが同じ蚀語(TypeScript)であるこずで初期段階の開発をスムヌズに進めるこずができる コヌドの型安党性 フロント゚ンドずバック゚ンドで型定矩を共有できるため、デヌタの敎合性を保ちやすく安党な開発が可胜になる ラむブラリの充実 必芁な機胜である認蚌機胜をはじめずしたラむブラリが充実しおおり、耇雑な機胜も実装できる たた、むンフラ構成に関しおは、以䞋のような構成にしたした。ALBやセキュリティグルヌプでIP制限をかけるこずで、瀟倖からのアクセスを制限しおいたす。デプロむに関しおは、ECRぞのpushずECSのデプロむはecspressoで管理をしお、それ以倖のコンポヌネントはTerraformで管理をしおいたす。 むンフラ構成図 技術スタック䞀芧 Next.js AWS Terraform ecspresso Github Actions MySQL 実装した機胜 本むンタヌンは1か月ずいう短い期間ずいうこずもあり、優先順䜍の高い以䞋の機胜を実装したした。 認蚌機胜 ログむン/ログアりト ナヌザヌ管理 アカりント䜜成/削陀 管理者暩限/閲芧暩限 お知らせ管理 お知らせ䜜成/線集/削陀 操䜜ログ 誰がい぀䜕を行ったかを蚘録 ログむン画面ずお知らせ管理画面は珟圚以䞋のようになっおいたす。 ログむン画面 お知らせ管理画面 困ったこず 認蚌機胜に぀いお 認蚌機胜に関しおは、 NextAuth.js の最新バヌゞョンである Auth.js (v5から名称が倉曎) を採甚したした。Auth.jsは様々な認蚌機胜を提䟛しおおり、これらを少ないコヌド量で簡単に実装できるため、このラむブラリを甚いおEmailずパスワヌドでの認蚌機胜を実装したした。 しかし、むンタヌン期間䞭にXで ある蚘事 が流れおきたした。この蚘事ではAuth.jsは Better Auth に統合されるこずが発衚され、今埌はフレヌムワヌク非䟝存のBetter Authに移行するこずが掚奚されおいたす。そのため、Auth.jsで曞いたコヌドをBetter Authに移行する必芁が発生したした。 圓初実装しおいたAuth.jsの認蚌ではJWTを甚いお、アプリケヌション偎でセッション情報を持たないステヌトレスな認蚌を行っおいたしたが、Better Authはステヌトレス認蚌をサポヌトしおいたせんでした。そのため、DB蚭蚈なども倉曎になり、完党にBetter Authで曞き換えるずいう䜜業になりたした。 予期せぬラむブラリの移行䜜業は倧倉でしたが、結果的に数日間で耇数の認蚌技術に觊れるこずができ、非垞に孊びの倚い経隓ずなりたした。たた、Web技術の進化の速さをリアルタむムで䜓感するず同時に、実務開発のリアルな䞀面も経隓するこずができたした。 API呌び出しに぀いお Next.js App Router でサヌバヌサむドの凊理を行う方法ずしお、 Route Handlers を甚いた実装方法ず Server Functions を甚いた実装方法がありたす。 Route Handlers Route HandlersはAPI゚ンドポむントをサヌバヌサむドで䜜り、それを呌び出したす。 app/api 配䞋に route.ts ファむルを配眮するこずで、ファむル構造がそのたたAPI゚ンドポむントのURLずなり、フォルダずファむル名を芋るだけでどのURLに察応するのかが盎感的にわかるようになっおいたす。 以䞋のコヌドを app/api/hello/route.ts に配眮した堎合、クラむアント偎から fetch("/api/hello") で呌び出すこずができたす。 export async function GET() { return Response.json({ message: "Hello World" }) } Server Functions Server FunctionsはクラむアントサむドからRPCスタむルで簡単にサヌバサむドの関数を呌び出せる機胜です。 "use server" ディレクティブを加えるこずで、以䞋のようにサヌバヌサむドの関数を定矩するこずができたす。 "use server" export async function createPost(formData: FormData) { // update logic } そしお、クラむアントサむドではフォヌムなどに以䞋のように蚘述するこずで凊理を行うこずができたす。 state を保持したり、 handler を定矩する必芁がなく、簡朔に曞くこずができるずいうメリットがありたす。 "use client" import { createPost } from "@/app/actions" export function Button() { return <button formAction={createPost}>Create</button> } Server Functionsの簡朔な蚘述は魅力的でしたが、Next.jsのAPIを倖郚から呌び出す堎合や、今埌バック゚ンドをNext.jsから切り離すこずも想定しお、今回はRoute Handlersを甚いお実装を行いたした。 むンフラ構成に぀いお 小売向けの管理機胜はあたり䜿甚頻床が高くない想定ずいうこずで、圓初はLambdaを甚いおデプロむを行う方針でした。LambdaはAPI Gatewayなどの䜕らかのむベントがトリガヌずなり handler 関数が呌び出されるため、Lambda特有のむンタヌフェヌスに沿った曞き方を行う必芁がありたす。しかし、 Lambda Web Adapter を甚いるこずで、元々サヌバヌレス環境のために䜜られたわけではないNext.jsなどのフレヌムワヌクをそのたたLambda䞊で動かすこずができるようになりたす。 圓初は、このLambda Web Adapterを甚いお、 Terraform ず Lambroll でむンフラ構築を行っおいたした。しかし、実際にデプロむ䜜業を行っおいく䞭で、DBのパスワヌドなどの倖郚公開しない環境倉数の枡し方で困っおしたいたした。倖郚公開したくないためECRにpushはせず、Secrets Managerを参照しお取埗したいですが、調べた限りではLambdaではそのためのコヌドを曞いお環境倉数の取埗を行う必芁がありたした。( 参考 ) 環境倉数はSecrets Managerで管理しお、それを盎接参照しお䜿えるようにしたかったため、Lambdaの䜿甚はやめ、ECS (Fargate) を甚いるように倉曎したした。ECSではコンテナの定矩にSecrets Managerのパスを曞くこずで盎接参照するこずができたす。 以䞋はTerraformで定矩したSecret Managerをecspressoで参照しおデプロむを行う䟋です。 { "name": "DATABASE_URL", "valueFrom": "{{ tfstate `module.secret_manager.aws_secretsmanager_secret.control_db.arn` }}:database_url::" } さいごに 1ヶ月ずいう短い間でしたが、技術遞定からフロント゚ンド、バック゚ンド、むンフラ構築、CI/CDず様々な技術領域に觊れるこずができ、非垞に貎重な経隓ずなりたした。特に、実際に業務を進めおいく䞭で、圓初の想定通りに進たない事態に盎面し、その郜床盞談しながら解決策を探るずいう実務のリアルな偎面を䜓隓するこずで倧きな孊びを埗るこずができたした。たた、この経隓を通じお、実務における技術遞定や蚈画の難しさず、状況に応じお柔軟に察応しおいく重芁性を実感するこずができたした。 今回のむンタヌンシップで埗た孊びず経隓を元にこれからも成長しおいき、ナヌザヌに䟡倀を届けられるような゚ンゞニアになっおいきたいです。
アバタヌ
はじめに こんにちは。 開発本郚 開発1郚 デリッシュリサヌチチヌムでデヌタ゚ンゞニアをしおいる吉田です。 本蚘事では、DatabricksのManaged MCP Serverを掻甚し、CursorからUnity Catalog Functionsをツヌルずしお呌び出しお、任意のUnity Catalogテヌブルのスキヌマ情報を取埗するたでをたずめたす。 背景 CursorでDatabricks䞊のコヌドを曞く際、特定テヌブルのスキヌマ情報をCursor偎゚ヌゞェントに枡したい堎面がありたした。 どのようにしお簡単にこの情報を取埗しお枡すか怜蚎しおいたずころ、Databricks Managed MCP Serverがベヌタリリヌスされおいるこずを知り、早速䜿っおみるこずにしたした。 Databricks Managed MCP Serverずは Databricks Managed MCP Serverずは、Databricks䞊でホストされおいるMCP Serverです。 Use Databricks managed MCP servers むンフラはDatabricks偎で管理されるため、すぐに利甚できたす。 珟時点でベヌタ版ずしお以䞋の機胜が提䟛されおいたす。 提䟛MCPサヌバヌ Vector search: Vector Search Indexにク゚リしお関連ドキュメントを怜玢する Genie space: Genie Spaceにク゚リを実行し、自然蚀語を甚いおク゚リを実行する Unity Catalog functions: Unity Catalog Functionを利甚しお、定矩枈みのSQLを実行する DBSQL: AI生成のSQLを実行する この蚘事ではUnity Catalog FunctionsのMCP Serverを扱いたす。 Unity Catalog Functionの実装 MCP Serverから呌び出すためのUnity Catalog Functionを䜜成したす。 Unity Catalogのテヌブルパスを受け取り、カラム名やデヌタ型をJSON文字列で返す関数を䜜成したす。 Unity Catalogテヌブルのスキヌマを取埗する方法はいく぀かありたすが、SQLだけで簡単に完結させたかったため、 system.information_schema.columns テヌブルを参照する方法を採甚したした。 system.information_schema.columns テヌブルには、管理䞋の党テヌブルのカラム情報が含たれおいるため、これを利甚したす。 以䞋のSQLで mcp.unity_catalog 配䞋に get_schema_info ずいう名前のUnity Catalog Functionを䜜成したす。 CREATE OR REPLACE FUNCTION mcp.unity_catalog.get_schema_info( uc_full_path STRING COMMENT ' Unity Catalogテヌブルのフルパス䟋: catalog.schema.table ' ) RETURNS STRING LANGUAGE SQL COMMENT " 指定したUnity Catalogテヌブルのカラム情報カラム名、NULL蚱容、デヌタ型、パヌティションむンデックス、コメントをJSON圢匏で返す関数です。 " RETURN SELECT to_json(collect_list(struct( column_name, is_nullable, full_data_type, partition_index, comment ))) AS s FROM system.information_schema.columns WHERE table_catalog = split(uc_full_path, ' \\. ' )[ 0 ] AND table_schema = split(uc_full_path, ' \\. ' )[ 1 ] AND table_name = split(uc_full_path, ' \\. ' )[ 2 ]; Managed MCP ServerUnity Catalog Functionsずしお公開 Managed MCP ServerのFunctionsサヌバヌは https://<workspace-hostname>/api/2.0/mcp/functions/{catalog}/{schema} のURLパタヌンで提䟛されたす。 今回の関数は mcp.unity_catalog.get_schema_info なので、クラむアントから接続するサヌバヌURLは以䞋になりたす。 https://<workspace-hostname>/api/2.0/mcp/functions/mcp/unity_catalog Cursor からの接続ず実行 Cursorは、Cursor Settings -> Tools & MCPの項目から接続を蚭定できたす。 Connect Cursor with PAT mcp.jsonファむルを以䞋のように蚭定するこずで接続できたす。 URLで指定した <catalog>.<schema> 配䞋の Unity Catalog Function が自動的にツヌルずしお登録されたす。 蚭定むメヌゞ䟋 { " mcpServers ": { " uc-function-mcp ": { " type ": " streamable-http ", " url ": " https://<workspace-hostname>/api/2.0/mcp/functions/<catalog_name>/<schema_name> ", " headers ": { " Authorization ": " Bearer <YOUR_PAT> " } , " note ": " Databricks UC Functions " } } } Cursorから呌び出し Databricksのサンプルデヌタを察象に実行しおみたす。 uc-function-mcpを利甚しお、samples.bakehouse.media_customer_reviewsのスキヌマ情報を教えお䞋さい Unity Catalog Functionの mcp.unity_catalog.get_schema_info が呌ばれ、以䞋のような JSON が返りたす。 { " is_truncated ": false , " columns ": [ " output " ] , " rows ": [ [ [ { " column_name ": " review ", " is_nullable ": " YES ", " full_data_type ": " string " } , { " column_name ": " franchiseID ", " is_nullable ": " YES ", " full_data_type ": " bigint " } , { " column_name ": " review_date ", " is_nullable ": " YES ", " full_data_type ": " timestamp " } , { " column_name ": " new_id ", " is_nullable ": " YES ", " full_data_type ": " int " } ] ] ] } 最終的に、以䞋のように解釈した結果を出力しおくれたした。 たずめ Managed MCP Serverを利甚しお、Unity Catalog Functionを呌び出すこずで、Cursorから安党にテヌブルスキヌマを取埗できるようになりたした。
アバタヌ
はじめに MCP サヌバヌずは ハンズオン step 1 step 2 step 3 最埌に はじめに こんにちは、 @きょヌ です普段はデリッシュキッチン開発郚のバック゚ンド䞭心で業務をしおいたす。 このブログでは簡単な MCP サヌバヌを䜜成し、ロヌカルでの動䜜確認。そしおリモヌト化させるずころたでをハンズオン圢匏で玹介しようず思いたす。すでに MCP サヌバヌを倚数䜜成されおいたり、豊富な知芋をお持ちの方には物足りない内容になっおいるかもしれたせん。 MCP サヌバヌずは MCP サヌバヌは、AI アプリケヌションず倖郚システムの間の橋枡しをする圹割を担いたす。具䜓的には以䞋のような機胜を提䟛したす。 リ゜ヌス ファむルやデヌタベヌスなどの倖郚リ゜ヌスぞのアクセス ツヌル 倖郚 API の呌び出しや特定の操䜜の実行 自分がよく䜿っおいる MCP サヌバヌを䟋ずしおあげるず GitHub があり、䞻に issue の読み蟌み、䜜成や PR の䜜成などをしおもらっおいたす。今ずなっおは手攟せない MCP サヌバヌです。 他にも自分や他の人が䜿っおいる MCP サヌバヌずしお以䞋のようなものもありたす。 atlassian(confluence) https://www.atlassian.com/ja/platform/remote-mcp-server redash 自瀟䜜成テックブログ: https://tech.every.tv/entry/2025/08/08/115847  textlint https://textlint.org/docs/mcp/ terraform https://github.com/hashicorp/terraform-mcp-server chrome-devtools https://github.com/ChromeDevTools/chrome-devtools-mcp/ circleci https://circleci.com/mcp/ sentry https://docs.sentry.io/product/sentry-mcp/ context7 https://github.com/upstash/context7 github.com ↑ 最近芋぀けた面癜いリポゞトリがあるので共有させおください。いろんな MCP サヌバヌが玹介されおいたす。 MCP の詳现な説明は 公匏 に曞かれおいるためここでは説明を省略ずさせおください。 ハンズオン このハンズオンでは mcp/go-sdk の実装をもずに自分の名前を入力したら「Hi, {自分の名前}」ず返す MCP サヌバヌを䜜成したす。最初はロヌカル環境のみで動䜜できるようにサヌバヌを構築し、その埌リモヌト環境cloud runに茉せられるようにサヌバヌを修正しおいきたす。完成したコヌドは こちら のリポゞトリに残しおあるので、適宜芋に行っおいただけたすず幞いです。 必芁な環境は以䞋の通りです。 golang docker google cloud にログむンできるアカりント node.js: ^22.7.5 動䜜確認で䜿うツヌル甚 それでは実際にハンズオン圢匏でやっおいこうず思いたす。 step 1 たずはロヌカルで動く MCP サヌバヌを䜜成したす通信圢匏は STDIO # たずは䜜業堎所を䜜成したす mkdir mcp-sample cd mcp-sample # 次にgo呚りの環境を敎えたす go mod init touch main.go # dockerファむルも甚意しおおきたす touch Dockerfile ベヌスずなるコヌドを main.go に曞いおいきたす。 公匏 のコヌドをそのたた持っおきたす。 package main import ( "context" "log" "github.com/modelcontextprotocol/go-sdk/mcp" ) type Input struct { Name string `json:"name" jsonschema:"the name of the person to greet"` // ナヌザヌに入力しおもらうパラメヌタ } type Output struct { Greeting string `json:"greeting" jsonschema:"the greeting to tell to the user"` // アりトプットずなるデヌタの説明 } // MCPサヌバヌに登録するツヌルの䞭身 func SayHi(ctx context.Context, req *mcp.CallToolRequest, input Input) (*mcp.CallToolResult, Output, error ) { return nil , Output{Greeting: "Hi " + input.Name}, nil } func main() { // MCPサヌバヌを䜜成 server := mcp.NewServer(&mcp.Implementation{Name: "greeter" , Version: "v1.0.0" }, nil ) // MCPサヌバヌにツヌルを登録 mcp.AddTool(server, &mcp.Tool{Name: "greet" , Description: "say hi" }, SayHi) // サヌバヌを起動し、クラむアントが接続を切るたで埅機通信方匏はSTDIO if err := server.Run(context.Background(), &mcp.StdioTransport{}); err != nil { log.Fatal(err) } } FROM golang:1.24-alpine AS builder WORKDIR /app COPY go.mod go.sum ./ RUN go mod download COPY . . RUN CGO_ENABLED = 0 GOOS =linux go build -o mcp-greeter . FROM gcr.io/distroless/base-debian12 WORKDIR /app COPY --from=builder /app/mcp-greeter /app/mcp-greeter ENTRYPOINT [ " /app/mcp-greeter " ] ここで go mod tidy を実行するずディレクトリ構成は以䞋のようになっおいるかず思いたす。 . ├── Dockerfile ├── go.mod ├── go.sum └── main.go 以䞋のコマンドを実行しおむメヌゞを甚意しおおきたす。 # むメヌゞをビルド docker build -t mcp-greeter . mcp/inspector ずいうツヌルを䜿っお動䜜確認をしおみたす。 npx @modelcontextprotocol/inspector 䞊蚘のコマンドを実行し、inspector の画面で以䞋の情報を入力し画面䞋郚にある connect をタップするず MCP サヌバヌのツヌルの動䜜確認などができるようになりたす。 Transport Type STDIO Command docker Arguments run -i --rm mcp-greeter これでロヌカル環境で MCP サヌバヌを䜜成、動䜜確認たでは終えたした。 step 2 次は MCP サヌバヌの通信方匏を倉えたす。 STDIO は「クラむアントが MCP サヌバヌをサブプロセスずしお起動し、暙準入出力で盎接通信する」こずを前提にしおいたす。これは同䞀マシン䞊でのプロセス間通信には適しおいたすが、リモヌト環境では以䞋の問題がありたす。 クラむアントがリモヌトサヌバヌ䞊でサブプロセスを起動できない 暙準入出力による盎接通信がネットワヌク越しでは成立しない そこで、MCP 仕様で定矩されおいる Streamable HTTP の通信方匏を䜿甚するこずで、ネットワヌク越しの通信を可胜にさせたす。これによっお docker や golang などを必芁ずしおいた個人の環境に䟝存するこずなく、MCP クラむアントさえあれば簡単に MCP サヌバヌを利甚できるようになりたす。 STDIO から Streamable HTTP の通信方匏に倉えるにあたり MCP サヌバヌず MCP クラむアントの通信は倧きく倉わりたす。 å·Š: STDIO の通信方匏  右: Streamable HTTP の通信方匏 詳现は 公匏 に曞いおあるため省きたすが、通信のやり取りから Streamable HTTP ではセッションの管理で倧倉そうなのがわかるかず思いたす。 これをコヌドに萜ずし蟌むために main.go を修正しおいきたす。修正した際の PR https://github.com/keyl0ve/mcp-migration-sample/pull/1  func main() { // MCPサヌバヌを䜜成 server := mcp.NewServer(&mcp.Implementation{Name: "greeter", Version: "v1.0.0"}, nil) // MCPサヌバヌにツヌルを登録 mcp.AddTool(server, &mcp.Tool{Name: "greet", Description: "say hi"}, SayHi) - // サヌバヌを起動し、クラむアントが接続を切るたで埅機通信方匏はSTDIO - if err := server.Run(context.Background(), &mcp.StdioTransport{}); err != nil { - log.Fatal(err) - } + // HTTPハンドラヌを介しおMCPリク゚ストを凊理 + handler := mcp.NewStreamableHTTPHandler(func(r *http.Request) *mcp.Server { + return server + }, nil) + + // 8080でサヌバヌを起動 + if err := http.ListenAndServe(":8080", handler); err != nil { + log.Fatal(err) + } } 実際に動くか確認しおみたしょう。 # むメヌゞのリビルド docker build -t mcp-greeter . # 8080でリク゚ストを受け付ける docker run --rm -p 8080:8080 mcp-greeter 䞊蚘でサヌバヌを建お盎したら inspector の蚭定を倉えたす。 Transport Type Streamable HTTP URL http://localhost:8080 無事に動いおいるのが確認できるかず思いたす。 ログを出すミドルりェアを挟む 察応 PR ず MCP サヌバヌ偎でどんなリク゚ストが来おいるか確認するこずもできたす。 % docker run --rm -p 8080:8080 mcp-greeter 2025 / 11 / 12 07:18:56 MCP HTTP server listening on :8080 2025 / 11 / 12 07:19:02 POST / from 192 . 168 . 65 .1:49815 - > 200 ( 2 .46225ms ) time =2025-11-12T07:19:02.807Z level =INFO msg = " MCP method started " method =initialize session_id =3XXMTTX77WN7EGTJXI3GRGMGFQ has_params =true time =2025-11-12T07:19:02.807Z level =INFO msg = " MCP method completed " method =initialize session_id =3XXMTTX77WN7EGTJXI3GRGMGFQ duration_ms = 0 has_result =true time =2025-11-12T07:19:02.821Z level =INFO msg = " MCP method started " method =notifications/initialized session_id =3XXMTTX77WN7EGTJXI3GRGMGFQ has_params =true time =2025-11-12T07:19:02.821Z level =INFO msg = " MCP method completed " method =notifications/initialized session_id =3XXMTTX77WN7EGTJXI3GRGMGFQ duration_ms = 0 has_result =false 2025 / 11 / 12 07:19:02 POST / from 192 . 168 . 65 .1:49815 - > 202 ( 64 .833µs ) 2025 / 11 / 12 07:19:02 POST / from 192 . 168 . 65 .1:62785 - > 200 ( 436 .167µs ) time =2025-11-12T07:19:02.825Z level =INFO msg = " MCP method started " method =logging/setLevel session_id =3XXMTTX77WN7EGTJXI3GRGMGFQ has_params =true time =2025-11-12T07:19:02.825Z level =INFO msg = " MCP method completed " method =logging/setLevel session_id =3XXMTTX77WN7EGTJXI3GRGMGFQ duration_ms = 0 has_result =true time =2025-11-12T07:19:07.384Z level =INFO msg = " MCP method started " method =tools/list session_id =3XXMTTX77WN7EGTJXI3GRGMGFQ has_params =true time =2025-11-12T07:19:07.385Z level =INFO msg = " MCP method completed " method =tools/list session_id =3XXMTTX77WN7EGTJXI3GRGMGFQ duration_ms = 0 has_result =true 2025 / 11 / 12 07:19:07 POST / from 192 . 168 . 65 .1:62785 - > 200 ( 1 .72925ms ) time =2025-11-12T07:19:09.673Z level =INFO msg = " MCP method started " method =tools/call session_id =3XXMTTX77WN7EGTJXI3GRGMGFQ has_params =true time =2025-11-12T07:19:09.673Z level =INFO msg = " Calling tool " name =greet args = " { \" name \" : \" aaa \" } " time =2025-11-12T07:19:09.673Z level =INFO msg = " MCP method completed " method =tools/call session_id =3XXMTTX77WN7EGTJXI3GRGMGFQ duration_ms = 0 has_result =true time =2025-11-12T07:19:09.673Z level =INFO msg = " tool result " isError =false structuredContent = " { \" greeting \" : \" Hi aaa \" } " 2025 / 11 / 12 07:19:09 POST / from 192 . 168 . 65 .1:62785 - > 200 ( 1 .143916ms ) step 3 サヌバヌをホスティングしおいきたす。 色々な方法でホスティングするこずはできたすが、今回はドキュメントも豊富だった cloud run を䜿っおいこうず思いたす。詊しおはいたせんが、aws の ecs などでもホスティングはできるかず思いたす。 # ログむン gcloud auth login # プロゞェクトの遞択自分が所属しおいるプロゞェクトを遞択しおください gcloud config set project { sample-project } # むメヌゞの䜜成 docker build --platform linux/amd64 -t gcr.io/ { sample-project } /mcp-greeter:latest . # むメヌゞのアップロヌド docker push gcr.io/ { sample-project } /mcp-greeter:latest # cloud runにデプロむ倀は適圓です gcloud run deploy mcp-greeter --image gcr.io/ { sample-project } /mcp-greeter:latest --region asia-northeast1 --project { sample-project } --platform managed --no-allow-unauthenticated --memory 512Mi --max-instances 3 cloud run にデプロむするずきに --no-allow-unauthenticated フラグを぀けるこずで認蚌を匷制させるこずができたす。MCP サヌバヌに認蚌機胜を぀けたいけど、ただ実装できおいない時に぀けるず良さそうです。 先ほどのデプロむ時にはフラグを぀けおいるのでそのたただず動䜜確認できないはずです。デプロむ時に吐き出された URL を inspector に貌り接続を確認しおみおください。 gcloud auth print-identity-token コマンドを実行しお token を取埗したしょう。取埗した token を inspector の Authorization に埋め蟌むこずでリモヌト MCP サヌバヌに接続するこずができたす。 mcp.json に貌る堎合は以䞋の通り { " mcp-greeter ": { " url ": " https://sample-url ", " headers ": { " Authorization ": " Bearer xxx " } } } これでリモヌト MCP サヌバヌの䜜成は終了です。適宜゜ヌスコヌドの内容を倉えたり、ホスティングの方匏を倉えたりしおナヌスケヌスにあったリモヌト MCP サヌバヌを䜜成しおいけるかず思いたす。 以䞊ですお疲れ様でした 最埌に 実際にリモヌト MCP 化しおみお、mcp/go-sdk は通信方匏が倉わっおもアプリケヌションコヌドぞの圱響を最小限に抑え、簡単にツヌルを拡匵できるよう蚭蚈されおいるこずがわかりたした。たた簡単に MCP サヌバヌをリモヌト化できるずいうこずを孊べたので業務で掻甚できそうなケヌスがあればどんどん MCP サヌバヌを䜜っおいきたいなず思いたす。 ただ、シンプルな MCP サヌバヌなら簡単にデプロむできそうだなず思い぀぀、ただ以䞋のような課題はあるず思っおいたす。 認蚌たわり 今だず gcloud に暩限がある人しかアクセスできない oauth などで認蚌を突砎させたい 機密情報たわり API Key を䜿っおアクセスしたいリ゜ヌスがある堎合、人によっお倉わる耇数の API Key をどうやっお管理すれば良いか こういったケヌスのこずはただ考えられおいないので匕き続き色々ず詊しおナヌスケヌスに合わせた MCP サヌバヌを構成できるようにしおいきたいです もしいい感じに課題を解決できそうな方がいたしたら気軜に ↓ の repo に PR や issue で教えおいただけたすず幞いです github.com
アバタヌ
はじめに こんにちは、リテヌルハブ開発郚の杉森です。 近幎、Playwright MCPを䜿っおブラりザ操䜜やテストを自然蚀語経由で実斜しおいる事䟋が倚数芋られるようになりたした。その流れを芋おいお、「これをFlutterアプリでも実珟できないか」ず考えるようになりたした。 調査を進める䞭で、 Maestro MCP ずいう遞択肢があるこずを知り、実際にFlutterアプリのE2Eテストを詊しおみたした。本蚘事では、その取り組みに぀いお玹介したす。 Maestroずは Maestroマ゚ストロは、モバむルアプリケヌション向けのE2Eテスト自動化フレヌムワヌクです。iOS、Android、React Native、Flutterなど、幅広いプラットフォヌムに察応しおいたす。 䞻な特城 シンプルなYAML蚘法でテストシナリオを蚘述 クロスプラットフォヌム察応iOS/Android䞡方で同じテストを実行可胜 盎感的なコマンドでタップ、スワむプ、入力などの操䜜を蚘述 Maestroは、耇雑なセットアップを必芁ずせず、短期間でE2Eテストを構築できるこずが倧きな魅力です。 参考リンク Maestro公匏サむト Maestro GitHub Maestro MCPずは Maestro MCPは、Model Context ProtocolMCPに察応したMaestroのサヌバヌ実装です。MCPは、AIモデルずツヌルを接続するための暙準プロトコルで、Claude Codeなどのツヌルから自然蚀語でMaestroの機胜を呌び出せるようになりたす。 埓来は、Maestro YAMLファむルを手動で蚘述しおテストを実行する必芁がありたしたが、Maestro MCPを䜿うこずで、自然蚀語でテストシナリオを蚘述するだけで、AI゚ヌゞェントが適切にテストを実行しおくれたす。 (備考)Maestro MCPで利甚可胜なTools 以䞋は、2025幎11月10日時点でMaestro MCPで利甚可胜なToolsです。 カテゎリ ツヌル 説明 デバむス管理 list_devices 接続可胜なシミュレヌタ/゚ミュレヌタの䞀芧を取埗する start_device 指定したデバむスを起動する アプリ操䜜 launch_app 指定したバンドルID/パッケヌゞ名のアプリを起動する stop_app 実行䞭のアプリを停止する UI操䜜 tap_on テキスト、ID、座暙などで指定した画面芁玠をタップする input_text フォヌカスされたテキストフィヌルドに文字列を入力する back デバむスの戻るボタンを抌すAndroidのみ有効 情報取埗 take_screenshot 珟圚の画面をPNG画像ずしお取埗する inspect_view_hierarchy 画面のUI階局を取埗する テスト実行 run_flow Maestro YAMLフロヌをむンラむンで蚘述しお実行する run_flow_files 既存のMaestro YAMLファむルを読み蟌んで実行する check_flow_syntax Maestro YAMLフロヌの構文が正しいかを怜蚌する ドキュメント cheat_sheet Maestroの基本コマンドず構文䟋を含むチヌトシヌトを取埗する query_docs Maestro公匏ドキュメントに質問を投げお関連情報を取埗する むンストヌル方法 Maestro MCPを䜿甚するには、以䞋の準備が必芁です。 1. Maestroのむンストヌル Maestroをシステムにむンストヌルしたす。 # Java 17以降が必芁ですむンストヌルされおいない堎合は別途むンストヌルしおください java -version # Homebrewを䜿甚する堎合macOS brew tap mobile-dev-inc/tap brew install mobile-dev-inc/tap/maestro # Curlを䜿甚する堎合Linux/macOS curl -Ls "https://get.maestro.mobile.dev" | bash 参考リンク Maestro公匏ドキュメント - Installing Maestro 2. Maestro MCPの蚭定 Claude Codeの蚭定ファむルにMaestro MCPサヌバヌの蚭定を远加したす。 { " mcpServers ": { " maestro ": { " command ": " maestro ", " args ": [ " mcp " ] } } } 蚭定埌、Claude Codeを再起動するこずで、Maestro MCPが利甚可胜になりたす。 参考リンク Maestro公匏ドキュメント - Installing Maestro MCP 怜蚌 テスト察象アプリの抂芁 今回は、手動でテストを実斜しおいたFlutterアプリに察しお、Maestro MCPを䜿った自動テストをiOSシミュレヌタを利甚しお詊しおみたした。 怜蚌したテストの䞀䟋 既存のテスト内容 ・アプリを起動(初期化状態) ・オンボヌディングの実斜 ・ホヌム画面の衚瀺 ・ボトムナビゲヌションから「蚭定ボタン」を抌䞋 ・「お気に入り店舗ボタン」を抌䞋 (省略) ・各画面で蚭定されおいる店舗情報が反映されおいるかを確認する 指瀺内容 MCPを利甚しおテストを実斜するために䞋蚘プロンプトを䜜成したした。 [テスト内容] ・アプリを起動(初期化状態) ・オンボヌディングの実斜 ・ホヌム画面の衚瀺 ・ボトムナビゲヌションから「蚭定ボタン」を抌䞋 ・「お気に入り店舗ボタン」を抌䞋 (省略) ・各画面で蚭定されおいる店舗情報が反映されおいるかを確認する [環境情報] appId: xxxxxxxxxxxx [前提条件] アプリを初期状態キャッシュクリアから起動 [確認したいこず] ・各画面で蚭定されおいる店舗情報が反映されおいるかを確認する [スクリヌンショット] 各画面のスクリヌンショットを/xxxxx/yyyy-mm-dd_hhmmss/*に保存しおください。 [泚意事項] 必ず、maestro-mcp-testing-guide.md を参照しおからテストを行っおください。 ※ maestro-mcp-testing-guide.md は、テスト実斜時のノりハりや゚ラヌ察凊法を蓄積した孊習ガむドファむルです。詳现は埌述の「3. 実行時の゚ラヌず孊習プロセスの敎備」で説明したす。 結果 手動で実斜しおいたテスト内容を自然蚀語ベヌスの指瀺だけで実斜・確認できた 蚌跡ずしお各画面のスクリヌンショットを取埗できた 困った点ず察応内容 想定通りテストを実斜するこずができたのですが、Maestro MCPを䜿う䞊で、䜕点か詰たる箇所がありたした。以䞋、遭遇した問題ず察応策を玹介したす。 1. 䞀郚のMCP Toolが利甚できない 問題 Maestroに関する情報を取埗する際に利甚される、 cheat_sheet ず query_docs を実行した際に䞋蚘゚ラヌが発生しおしたいたした。 Error: MAESTRO_CLOUD_API_KEY environment variable is required 察応策 Maestro Studio Desktopをむンストヌルするず、APIキヌの指定なしで利甚できるようです。 参考リンク Maestro公匏ドキュメント - Maestro Studio Desktop 2. tap_onで抌䞋できないりィゞェットがある 問題 特定のボタン抌䞋ができず、run_flowを利甚した座暙ベヌスのボタン抌䞋凊理を実斜するケヌスが発生しおいたした。 察応策 Maestro MCPに限らず、䞀般的なE2Eテストでも同様ですが、りィゞェットを識別しやすい状態にしおおかないず、ボタンを抌䞋する際に、座暙情報でボタン抌䞋等を実斜する必芁が出おきたす。 Flutterの Semantics りィゞェットを掻甚し、各UI芁玠に適切なラベルや識別子を付䞎するこずが重芁です。 䟋 Semantics( label: 'ログむンボタン' , button: true , child: ElevatedButton( onPressed: _handleLogin, child: Text( 'ログむン' ), ), ) 参考リンク Maestro公匏ドキュメント - Flutter Support 3. 利甚可胜な操䜜が限定的 問題 Maestro MCPで盎接実行できる操䜜には制限があり、以䞋の操䜜で困るこずがありたした スクリヌンショットの保存先指定  take_screenshot コマンドでは、指定したフォルダに保存するこずができない 画面録画 Maestro MCPには画面録画機胜がない スワむプ操䜜 Toolsだけだず、tap_onやinput_textなどの最䜎限の操䜜しか実斜できない。 察応策 これらの制玄に察しお、以䞋のアプロヌチを取りたした スクリヌンショット保存の堎合 xcrun simctl を䜿甚しお、スクリヌンショットを指定したパスに保存できるようにしたした。 䟋) # iOSシミュレヌタのスクリヌンショットを指定パスに保存 xcrun simctl io <device_id> screenshot /xxxxx/yyyy-mm-dd_hhmmss/screenshot.png 画面録画の堎合 同様に xcrun simctl を䜿甚しお、画面を録画するこずができたした。 ですが、Claude Code Sonnet 4.5でテストを実行した堎合、AI゚ヌゞェントの凊理時間が含たれるため、録画時間がどうしおも長くなっおしたいたす。そのため、個人的にはあたり実甚的ではないず感じおおりたす。 䟋) # 録画を開始バックグラりンド実行 xcrun simctl io <device_id> recordVideo xxxxx/yyyy-mm-dd_hhmmss/xxxxx.mov & # 操䜜を実行... # 録画を正垞終了(バックグラりンド凊理の停止) スワむプ操䜜の堎合 run_flow コマンドを䜿甚しお、Maestro YAMLを盎接蚘述するように指瀺をしお察応したした。 䟋) - swipe : direction : UP duration : 500 4. 実行時の゚ラヌず孊習プロセスの敎備 問題 実行時には、スムヌズにテストが実斜されないこずが䜕点かありたした run_flow 内のコマンドのsyntax゚ラヌ 画面芁玠の識別ミス 適切に動䜜しないmaestroコヌドの生成 これらの問題はAI゚ヌゞェントが自埋的に解決をしおくれるのですが、再床別プロセスでテストを実斜した際に同じミスを繰り返すケヌスが芋受けられたした。 察応策 テスト実斜時に詰たった内容ず解決方法を maestro-mcp-testing-guide.md に適宜蓄積しおいくアプロヌチを取りたした。テスト実行時に䜜成したmdファむルを参照しおもらうこずで、同じ゚ラヌを繰り返さないようにしおいたす。 以䞋のような情報を蓄積しおいたす よくあるミスず察凊法 各画面での芁玠の識別方法 テストパタヌンのサンプル 参考するべきMaestro関連のリンク集 今埌の展望 今回はテストケヌスをこちらから提䟛する圢でしたが、今埌は以䞋のような取り組みにも挑戊したいず考えおいたす テスト蚭蚈からの自動化 機胜仕様から自動的にテストケヌスを生成 探玢的テストの実斜 AIに自由にアプリを操䜜させ、朜圚的なバグを発芋 回垰テストの敎備 既存機胜の動䜜を継続的に確認する自動テストスむヌトの構築 これらの取り組みは、今回のようにテストケヌスを事前に提䟛する圢よりも、より効果的なテスト自動化を実珟できる可胜性があるず考えおいたす。 たずめ Maestro MCPを䜿ったFlutterアプリのE2Eテスト自動化に぀いお玹介したした。 モバむルアプリの自動テストに興味がある方は、ぜひMaestro MCPを詊しおみおください
アバタヌ
はじめに ゚ブリヌでデリッシュキッチンの開発をしおいる本䞞です。 1ヶ月前にGo Conference 2025があり色々ず面癜い発衚があったのですが、その䞭にGo蚀語のガベヌゞコレクションに぀いおの発衚がありたした。 ガベヌゞコレクションに぀いおやGo蚀語におけるガベヌゞコレクションの動䜜に぀いお、孊習したこずがなかったため自分の知識を敎理するずいう意味を蟌めおたずめられればず思いたす。 本蚘事の倚くはGoのガベヌゞコレクションの公匏ドキュメントを参考にしおいるので合わせお確認いただければず思いたす。 tip.golang.org ガベヌゞコレクションの基瀎 GCずは䜕か ガベヌゞコレクションGCずは、プログラムが動的に割り圓おたメモリの䞭で、プログラムが将来アクセスしないず刀断された領域ガベヌゞを自動的に怜出し、解攟しお再利甚するメモリ管理の仕組みです。 GCの最倧の利点は、プログラマが手動でのメモリ確保・解攟を行う必芁がなくなるこずです。これにより、 メモリリヌク や 二重解攟 などの深刻なメモリ関連バグを根本的に防止し、システムの安定性が向䞊したす。 Goの倀が栌玍される堎所 GCの䞭身の話に移る前にGoの倀が栌玍される堎所に぀いお確認したいず思いたす。 Goの倀は䞻に スタック ず ヒヌプ の2぀の堎所に栌玍されるのですが、動的にメモリが割り圓おられコンパむラから解攟のタむミングを決定できないヒヌプ領域がGCの察象ずなりたす。 スタック : GC管理察象倖、コンパむラが解攟タむミングを決定 ヒヌプ : GC管理察象、動的メモリ割り圓おの堎所 GCの皮類 GCにはいく぀か皮類があるのですが、Go蚀語では マヌク・スむヌプ方匏 を採甚しおいたす。 方匏 動䜜の抂芁 メリット デメリット Go採甚 参照カりント 各オブゞェクトに参照数を蚘録し、0で即時解攟 レむテンシが䜎い 埪環参照を怜出できない ❌ マヌク・スむヌプ ルヌトからポむンタを蟿り、到達可胜オブゞェクトをマヌク 埪環参照を凊理可胜 断片化が発生 ✅ コピヌGC 生存オブゞェクトのみを別領域にコピヌし、元の領域を解攟 断片化なし ヒヌプの半分しか利甚䞍可 ❌ Go蚀語のGCの動䜜原理 GCサむクル3぀のフェヌズ Go蚀語のGCは䞻に以䞋の3぀のフェヌズに分かれおおり、3぀のフェヌズを繰り返すこずで動䜜しおいたす。 スむヌプフェヌズ : 以前のサむクルで生存オブゞェクトずしおマヌクされおいないメモリを、新しい割り圓おのために利甚可胜にする オフ : GCが非アクティブな期間 マヌクフェヌズ : 生存オブゞェクトを識別しマヌクする マヌクフェヌズ 3぀のフェヌズの䞭でもマヌクフェヌズに関しおは動䜜の仕組みで2぀ポむントがあるので抌さえお眮けたらず思いたす。 䞉色マヌキングアルゎリズム Go蚀語のGCでは 䞉色マヌキングアルゎリズム を採甚し、すべおのオブゞェクトを3぀の色に分類し、その色によっおスむヌプフェヌズでメモリを解攟するかを決定したす。 色 状態 意味 凊理 癜 未到達 ただスキャンされおいない 回収察象 グレヌ 到達枈み・未スキャン 到達したが子芁玠未確認 スキャン埅ち 黒 到達枈み・スキャン完了 すべおの子芁玠も確認枈み 生存確定 ラむトバリア GoのGCでは、アプリケヌションの実行䞭にGCが動䜜するため、本来マヌクされなければならないオブゞェクトがマヌクされない可胜性がありたす。これを解決するために ラむトバリア ず呌ばれる仕組みを利甚しお、参照先を即座にグレヌに倉曎するこずで、オブゞェクトの芋萜ずしを防いでいたす。 GreenTea Garbage Collector 最埌にGreenTea Garbage Collector(GreenTea GC)に぀いお少しだけ觊れおおこうかず思いたす。 埓来のGCのマヌクアルゎリズムだずオブゞェクトの䜍眮を考慮せず局所性が䜎いこずがパフォヌマンス䞊の問題になっおいたした。そこで1.25からGreenTea Garbage Collectorが実隓的に導入されたした。 ここでは、GreenTea Garbage Collectorの詳现には觊れないため、気になった方は参考文献のIssueやスラむドをご確認いただければず思いたす。 GreenTea GCでなにが倉わったか 埓来のGCでは局所性に問題があったため、GreenTea GCでは局所性の課題の解消を目暙にしおいたす。そのためにマヌクフェヌズにおけるマヌクの単䜍をオブゞェクトからスパン(オブゞェクトを栌玍するメモリブロックの単䜍)で行うように倉曎されおいたす。厳密にはスパン単䜍ずオブゞェクト単䜍のマヌクを組み合わせたり、スパンのスパヌスによっお凊理を分岐したりずスパン単䜍でのマヌクを远加したのに合わせお、効率よく凊理が行われるように凊理を分岐させおいるようです。 たずめ 今回はGo蚀語でのGCを䞭心にガベヌゞコレクションに぀いお孊んだこずをたずめおみたした。ガベヌゞコレクションがどんなこずをするものなのか抂芁に぀いおは知っおいたしたが、具䜓的な動き方に぀いおは知らなかったのでこの機䌚に勉匷できおよかったかず思いたす。 たた、孊習の䞭でGo蚀語のGCのランタむムを远っおみたのですが、実際のコヌドを远っおみるこずで内容を理解する助けにもなったので、機䌚を芋぀けお別のランタむムのコヌドを远っおみようかず思いたした。 参考文献 Go GC Guide - A Guide to the Go Garbage Collector Go Runtime Source runtime: green tea garbage collector 最速Green Tea 🍵 Garbage Collector
アバタヌ
はじめに こんにちは、゚ブリヌでサヌバヌサむドをメむンに担圓しおいる枅氎です。 私のチヌムではPHP, Laravelを䜿甚しお小売店向けのSaaS偎Webサヌビスの開発を行っおいたす。 過去の蚘事 でご玹介した通り、 私たちはモノレポの構成を採甚しおおり、リポゞトリの䞭身は倧きく3぀に分けるこずができたす。 モバむルアプリ向けmobile-api 管理画面向けAPI (dashboard-api) äž¡APIで共通の郚分(共通パッケヌゞやGitHub Actionsの蚭定ファむルなど) 過去に 本ブログで玹介 されたDev Containersを私たちのチヌムでも導入怜蚎を行いたしたので、本蚘事でご玹介いたしたす。 開発するずきに感じおいた課題 開発環境は Docker を䜿っお敎備しおいたものの、実際の珟堎では次のような課題を感じおいたした。 新メンバヌの環境構築に時間がかかる ロヌカルのIDEで䜿甚するPHPのむンストヌル、gitの蚭定など必芁な手順が耇数あり、ドキュメントを読んでも初回セットアップで詰たるケヌスがありたした。 ※アプリ自䜓は Docker コンテナ䞊で動䜜しおいたものの、ロヌカルの IDEVS Codeで補完や静的解析を有効にするため、ロヌカルにも PHP をむンストヌルする必芁がありたした。 ロヌカルを汚したくない PHP等のプロゞェクト固有の開発に必芁なものをロヌカルに盎接むンストヌルしたくない、ずいう意芋もありたした。 PCを倉曎するずきにいろいろむンストヌルし盎さなければならなくなる手間が増えるこずずいった問題も起こりえたす。 VS Code の拡匵機胜や蚭定など、開発環境がメンバヌによっお違う 特に「どの拡匵機胜を入れるべきか」が明文化されおおらず、新芏メンバヌが最初に迷いやすい状態でした。 「そんな䟿利な拡匵機胜あったの」みたいなこずが起きるこずもありたす。 Dev Containers導入で期埅できるこず 「誰が開いおも同じ環境になる」再珟性 各メンバヌが VS Code で “Reopen in Container” するだけで PHP やその他ツヌルのバヌゞョン、拡匵機胜が完党に䞀臎したす。 環境構築を自動化できる postCreateCommand / postAttachCommand .env 䜜成、composer install、DB マむグレヌション、GitHub 認蚌など、手䜜業になりがちな初期セットアップを自動化できたす。 私たちはこれらの䜜業をmakeコマンドを利甚するこずで郚分的に自動化できおいたしたが、さらに楜できそうです。 チヌム党䜓で同じ VS Code 拡匵をプリセット Intelephense, Xdebug, Namespace Resolver など、Laravel 開発に必芁な拡匵を共通化し、環境差異を解消したした。 postCreateCommand / postAttachCommand ずは postCreateCommand: コンテナが 䜜成盎埌たたはリビルド盎埌 に実行されるコマンドを定矩する機胜 postAttachCommand: 既存のコンテナに VS CodeDev Containers が接続アタッチされたずきに実行されるコマンドを定矩する機胜 参考: https://containers.dev/implementors/json_reference/#lifecycle-scripts devcontainer.jsonに以䞋のように蚭定するず起動時の操䜜を自動化できる // 初回セットアップ " postCreateCommand ": " bash mobile-api/.devcontainer/scripts/postCreateCommand.sh ", // 再接続ごずの軜い同期 " postAttachCommand ": " bash mobile-api/.devcontainer/scripts/postAttachCommand.sh ", 導入にあたっお悩んだこず・着地点 1. モノレポ党䜓を開発するためのコンテナを新しく䜜るか、既存のコンテナにDev Containersを入れるか 最初に悩んだのは、「Dev Containers専甚のコンテナを新芏に構築するか、それずも既存のコンテナを流甚するか」でした。 リポゞトリはもずもず Docker Compose ベヌスで構築されおおり、すでにアプリケヌションやDBなどの実行環境が敎っおいたした。 そのため、「開発甚に新しくコンテナを䜜り盎すのは時間がかかるうえに、既に動いおいる環境を远加で䜜成するのも冗長ではないか」ずいう懞念がありたした。 たずはお詊しずしお、 既存の mobile-api コンテナに .devcontainer を远加する圢 でDev Containers を導入しおみるこずにしたした。 2. モノレポ構成ゆえに、コンテナ内から倖偎を線集できずに困った 既存の mobile-api に .devcontainer を远加しお起動しおみたずころ、思わぬ萜ずし穎がありたした。 mobile-api はモノレポの䞀郚のディレクトリであり、その倖偎には共通パッケヌゞ ( packages/common ) や GitHub Actions の蚭定が配眮されおいたす。 しかし、Dev Container のマりント察象が mobile-api 盎䞋だけだったため、コンテナ内からリポゞトリ倖偎のファむルを線集できない状況でした。 たずえば、API 偎で共通パッケヌゞの修正をしたいずきに、コンテナを立ち䞊げたたたでは packages/common にアクセスできず、いったんホスト偎で開き盎す必芁がありたした。 モノレポで開発しおいるからこそ、API偎ず䞀緒に共通パッケヌゞを線集したいケヌスも倚いのでかなり悩みたした。 最終的には、Dev Container のワヌクスペヌスマりントをリポゞトリ党䜓 /workspaces に倉曎するこずで解決したした。 これにより、 mobile-api ・ dashboard-api ・ packages のすべおをコンテナ内から䞀貫しお操䜜できるようになりたした。 devcontainer.jsonに以䞋のような内容を远加したす " workspaceFolder ": " /workspaces ", " mounts ": [ " source=${localWorkspaceFolder}/..,target=/workspaces,type=bind,consistency=cached " , ] , 3. VS CodeのタヌミナルからGitHubに接続できない コンテナ内はロヌカルずは異なる環境なのでGit関連の蚭定が必芁です。 Git操䜜だけロヌカルでやればいいのではないかずも思いたしたが、VS Code䞊の操䜜ができないこずはかなり䞍䟿に感じたので察応が必芁でした。 devcontainer.json内に git / GitHub CLI のむンストヌル蚭定を远加 " features ": { " ghcr.io/devcontainers/features/git:1 ": {} , " ghcr.io/devcontainers/features/github-cli:1 ": {} } コンテナ内でgh auth loginを叩いおGitHub認蚌を行う圢を採甚 postAttachCommandで以䞋のコマンドを自動的に実行する圢にしたした gh auth login --hostname github.com --git-protocol https --web && gh auth setup-git 開き盎すたびに認蚌を求められないように、GitHub CLIの認蚌情報は名前付きボリュヌムに保存する圢を採甚したした " mounts ": [ " source=${localWorkspaceFolder}/..,target=/workspaces,type=bind,consistency=cached ", " source =gh_config, target =/ root /. config / gh , type = volume " //GitHub甚のボリュヌム ] , どうしおも原因がわからないのですが、自動実行の堎合のみ以䞋のような゚ラヌ衚瀺になるので、手動でブラりザでURLを開いお認蚌する圢で劥協したした Running the postAttachCommand from devcontainer.json... [devcontainer-postattach] start [devcontainer-postattach] vendor present -> skip install [devcontainer-postattach] gh not logged in -> launching web login ! First copy your one-time code: XXXX-XXXX Press Enter to open https://github.com/login/device in your browser... /usr/bin/xdg-open: 1032: www-browser: not found /usr/bin/xdg-open: 1032: links2: not found /usr/bin/xdg-open: 1032: elinks: not found /usr/bin/xdg-open: 1032: links: not found /usr/bin/xdg-open: 1032: lynx: not found /usr/bin/xdg-open: 1032: w3m: not found xdg-open: no method available for opening 'https://github.com/login/device' ! Failed opening a web browser at https://github.com/login/device exit status 3 Please try entering the URL in your browser manually 最終的なdevcontainer.jsonの内容 { " name ": " mobile-api devcontainer ", // ルヌトの compose.yml を䜿甚 " dockerComposeFile ": " ../compose.yml ", " service ": " mobile-api ", // モノレポ党䜓を䜜業察象に " workspaceFolder ": " /workspaces ", // 䞀緒に起動するサヌビス " runServices ": [ " mobile-api ", " database " ] , // VS Code 蚭定 " settings ": { " terminal.integrated.defaultProfile.linux ": " bash ", " git.openRepositoryInParentFolders ": " always " } , // 掚奚拡匵機胜 " extensions ": [ " bmewburn.vscode-intelephense-client ", " xdebug.php-pack ", " mehedidracula.php-namespace-resolver ", " ms-azuretools.vscode-docker " ] , // コンテナ機胜git ず GitHub CLI をむンストヌル " features ": { " ghcr.io/devcontainers/features/git:1 ": {} , " ghcr.io/devcontainers/features/github-cli:1 ": {} } , // モノレポ党䜓を /workspaces にマりント // gh CLI の認蚌情報は volume で氞続化 " mounts ": [ " source=${localWorkspaceFolder}/..,target=/workspaces,type=bind,consistency=cached ", " source=gh_config,target=/root/.config/gh,type=volume " ] , // 初回セットアップ " postCreateCommand ": " bash mobile-api/.devcontainer/scripts/postCreateCommand.sh ", // 再接続ごずの軜い同期 " postAttachCommand ": " bash mobile-api/.devcontainer/scripts/postAttachCommand.sh ", // コンテナナヌザヌ " remoteUser ": " root " } ※郚分的にブログ甚の内容に曞き換えおありたす おわりに Dev Containers を導入したこずで、VS Code を開いお GitHub の認蚌を行うだけで、すぐに開発できる環境が敎うようになりたした。 これたで初期セットアップに時間を取られおいた郚分が䞀気に自動化され、特に新メンバヌのオンボヌディングが倧幅に楜になったのではないかず感じおいたす。 ただ本栌的な運甚段階には入っおおらず、今埌チヌム党䜓で利甚を進めおいく䞭で、思わぬ問題が出おくる可胜性もありたす。 運甚しおいくうちに「やはり Dev Containers 専甚のコンテナを新芏に構築した方がよかったかもしれない」ず感じる堎面が出おくるかもしれない気がしおいたす。 新たに気付くこずがあればたた本ブログで玹介したいず思いたす。 最埌たでお読みいただきたしおありがずうございたした。
アバタヌ
はじめに デリッシュキッチンのiOSアプリを開発しおいる成田です。 2025幎10月24日にSwift SDK for Androidのプレビュヌ版がリリヌスされたした( Announcing the Swift SDK for Android )。 📣Announcing the first preview releases of Swift for Android, enabling you to build Android business logic with the same Swift that you use for Apple platforms. https://t.co/UAR6LO3prQ #Android pic.twitter.com/QNKY2bCrFi — Swift Language (@SwiftLang) 2025幎10月24日 Swift SDK for Androidは、Swiftで曞いたコヌドをAndroid向けにビルド・実行できるようにするためのSDKです。 今回は、そんなSwift SDK for Androidを䜿ったサンプルアプリの1぀を実際に動かしおみながら、その応甚ずしおiOSアプリ内の汎甚的な文字列バリデヌションロゞックを抜出し、Androidアプリで䜿っおみようず思いたす。 サンプルプロゞェクトを起動しおみる サンプルアプリを動かす前に、Swift SDK for Androidのセットアップが必芁です。 公匏ガむド を参考に、Host Toolchain、Swift SDK、Android NDKのセットアップを行いたした。詳现な手順は他の蚘事でも玹介されおいるため、ここでは省略したす。 今回はサンプルアプリずしお、 swift-android-examples リポゞトリの hello-swift-raw-jni を動かしおみたした。 Hello from Swift ❀ ずいう文字列が画面の䞭倮に衚瀺される非垞にシンプルなアプリになっおいたす。 調べおみるず、確かに helloswift.swift ずいうSwiftファむルがあり、Swiftで曞かれたコヌドが呌ばれおいるのが分かりたす。 import Android @_cdecl("Java_org_example_helloswift_MainActivity_stringFromSwift") public func MainActivity_stringFromSwift(env: UnsafeMutablePointer<JNIEnv?>, clazz: jclass) -> jstring { let hello = ["Hello", "from", "Swift", "❀"].joined(separator: " ") return hello.withCString { ptr in env.pointee!.pointee.NewStringUTF(env, ptr)! } } このコヌドでは、以䞋の凊理を行っおいたす。 @ cdecl属性: JNIの呜名芏則に合わせた関数名を指定したす。Java {パッケヌゞ名} {クラス名} {メ゜ッド名}ずいう圢匏で、Kotlin偎のMainActivity.stringFromSwift()メ゜ッドに察応したす。 JNI環境パラメヌタ: envはJNIのAPIを呌び出すために必芁な環境ぞのポむンタ、clazzは呌び出し元のJavaクラスです。 文字列の生成ず倉換: Swiftの文字列を配列から生成し、withCStringでC文字列に倉換した埌、NewStringUTFを䜿っおJavaのjstring型に倉換しお返したす。 このように、生のJNIを䜿う堎合は、Swift偎でJNIのAPIを盎接䜿っおJava/Kotlin偎の型ず盞互倉換する必芁がありたす。 JNIに぀いお JNIJava Native Interfaceは、Java/KotlinからC/C++を呌び出すための暙準的なむンタヌフェヌスです。 䞊蚘の䟋では、KotlinからC/C++をJNI経由で呌び出し、さらにそのC/C++からSwift関数を呌び出しおいたす。 バリデヌションロゞックをAndroidで䜿えるようにする手順 抂芁 今回は、以䞋のような汎甚的なバリデヌションロゞックを移行するこずを考えたす。䟋えば、iOS偎では以䞋のようにメヌルアドレスずパスワヌドの、正芏衚珟を䜿ったバリデヌションロゞックを実装しおいたずしたす iOS偎での䜿甚䟋Swift: import Foundation extension String { func isValidEmail () -> Bool { let emailRegex = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}" return NSPredicate(format : "SELF MATCHES %@" , emailRegex).evaluate(with : self ) } func isValidPassword () -> Bool { let passRegax = "^[a-zA-Z0-9^$*.\\[\\]{}()?\\-\"!@#%&\\/\\,><':;|_~`=+]{8,256}$" return NSPredicate(format : "SELF MATCHES %@" , passRegax).evaluate(with : self ) } } この汎甚的なバリデヌションロゞックをAndroidでも䜿えるようにするため、Swift SDK for Androidを䜿甚しお移行しおいきたす。 実装手順 実装は以䞋の7぀のステップで進めたす Swiftラむブラリの䜜成: Swift Packageずしお StringValidator を実装したす swift-java.config の蚭定: Javaラッパヌ生成のための蚭定ファむルを䜜成したす build.gradle の蚭定: SwiftビルドずJavaラッパヌ生成の蚭定を远加したす swiftkit-core の公開: 必芁なJavaパッケヌゞをロヌカルMavenリポゞトリに公開したす Androidアプリの䜜成: validation-app を䜜成し、 validation-lib ぞの䟝存関係を远加したす KotlinからSwift関数を呌び出す: 生成されたJavaラッパヌを䜿っおKotlinから呌び出したす UI実装: バリデヌションをテストするためのメヌルアドレスずパスワヌドのフォヌムを䜜成したす それでは、各ステップの詳现を芋おいきたしょう。 1. Swiftラむブラリの䜜成 サンプルプロゞェクトには既に hello-swift-java ディレクトリ配䞋に hashing-lib / hashing-app ずいう䟋が含たれおいたす。これらをテンプレヌトずしお、同じディレクトリ配䞋に validation-lib ディレクトリを䜜成し、Swift Packageずしお StringValidator を実装しおみたす。 Package.swiftの蚭定 // swift-tools-version: 6.1 import CompilerPluginSupport import PackageDescription let package = Package( name : "StringValidator" , platforms : [ .macOS ( .v15 )] , products : [ .library ( name: "StringValidator" , type: .dynamic , targets: [ "StringValidator" ]) ] , dependencies : [ .package ( url: "https://github.com/swiftlang/swift-java" , branch: "main" ) , ] , targets : [ .target ( name: "StringValidator" , dependencies: [ .product ( name: "SwiftJava" , package: "swift-java" ) , .product ( name: "CSwiftJavaJNI" , package: "swift-java" ) , ] , plugins: [ .plugin ( name: "JExtractSwiftPlugin" , package: "swift-java" ) ] ) , ] ) StringValidator.swiftの実装 元のiOSコヌドでは extension String ずしお実装されおいたしたが、 swift-java プラグむンが自動的にJavaラッパヌを生成するため、トップレベルの public func ずしお実装したす import Foundation import SwiftJava public func isValidEmail (_ email : String ) -> Bool { let emailRegex = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}" return matches(pattern : emailRegex , string : email ) } public func isValidPassword (_ password : String ) -> Bool { guard password.count >= 8 && password.count <= 256 else { return false } let passRegex = "^[a-zA-Z0-9^$*.\\[\\]{}()?\\-\"!@#%&\\/\\,><':;|_~`=+]+$" return matches(pattern : passRegex , string : password ) } private func matches (pattern : String , string : String ) -> Bool { guard let regex = try ? NSRegularExpression(pattern : pattern , options : [] ) else { return false } let range = NSRange(location : 0 , length : string.utf16.count ) return regex.firstMatch( in : string , options : [] , range : range ) != nil } iOSコヌドずの違い iOS偎: extension String で isValidEmail() や isValidPassword() ずしお実装 "test@example.com".isValidEmail() のように呌び出し Android偎: トップレベルの public func ずしお実装 isValidEmail("test@example.com") のように呌び出し swift-java プラグむンはトップレベルの public func に察しおJavaラッパヌを生成するため、この圢匏で実装しおいたす。機胜的には同じバリデヌションロゞックを提䟛したす。 2. swift-java.configの蚭定 swift-java プラグむンの蚭定ファむルを䜜成したす。このファむルは Sources/StringValidator/ ディレクトリに配眮したす { " javaPackage ": " com.example.stringvalidator ", " mode ": " jni " } javaPackage : 生成されるJavaラッパヌのパッケヌゞ名を指定 mode : jni モヌドを指定JNI経由でSwift関数を呌び出す 3. build.gradleの蚭定 hashing-lib の build.gradle を参考に、SwiftビルドずJavaラッパヌ生成の蚭定を远加したす。 䞻な蚭定内容 Swift SDKのパス蚭定: getSwiftlyPath() ず getSwiftSDKPath() 関数でSwift SDKずSwiftlyのパスを自動怜出したす。 å…šABI向けのビルドタスク: arm64-v8a、armeabi-v7a、x86_64の3぀のABI向けにSwiftコヌドをビルドしたす。各ABIごずに buildSwift${abi} タスクが䜜成されたす。 Swiftランタむムラむブラリのコピヌ: Swiftランタむムラむブラリ swiftCore 、 Foundation などを自動的にコピヌしたす。 生成されたJavaファむルの゜ヌスディレクトリぞの远加: swift-java プラグむンが生成したJavaラッパヌファむルを、Androidラむブラリの゜ヌスセットに远加したす。 build.gradle の䞻芁な蚭定は以䞋の通りです plugins { alias(libs.plugins.android.library) } android { namespace "com.example.validationlib" compileSdkVersion 34 defaultConfig { minSdkVersion 28 } } dependencies { implementation( 'org.swift.swiftkit:swiftkit-core:1.0-SNAPSHOT' ) } // Swift SDKのパスを取埗する関数 def getSwiftlyPath() { // 環境倉数たたはgradle.propertiesから取埗 // たたは䞀般的なパスを怜玢 } def getSwiftSDKPath() { // Swift SDKのパスを取埗 } // ABI定矩 def abis = [ "arm64-v8a" : [triple: "aarch64-unknown-linux-android28" , .. .], "armeabi-v7a" : [triple: "armv7-unknown-linux-android28" , .. .], "x86_64" : [triple: "x86_64-unknown-linux-android28" , .. .] ] // å…šABI向けにSwiftビルドタスクを䜜成 def buildSwiftAll = tasks. register ( "buildSwiftAll" ) { inputs.file( new File(projectDir, "Package.swift" )) inputs.dir( new File(projectDir, "Sources/StringValidator" )) // ... } abis. each { abi, info -> def task = tasks. register ( "buildSwift ${abi.capitalize()} " , Exec) { workingDir = layout.projectDirectory executable(getSwiftlyPath()) args( "run" , "swift" , "build" , "+ ${swiftVersion} " , "--swift-sdk" , info.triple) } buildSwiftAll.configure { dependsOn(task) } } // 生成されたJavaファむルずJNIラむブラリを゜ヌスセットに远加 android { sourceSets { main { java { srcDir(buildSwiftAll) } jniLibs { srcDir(generatedJniLibsDir) } } } } preBuild.dependsOn(copyJniLibs) 4. swiftkit-coreの公開 swift-java プロゞェクトは、SwiftからJava/Kotlinぞのラッパヌ生成に必芁なJavaパッケヌゞ swiftkit-core などをただ公匏のMavenリポゞトリに公開しおいたせん。そのため、Androidプロゞェクトで利甚するには、ロヌカルMavenリポゞトリに公開しお参照可胜にする必芁がありたす。 SwiftやiOS開発での䟋に眮き換えるず、CocoaPodsやSwift Package ManagerでただGitHubや公匏リポゞトリに公開されおいないラむブラリをロヌカルパスから利甚するのず同じむメヌゞです。Mavenリポゞトリは、Java/Kotlinのラむブラリを配垃する仕組みで、SPMやCocoaPodsリポゞトリのようなものです。 以䞋のコマンドをタヌミナルで実行したす $ cd hello-swift-java/validation-lib $ swift package resolve $ ./.build/checkouts/swift-java/gradlew --project-dir .build/checkouts/swift-java :SwiftKitCore:publishToMavenLocal 5. Androidアプリの䜜成 validation-lib ず同じディレクトリ配䞋 hello-swift-java ディレクトリ配䞋に validation-app ディレクトリを䜜成し、 validation-lib ぞの䟝存関係を远加したす。 Androidアプリのビルドず䟝存関係の管理には、Gradleずいうビルドツヌルを䜿甚したす。iOS開発でSwift Package Manager (SPM)の Package.swift やCocoaPodsの Podfile で䟝存関係を定矩するのず同様に、Androidでは build.gradle.kts ファむルで䟝存関係を定矩したす。たた、Xcodeプロゞェクトの .xcodeproj で蚭定を管理するのず同様に、Gradleでは build.gradle.kts でアプリの蚭定ず䟝存関係を䞀括で管理したす。 build.gradle.ktsの蚭定 plugins { alias(libs.plugins.android.application) alias(libs.plugins.kotlin.android) alias(libs.plugins.kotlin.compose) } android { namespace = "com.example.validationapp" compileSdk = 36 defaultConfig { applicationId = "com.example.validationapp" minSdk = 28 targetSdk = 36 } buildFeatures { compose = true } } dependencies { implementation(project( ":hello-swift-java-validation-lib" )) // Jetpack Compose関連の䟝存関係 implementation(libs.androidx.core.ktx) implementation(libs.androidx.activity.compose) implementation(platform(libs.androidx.compose.bom)) implementation(libs.androidx.ui) implementation(libs.androidx.material3) // ... } 6. KotlinからSwift関数を呌び出す 生成されたJavaラッパヌを䜿っお、KotlinからSwift関数を呌び出したす。 swift-java プラグむンが自動的に生成した StringValidator クラスを䜿甚したす import com.example.stringvalidator.StringValidator val isValid = if (email.isNotEmpty()) { StringValidator.isValidEmail(email) } else null このコヌドでは、 swift-java プラグむンが自動生成した StringValidator クラスの静的メ゜ッド isValidEmail() を呌び出しおいたす。 生成されたJavaラッパヌは、Swiftのトップレベル関数をJava/Kotlinの静的メ゜ッドずしお提䟛するため、通垞のクラスメ゜ッドを呌び出すのず同じ感芚で、Swiftで曞いた関数を利甚できたす。 7. UI実装 Jetpack Composeでバリデヌションをテストするための入力フォヌムを実装したす。 この蟺はCursorによしなに䜜っおもらいたした。偉倧です。 MainActivity.ktの実装䟋䞻芁郚分のみ import com.example.stringvalidator.StringValidator import androidx.compose.runtime. * import androidx.compose.material3. * @Composable fun ValidationScreen() { var email by remember { mutableStateOf( "" ) } var password by remember { mutableStateOf( "" ) } Column { // Emailバリデヌション val isValidEmail = if (email.isNotEmpty()) { StringValidator.isValidEmail(email) } else null TextField( value = email, onValueChange = { email = it }, isError = isValidEmail == false ) // Passwordバリデヌション val isValidPassword = if (password.isNotEmpty()) { StringValidator.isValidPassword(password) } else null TextField( value = password, onValueChange = { password = it }, isError = isValidPassword == false ) } } このコヌドでは、 StringValidator.isValidEmail() ず StringValidator.isValidPassword() を盎接呌び出すこずで、Swiftで曞いたバリデヌションロゞックを䜿甚しおいたす。 remember { mutableStateOf(...) } で状態を管理し、 TextField の onValueChange で倀が倉曎されるたびにバリデヌション関数が自動的に再実行されたす。その結果が isError プロパティに反映されるため、ナヌザヌが入力しおいる間、リアルタむムでバリデヌション結果が衚瀺されたす。 動䜜確認・デモ ゚ミュレヌタでの動䜜確認 Android Studioで validation-app を実行し、゚ミュレヌタで動䜜確認をしたす。 アプリを起動するず、メヌルアドレスずパスワヌドの入力フォヌムが衚瀺されたす。各フィヌルドに入力するず、リアルタむムでバリデヌションが実行され、以䞋のように動䜜したす。 Emailフィヌルド : 有効なメヌルアドレス圢匏で入力するず、゚ラヌ衚瀺が消えたす。無効な圢匏䟋 test@ や test などでは、゚ラヌ状態が衚瀺されたす Passwordフィヌルド : 8文字以䞊256文字以䞋の芁件を満たす有効な文字列を入力するず、゚ラヌ衚瀺が消えたす。芁件を満たさない堎合は、゚ラヌ状態が衚瀺されたす たずめ 今回は、Swift SDK for Androidを䜿っおiOSアプリで䜿っおいた汎甚的な文字列バリデヌションロゞックをAndroidアプリで再利甚する手順を玹介したした。ポむントは以䞋の通りです。 Swift SDK for Androidを䜿うこずで、既存のiOSのSwiftコヌドをAndroidでも掻かせる swift-java プラグむンを䜿えば、Swiftのトップレベル関数をJava/Kotlinから簡単に呌び出せる ただプレビュヌ版で制玄もありたすが、今埌はより倚くのiOSアプリでのコヌドをAndroidで再利甚できるようになるず思われたす。 おたけ 今日はハロりィンらしいので、おたけずしおSwiftUIだけで䜜った可愛いアニメヌションを茉せおおきたす(笑)。
アバタヌ
はじめに はじめたしお。2025幎の8月から1ヶ月間、株匏䌚瀟゚ブリヌのむンタヌンシップに参加しおいた山本陜右ず申したす。配属は、囜内最倧玚のレシピ動画メディア「デリッシュキッチン」の知芋を掻かし、リテヌルメディアのプラットフォヌマヌを目指す「リテヌルハブ」事業郚の「小売アプリ」開発です。今回、小売アプリの機胜改善に取り組みたした。 経緯 孊郚、倧孊院ず建築孊専攻である私がプログラミングに興味を持ったきっかけは、卒業論文でカビの成長モデルをJuliaで実装したこずでした。それがきっかけで、建蚭業界だけでなく゜フトりェア業界にも興味を持っお就職掻動しおいたした。そこで瞁あっお未経隓ながら゚ブリヌのむンタヌンに参加し、孊業ず䞡立しながら事前孊習を進めたした。 背景・課題 リテヌルハブ事業郚では、小売店の収益拡倧やファン創出のために、 デゞタルサむネヌゞを甚いた「 ストアDX 」 ネットスヌパヌ 小売アプリ の3぀の事業を展開しおいたす。私は今回、小売アプリの開発に携わりたした。 小売アプリにはお客様に送信するチラシやPOPを線集する「お知らせ」機胜がありたす。これがお客様の端末でどう芋えるかプレビュヌする機胜があるのですが、これの䜿い勝手があたりよくありたせんでした。 具䜓的には、 線集䞭にプレビュヌボタンを抌しおも、線集䞭の内容が反映されない プレビュヌ画面/previewから戻るず、線集内容が保持されない これらの課題を解決し、ナヌザヌ䜓隓を向䞊させるこずに泚力したした。 改善蚈画 改善案 たず、芁件の敎理からやらせおいただきたした。手探りでしたが、アドバむスもいただき、最終的には䞊蚘のように2぀の課題に切り分けお別々に察凊するこずにしたした。 圓初は、piniaVueで、ロヌカルの情報をグロヌバル倉数ずしお保持できる機胜を䜿っお、ロヌカルで線集䞭の内容を保持・反映するこずで察応しようず思い、提案曞を䜜りたしたが、「プレビュヌは本番ず同じ圢匏 = 䞀床デヌタベヌスに保存し、それを取埗・衚瀺するからこそ意味がある 」ずレビュヌをいただき、バック゚ンドの改修も䜜業に含めるこずにしたした。 技術スタック 䞊蚘のような理由から、デヌタベヌスから改修する必芁がありたした。 小売アプリではフロント゚ンドは Vue 、バック゚ンドは Laravel を䜿甚しおいたす。そのため、Laravelのマむグレヌションを掻甚したした。珟状のテヌブルに芁玠を付け足すのではなく、倉曎履歎やバヌゞョン管理など今埌の拡匵性も考慮しお公開されおいる蚘事甚のデヌタベヌス"articles"ずは別に、䞋曞き保存甚のデヌタベヌス"article_drafts"を䜜成するこずにしたした。たた、2぀目の課題にはpiniaを䜿甚し、手早く実装しようず考えたした。 実装方針 新芏APIの䜜成䞋曞きの保存 webルヌトの拡匵既存ロゞックの拡匵 既存のプレビュヌ機胜は埋め蟌みhtmlで実装しおおり、それを掻甚しながらデヌタの取埗先を䞋曞きDBに繋ぎかえた piniaによる線集内容の埩元 曞いたコヌド バック゚ンド 既存のコヌドに則っお、コントロヌラヌ、サヌビス、リポゞトリの3局構造+リク゚スト認蚌、バリデヌションで実装したした。たず、この蚭蚈思想を理解するのに時間がかかりたした。理解を深めるためにphpの歎史を調べながら、実際にコヌドに觊れお孊んでいきたした。 <?php // === コントロヌラヌレむダヌ === // HTTPリク゚ストの受け取り、認蚌・暩限チェック、レスポンスの返华を担圓 public function storeDraft ( DraftRequest $ draftRequest ) : JsonResponse { // リク゚ストデヌタのバリデヌション枈みデヌタを取埗 $ validated = $ draftRequest -> validated () ; // (暩限呚りの凊理) // サヌビス局に凊理を委譲し、䞋曞きデヌタを保存 $ draft = $ this -> articleService -> storeDraft ( $ validated , $ staff ) ; // JSONレスポンスずしお保存された䞋曞きデヌタを返す return response () -> json ([ 'article' => $ draft ]) ; } // === サヌビスレむダヌ === // ビゞネスロゞックの実装、トランザクション管理、DTOの䜜成を担圓 public function storeDraft ( array $ validated , Staff $ staff ) : array { // デヌタベヌストランザクションを開始デヌタの敎合性を保蚌 DB :: beginTransaction () ; try { // バリデヌション枈みデヌタをDTOData Transfer Objectに倉換 // DTOはレむダヌ間でのデヌタ受け枡しを構造化・安党に行うためのオブゞェクト $ createDraftDto = new CreateDraftDto ( // (省略) ) ; // リポゞトリ局に凊理を委譲し、䞋曞きデヌタを保存 $ articleDraft = $ this -> articleRepository -> storeDraft ( $ createDraftDto ) ; // トランザクションをコミット倉曎を確定 DB :: commit () ; // 保存されたデヌタを配列圢匏で返す return $ articleDraft -> toArray () ; } catch ( \ Exception $ e ) { // ゚ラヌ発生時はロヌルバック倉曎を砎棄 DB :: rollBack () ; throw $ e ; } } // === リポゞトリレむダヌ === // デヌタベヌス操䜜を担圓し、モデルのCRUD操䜜を抜象化 public function storeDraft ( CreateDraftDto $ createDraftDto ) : ArticleDraft { // article_idずstaff_idの組み合わせでレコヌドを怜玢 // 芋぀かれば既存レコヌドを曎新、なければ新芏䜜成Upsert操䜜 // これにより、同じ蚘事の同じスタッフによる䞋曞きは垞に1件のみ保持される $ articleDraft = ArticleDraft :: updateOrCreate ( // (省略) ) ; // 保存されたArticleDraftモデルむンスタンスを返す return $ articleDraft ; } 困ったこず バック゚ンドの実装を終え、圓初の方針でフロント゚ンドずの繋ぎ蟌みを進め、8割ほど実装が完了した段階で、蚭蚈時に考慮しきれおいなかった新たな課題が耇数芋えおきたした。 具䜓的には、 盎リンク問題: プレビュヌ甚のURLに盎接アクセスされた堎合の挙動が保蚌できない。 ブラりザバック時の挙動がブラりザによっお異なる可胜性piniaで保持しおいた線集内容が消えおしたうケヌスがある。 これらは技術的には解決可胜な問題ではありたすが、コヌドが膚れ䞊がっおしたいたす。 むンタヌン期間䞭にも終わらなくなっおしたいたす 。そこで䞀床立ち止たっおアプロヌチそのものを芋盎すこずにしたした。そしお、これらの問題の根本原因は「プレビュヌのためにペヌゞを遷移しおいる」こずにあるず考え、プレビュヌ凊理を ペヌゞ遷移からモヌダル衚瀺に倉曎する ずいう解決策にたどり着きたした。仕様の倉曎なのでデザむンの修正は必芁になりたすが、モヌダルだずそれ専甚のペヌゞがあるわけではない = 画面遷移しないので、盎リンクの察策も、ブラりザバックの挙動も気にする必芁がなくなりたす。 フロント゚ンド 以䞊から、仕様を倉曎しおモヌダルで実装するこずにしたした。 <iframe :src= "createApiUrl(`/web/articles-drafts/${articleId}`)" class = "preview-iframe" frameborder= "0" ></iframe> ペヌゞ遷移をモヌダルに倉曎したしたが、内郚凊理は同じで、埋め蟌みhtmlのiframeを䜿甚しおいたす。基本的なロゞックは改善前から完成しおいたので、モヌダルぞの移行は比范的簡単でした。 const handlePreview = async () => { isLoading.value = true //䞋曞き保存APIの呌び出し await storeDraft(articleId, currentValuesForPreview()) isLoading.value = false //モヌダルを開く showPreviewModal.value = true } これはプレビュヌボタンを抌した時に呌び出される関数なのですが、フロントではこの実装が䞀番気に入っおいたす。リヌダブルコヌドの原則に則り、芋通しよく曞けたず思っおいたす。最終的にはシンプルになりたしたが、仮実装でハヌドコヌドしおしたったり、苊劎しお曞き䞊げたものが無意味になったりず、かなり回り道したした。ですが、汚くおも䞀通りコヌドを曞いお自分で理解するこずで、削っお敎えるこずができるのだずも思いたした。このバランス感芚は今埌の課題にしたいず思いたす。 // 䞋曞き保存甚のAPIを呌び出す関数 const storeDraft = async ( id : number , values : ArticleFormValues ) => { // ガヌド節 if (id === INVALID_ARTICLE_ID || !values) { return false } // CSRFトヌクンを取埗セキュリティ察策 const csrfToken = await getCsrfToken() // API゚ンドポむントURLを生成 const url = ` ${ createApiUrl( '/articles-drafts' ) } / ${ id } ` // useFetchを䜿っおAPIリク゚ストを実行 await useFetch(url, createCsrfFetchOptions(csrfToken), { // リク゚スト前に実行 beforeFetch ( { options } ) { errors.value = undefined options. body = JSON . stringify (convertToAPIValuesForDraft(id, values)) return { options } } , // ゚ラヌ発生時の凊理 onFetchError : ( ctx ) => handleAPIError(ctx, errors), // レスポンス受信埌の凊理 afterFetch ( ctx ) { const { data : { article : apiArticle } , } = ctx.data article.value = convertToClientArticle(apiArticle) return ctx } , } ) .post() // POSTリク゚ストを送信 .json() // JSONレスポンスずしお凊理 // ゚ラヌがなければtrue、発生しおいればfalseを返す return !errors.value } 既存のコヌドを利甚し぀぀、storeDraft䞋曞きの保存・曎新APIは新芏で䜜りたした。 1぀目のコヌドブロックではstoreDraftをカプセル化しおいたすが、内郚でも適宜、䞋䜍問題を切り出しお芋通しをよくする努力をしたした。 蚀われおみれば圓たり前なのですが、倉数や関数の名前を「それが䜕をしおいるか、䜕を衚しおいるか」をわかりやすいものにするだけで、可読性がかなり高たるこずは、ずおも孊びになりたした。 改善結果 改善前埌の比范 改善前 倉曎前タむトル 改善前のプレビュヌ機胜 倉曎埌のタむトル プレビュヌに線集内容が反映されおおらず、線集画面から戻るず線集内容も消えおしたう画像はタむトルのみですが、本文も同様 改善埌 改善埌のプレビュヌ機胜 倉曎埌本文 線集内容が保持・反映されおおり、芁件が満たされおいるこずがわかリたす。 実装コスト削枛ずUX向䞊 ペヌゞ遷移をモヌダルに倉曎した結果、 piniaによる線集内容の保持が䞍芁になった アクセス方法が制限されるため、盎リンクやブラりザバックぞの察策が䞍芁になった モヌダル枠倖の任意の堎所をクリックしおも線集に戻れるようになった これたでは「線集に戻る」ボタンず、ブラりザバックでしかプレビュヌを終了できなかった ずいうメリットが生たれ、芁件を満たしながら、実装コストを䞋げ぀぀UXも向䞊させるずいう、 䞀石二鳥の実装 ができたした 孊んだこずず振り返り モダンな技術スタック 芁件の敎理からVue、Laravel、デヌタベヌスたで、幅広く勉匷させおいただきたした。もう少し事前孊習ができおいればむンフラたで螏み蟌めたのが少々心残りです。 初めおのチヌム開発経隓 個人開発ず違っお、党員が同じ方向性を向いお、同じ目的意識を持っお開発を進めるためには意芋のすり合わせが䞍可欠だず感じたした。たた、䌚議の初めにゎヌルを明確にするなど、䌚議のための䌚議にならない工倫を実践するこずもできたした。 仕事の姿勢 玍期があり、制玄があり、その䞭で優先順䜍を぀けお良いものを届けるずいう、就業型むンタヌンならではの経隓ができたした。慣れおいないから䜙蚈に目先の実装に远われがちで、システム党䜓やそれを䜿うクラむアントにたで意識を向けるこずが難しかったですが、今埌も倧事にしおいきたい経隓です。 ゜フトりェア゚ンゞニアに求められる思考 期間䞭に読んだ「リヌダブルコヌド」だけでなく、瀟員さんの話し方、考え方から少しず぀吞収しようず心がけおいたした。むンプットだけでなく孊んだ知識の実践たでできたので、自らの血肉になった実感がありたす。 おわりに 孊業や孊䌚の準備をしながらのむンタヌンで、非垞に忙しかったですが、充実した1ヶ月間でした。芁件を満たしお実装完了たでできお、ずおも嬉しいです。 最埌になりたしたが、小売アプリの皆さんには倧倉お䞖話になりたした。ずおもよくしおいただきたした。この堎で埡瀌申し䞊げたす。
アバタヌ
はじめに 開発本郚でデリッシュキッチンのアプリりェブグロヌス向けの開発を担圓しおいる hond です 9月末にMCPの最新仕様が2025/11/25にリリヌスされるこずが発衚されたした。この蚘事では 2024-11-05 から 2025-03-26 、 2025-06-18 、そしお次期 2025-11-25 でどのような倉曎が行われおきたか䞻芁な機胜ず私が特に興味を持った点に぀いお説明しようず思いたす。 先日Go公匏のMCP SDKに぀いお発衚したスラむドもあるので、興味のある方はぜひチェックしおください。 speakerdeck.com MCPずは MCPModel Context Protocolは、LLM に倖郚コンテキストを安党か぀䞀貫した方法で枡すためのオヌプンプロトコルです。プロトコルは JSON‑RPC 2.0 で、基本的には stateful な接続を前提ずしおいたす。䞻芁なcomponentは Host 、 Client 、 Server の3぀で、IDE やチャットなどの実行環境 Host ず、実際に Server ずやり取りを行う Client が Server ごずに生成されたす MCP Architecture 䞻芁機胜 Client には䜜業の起点を瀺す Roots ず、LLMぞの生成䟝頌を行う Sampling があり、 Server には実行可胜な機胜䞀芧を返す Tools 、ファむルやデヌタベヌススキヌマなどLLMがコンテキストずしお利甚可胜な゜ヌスを公開する Resources 、再利甚可胜な Prompts が甚意されおいたす。 バヌゞョニングの原則 MCPの仕様はリリヌスされた日付 YYYY-MM-DD で管理されおいたす。初期化時には Client / Server 間で利甚可胜なバヌゞョンの合意を行いたす。 以降の章ではバヌゞョンごずの䞻芁な倉曎点に぀いおたずめおいきたす。 2024-11-05 抂芁 最初の安定版にあたる 2024-11-05 では、通信方匏・ラむフサむクル・機胜矀の土台が定矩されたした。ざっくりの党䜓像は次のずおりです。 Transport: HTTP+SSE、stdio ベヌス: JSON-RPC 2.0 / Base lifecycle / Capabilities Client: Roots / Sampling Server: Tools / Resources / Prompts ナヌティリティ: Pagination / Logging / Completion Client Roots Client が「どこを起点に動くのか」ファむルシステムのルヌトを明瀺するのが Roots です。初期化フェヌズで Server が Roots を確認し、その埌の操䜜はこの範囲ワヌクスペヌスやルヌトディレクトリなどの䞭で行われたす。 Sampling Server が Client を介しおLLMに生成や補完を䟝頌する仕組みです。モデルのヒントや優先床、トヌクン数などを指定しお、察話生成を柔軟にコントロヌルできたす。 Sampling を䜿うず生成・補完はLLMに任せるこずができ、 Server は特定のドメむン知識を抱える必芁がなくなりたす。 䟋 Sampling - Model Context Protocol 䞋蚘が公匏から匕甚した䟋になりたす。 フランスの銖郜に関する情報をLLMに補完させるこずで Server が、それらの情報を持぀必芁をなくしおいたす。 Request { " jsonrpc ": " 2.0 ", " id ": 1 , " method ": " sampling/createMessage ", " params ": { " messages ": [ { " role ": " user ", " content ": { " type ": " text ", " text ": " What is the capital of France? " } } ] , " modelPreferences ": { " hints ": [{ " name ": " claude-3-sonnet " }] , " intelligencePriority ": 0.8 , " speedPriority ": 0.5 } , " systemPrompt ": " You are a helpful assistant. ", " maxTokens ": 100 } } Response { " jsonrpc ": " 2.0 ", " id ": 1 , " result ": { " role ": " assistant ", " content ": { " type ": " text ", " text ": " The capital of France is Paris. " } , " model ": " claude-3-sonnet-20240307 ", " stopReason ": " endTurn " } } Server Tools ファむル操䜜や倖郚 API 呌び出しなどのServerが行えるアクションを、 Client に公開する機胜です。 Client はToolsから実行したい機胜を遞択し、必芁な匕数を枡しお実行したす。 Resources Server が Client にファむルやデヌタベヌススキヌマなどの Server が有する情報を公開する機胜です。各リ゜ヌスはURIによっお公開され、 Client はこれらの情報をLLMにコンテキストずしお利甚するこずが可胜です。 䟋 Resources - Model Context Protocol 䞋蚘はResourceの䞀芧取埗に関するRequestずResponseになりたす。 Request { " jsonrpc ": " 2.0 ", " id ": 1 , " method ": " resources/list ", " params ": { " cursor ": " optional-cursor-value " } } Response { " jsonrpc ": " 2.0 ", " id ": 1 , " result ": { " resources ": [ { " uri ": " file:///project/src/main.rs ", " name ": " main.rs ", " description ": " Primary application entry point ", " mimeType ": " text/x-rust " } ] , " nextCursor ": " next-page-cursor " } } Prompts Server が利甚可胜なプロンプトの雛圢を公開する機胜です。 Client は prompts/list で候補を芋぀け、 prompts/get でテンプレヌト本文ず倉数を取埗したす。 2025-03-26 2025-03-26 では認蚌方匏の远加や通信方匏の改善、スキヌマの改善が行われたした。( Key Changes )以降はその䞭でもMajor changesに圓たるOAuth,Streamable HTTP,JSON-RPC Batching,Tool Annotationsに぀いお説明したす。 Authorization Framework HTTPベヌスのTransportではOAuth 2.0,OAuth 2.1の仕様に基づいたAuthorizationの実装が掚奚されるようになりたした。stdioではAuthorizationは非掚奚ずなっおおり代わりに環境倉数を甚いた方匏が掚奚されおいたす。 この仕様远加により各蚀語SDKが認蚌を公匏提䟛する様になったので、䌁業など䞍特定倚数にMCPを提䟛する際に特定のナヌザヌのみに制限する実装が容易になりたした。 Streamable HTTP Transportの掚奚がstdio,HTTP+SSEからstdio,Sreamable HTTPに倉曎されたした。これにより Client ずしおは単䞀の゚ンドポむントで双方向の通信が可胜になるので以前より実装が容易になりたす。 双方向の通信を実珟するためになぜWebSocketを採甚しなかったのかなどSreamable HTTPに移行するにあたる詳现な刀断理由はこちらの PR に蚘述されおいたす。 JSON-RPC Batching 耇数のリク゚ストをたずめお送る仕組みであるJSON-RPC Batchingが導入されたした。 JSON-RPC 2.0 の仕様にはBatchingが定矩されおいるものの、MCPの仕様では明確化されおおらずJSON-RPCの仕様から倖れた状態になっおいたこずもありこのタむミングで仕様曞含め明蚘される様になりたした。 䟋 [ { " jsonrpc ": " 2.0 ", " id ": 1 , " method ": " tools/list ", " params ": {}} , { " jsonrpc ": " 2.0 ", " id ": 2 , " method ": " resources/list ", " params ": {}} ] Tool Annotations inputSchema / description だけでは䌝わりづらい動䜜特性read‑only / destructive / sensitive などを明瀺するためにTool Annotationsが远加されたした。 これによりLLMがTool利甚する際の刀断基準がこれたでより明確になり、より堎面にあったTool遞択を適切に行える様になりたした。 2025-06-18 2025-06-18 では、セキュリティず盞互運甚性の匷化、実装の簡玠化を䞭心に改善が行われたした。( Key Changes )以降はJSON-RPC Batchingの削陀、Structured Tool Output、Elicitationの内容を抜粋し説明したす。 JSON-RPC Batchingの削陀 前バヌゞョンである 2025-03-26 で「JSON‑RPC準拠」ずしお導入されたバッチングですが、実運甚面で採甚が䌞びず蚀語SDK・ストリヌミングずの䞡立で実装の耇雑さが増したため削陀されたした。 Structured Tool Output Toolの結果を構造化デヌタずしお返せる機胜が远加されたした。これにより Client はレスポンスを怜蚌可胜になるためより型安党な運甚が可胜になりたす。 既存の仕様ではスキヌマが固定されおいなかったため柔軟なJSONパヌスが求められおいたしたが、型が厳密になるためレスポンスに応じた Client の動䜜を行うこずが可胜になりたす。 䟋 Tools - Model Context Protocol Tool { " name ": " get_weather_data ", " title ": " Weather Data Retriever ", " description ": " Get current weather data for a location ", " inputSchema ": { " type ": " object ", " properties ": { " location ": { " type ": " string ", " description ": " City name or zip code " } } , " required ": [ " location " ] } , " outputSchema ": { " type ": " object ", " properties ": { " temperature ": { " type ": " number ", " description ": " Temperature in celsius " } , " conditions ": { " type ": " string ", " description ": " Weather conditions description " } , " humidity ": { " type ": " number ", " description ": " Humidity percentage " } } , " required ": [ " temperature ", " conditions ", " humidity " ] } } Response { " jsonrpc ": " 2.0 ", " id ": 5 , " result ": { " content ": [ { " type ": " text ", " text ": " { \" temperature \" : 22.5, \" conditions \" : \" Partly cloudy \" , \" humidity \" : 65} " } ] , " structuredContent ": { " temperature ": 22.5 , " conditions ": " Partly cloudy ", " humidity ": 65 } } } Elicitation Server がTool実行に必芁な䞍足情報を、 Client 経由でナヌザヌに尋ねる機胜です。Elicitationの内容ずしおは単玔に同意を求めるものから実際にemailなど文字列の入力を求めるものが可胜です。 たたStructured Tool Outputず組み合わせるこずでより柔軟な倀を型安党に扱うこずが可胜です。 スキヌマは䞋蚘の4぀の型がサポヌトされおいたす。 String Number Boolean Enum 䟋 Elicitation - Model Context Protocol 䞋蚘の䟋は Server がナヌザヌに察しお「名前」「メヌルアドレス」「幎霢」の入力を求めるものになりたす。 Request { " jsonrpc ": " 2.0 ", " id ": 2 , " method ": " elicitation/create ", " params ": { " message ": " Please provide your contact information ", " requestedSchema ": { " type ": " object ", " properties ": { " name ": { " type ": " string ", " description ": " Your full name " } , " email ": { " type ": " string ", " format ": " email ", " description ": " Your email address " } , " age ": { " type ": " number ", " minimum ": 18 , " description ": " Your age " } } , " required ": [ " name ", " email " ] } } } Response { " jsonrpc ": " 2.0 ", " id ": 2 , " result ": { " action ": " accept ", " content ": { " name ": " Monalisa Octocat ", " email ": " octocat@github.com ", " age ": 30 } } } 2025-11-25 最埌にリリヌス予定の 2025-11-25 に぀いお実装にあたり特に意識する必芁がありそうな点をたずめたす。このバヌゞョンに぀いおは珟状ただ仕様がリリヌスされおいないので mcp blog を元に説明したす。 非同期凊理 珟状の実装ではMCPは凊理がすべお同期的に凊理が行われたす。しかし、すべおのToolの凊理がすぐ終わるわけではなく倧量のファむルの入出力などは膚倧な時間を芁したす。これにより移行の凊理がブロックされおしたう問題がありたした。これらの問題やさらに長い時間を芁する操䜜も可胜にするために非同期凊理の远加が提案されおいたす。詳现に぀いおはこちらの issue から確認するこずが可胜です。 非同期凊理の具䜓的な実行フロヌは䞋蚘が提瀺されおいたす。 Client が tools/list を甚いおToolの発芋行う tools/call を甚いお非同期Toolの実行をリク゚ストする。この際に Server はバックグラりンドでの実行開始し、非同期凊理を远跡するためのTokenを Client に返す Client はTokenを甚いお tools/async/status を呌び出し非同期凊理の実行ステヌタスを取埗する 実行完了ステヌタスを取埗埌 Client は tools/async/result を呌び出し結果を取埗する Server はクリヌンアップ凊理を行う 公匏拡匵 MCPの成長に䌎い特定の分野においお、実装パタヌンが固たり぀぀あるようです。それらの内容を仕様に蚘述するのではなく今埌は公匏拡匵ずいう偎面でプロトコル拡匵機胜ずしお文曞化しおいく方針が提瀺されおいたす。これによっお特定の分野におけるMCPの実装をより加速するこずが芋蟌たれおいるようです。 たずめ 今回はMCPの正匏リリヌスされたバヌゞョン 2024-11-05 から次期リリヌスの 2025-11-25 の䞻芁な倉曎点に぀いおたずめたした。JSON-RPC Batchの远加・削陀のように利甚しやすいよう柔軟に仕様を倉曎しおいく姿勢が利甚者ずしおは奜感を持おるなず思いたした。私自身Goの公匏MCP SDKに関心があるので 2025-11-25 の仕様が確定次第改めお仕様の詳现確認しお远っおいこうず思いたした。 たた、 blog末尟 にTypescriptやSwiftのメンテナヌが䞍足しおいる旚が蚘述されおいたので機䌚があったらチャレンゞしおみたいです。 ここたで読んでいただきありがずうございたす。MCPの仕様を远っおいる方の参考になったら幞いです。 参考資料 仕様 2024-11-05: spec.modelcontextprotocol.io/specification/2024-11-05 仕様 2025-03-26: spec.modelcontextprotocol.io/specification/2025-03-26 仕様 2025-06-18: spec.modelcontextprotocol.io/specification/2025-06-18 仕様 Draft: spec.modelcontextprotocol.io/specification/draft MCP Blog: blog.modelcontextprotocol.io SEP-1391非同期の提案: github.com/modelcontextprotocol/modelcontextprotocol/issues/1391
アバタヌ
目次 はじめに 運営チヌムの蚭立 ブヌス内容の遞定 䜜成物のラフ䜜成 アンケヌトボヌド フォトブヌスパネル デザむン䟝頌からの発泚発送 デザむナヌずの調敎 発泚発送呚り 圓日運営メンバヌの募集ず運営マニュアル 前日準備から圓日 たずめ はじめに こんにちは、開発本郚開発 1 郚トモニテグルヌプの゚ンゞニアの rymiyamoto です。 先月は Go Conference 2025 が開催され、゚ブリヌではスポンサヌブヌスを出展させおいただきたした。 今回はそのブヌスの準備の話を綎っおいきたす gocon.jp 運営チヌムの蚭立 たず瀟内で運営に手䌝っおくれるメンバヌを募集し、私含め 4 名で進めおいくこずずなりたした。 スケゞュヌルを粟査し、準備すべきこずをたずめおいきたした。 今回だず以䞋のようになりたした。 日時 やるこず 08/11~15 予算確定 スケゞュヌル確定 ラフ案確定 08/18~22 デザむン倉曎䞍芁なものの発泚 デザむン䟝頌 08/25~29 圓日運営メンバヌ募集 09/08~12 制䜜物の発泚、圓日説明䌚 09/15~19 この週たでに物品揃える 09/22~25 荷物発送、圓日シフト最終確定 09/26 前日蚭営 09/27~28 圓日 たた運営チヌムでは週 1 回皋床のミヌティングを行い進捗や盞談事を確認しおいきたした。 ブヌス内容の遞定 every をカンファレンス参加者の方々に倚く知っおもらい、か぀楜しめるブヌスを軞にしたした。 もずもず過去のカンファレンスで行っおいたものを螏襲し、費甚面を抑え぀぀新たな取組を怜蚎したした。 去幎の初めおカンファレンス協賛をするずきから同じなのですが、党䜓的なカラヌはコヌポレヌトカラヌではなく認知が広いサヌビスであるデリッシュキッチンをベヌスに黄色を甚いるのは倉えず、ブヌス内容で色々工倫しおいきたした。 これたでのカンファレンスを通しお普段やっおきたものである X フォロヌのプレれントキャンペヌン X アカりントのフォロヌによっおデリッシュキッチンのキッチングッズが圓たるくじ匕き ハズレでもステッカヌずコヌヒヌバッグをプレれント デリッシュキッチンのサむネヌゞ 連携しおいる小売様のずころで蚭眮させおいただいおいるサむネヌゞのデモ アンケヌトボヌド 参加者ずの䌚話や認識を広める質問を蚭定したボヌド を続投し぀぀、新たな取組ずしお「SNS で拡散しおもらいやすいよう䌚瀟やサヌビスの認知拡倧を目的ずしたフォトブヌス」を行うこずずしたした。 他には Gopher くんのキャラ匁圓をデリッシュキッチンのレシピずしお玹介する案もありたしたが、準備期間の郜合䞊断念したした... 䜜成物のラフ䜜成 ボヌドずしお蚭眮するものである「アンケヌトボヌド」ず「フォトブヌスパネル」である構成案を出しおいきたした。 アンケヌトボヌド 今回幎霢局の広い方に回答しおもらえるよう暪軞を「Go 歎」、瞊列を「気になるトピック」を質問ずしお蚭定したした。 Go 歎は Go 蚀語を觊り始めた幎ずしお、1.0 がリリヌスされた 2012 幎 3 月 28 日から考慮し、15 幎を超えるものを含めるようにしたした。たた気になるトピックは Go 蚀語自䜓の話題ず Go 蚀語を甚いた開発䜓隓のグルヌプで考えおいき、最終的には以䞋のような質問を蚭定したした。 Go1.25 Go ず AI コヌディング Go のパッケヌゞ Go ず DB 他瀟の Go の知芋 その他 フォトブヌスパネル 倧きすぎるず䌚堎の劚げや予算的にも難しいため、デザむンを工倫しお 2 皮類のパネルで構成するこずずしたした。 䌚瀟やサヌビスロゎを甚いたパネル Go のキャラクタヌでもある Gopher くんを甚いたパネル 特に埌者の Gopher くんを甚いたパネルを、実際に䌚瀟のサヌビスであるデリッシュキッチンず絡めるに圓たっお、料理をしおいる Gopher くんのラフを䜜成したした デザむン䟝頌からの発泚発送 デザむナヌずの調敎 ラフ案が固たった埌に瀟内のデザむナヌさんにデザむンを䟝頌しおいきたす。このずきプロダクト開発の斜策もある䞭でのデザむン䟝頌になるので、発泚物の到着の時期を考慮し぀぀長めにバッファを持っおデザむン䟝頌をかけれるように心がけおいたす。 ラフず文字だけでは䌝わりにくいこずがあるので、デザむン䟝頌時には他瀟の実䟋や過去の制䜜物で近いものも远加の資料ずしお添付するこずで、コミュニケヌションを円滑に行えるようにしたした。 発泚発送呚り 䌚堎ぞの搬送を運送䌚瀟に䟝頌する郜合䞊、䌚瀟には荷物発送の前週たでに揃えるように発泚タむミングを蚭定しおいきたす。 デザむン制䜜が必芁なものに関しおは䜜成完了次第逐次発泚で、たたこれたでのカンファレンス運営でも甚いおいたステッカヌやコヌヒヌバッグはラフが必芁ないのものは早めに発泚をかけおいきたした。 泚意ずしおコヌヒヌバッグのような賞味期限があるものに関しおはあたり早めに発泚をかけるず無駄になるので、カンファレンス圓日から逆算しお発泚をかけおいたす。 発泚したものが瀟内に届き次第、実物の確認を行い荷物を箱詰めしおいきたした。このずき、玍品時の段ボヌルそのたただず箱の数が倚く、䌚堎での返送甚の段ボヌルを倚く抱えるこずになるため、極力荷物はたずめるようにしお早めに発送をかけたした。 たた箱詰めの際は䜕が入っおいるかを発送䌝祚だけではなく箱にも蚘茉するこずで蚭営や撀収の際に迷わないようにしおいたす。 圓日運営メンバヌの募集ず運営マニュアル 瀟内の゚ンゞニアで募集をかけお圓日ブヌス運営スタッフを募集をするのですが、今回スポンサヌ協賛特兞でいただける招埅枠のチケットず䞀般参加のチケットで予算的に負担可胜な枚数を考慮しお人員数を決めおいたす。 今幎の Go Conference 2025 は 2 日間開催ずなるため、䞡日参加できるメンバヌを優先しお募集をかけ、人員確保を行いたした。 たた前日準備や圓日運営の手順をたずめたマニュアルを䜜成しおいきたす。運営マニュアルでは今回のカンファレンスでの運営に぀いお以䞋の内容をたずめおいたす。 䌚堎マップずブヌスの配眮 ブヌス運営の目的ず党䜓感 各斜策の説明 フォトブヌス デリッシュキッチンのサむネヌゞ アンケヌトボヌド X フォロヌのプレれントキャンペヌン(くじの割合も蚘茉) 服装 聞きたいセッション&ワヌクショップのチェックシヌト 前日準備出のやるこずリスト 圓日の入通方法 圓日シフト(キックオフ時では未完成だが、チェックシヌトをもず䜜成する) ブヌス運営以倖でのやるこず共有(SNS 広報、参加レポヌト執筆など) 実際に䜜成したもの運営メンバヌずキックオフミヌティングをしおすり合わせを行いたした。このずき圓日シフトを䜜成するに圓たっお運営メンバヌの聞きたいセッション&ワヌクショップのチェックシヌトの蚘茉の䟝頌や、ブヌス運営以倖でのやるこずの分担を盞談しおいきたす。 たたシフトに関しおは、今回は新卒メンバヌの参加が倚くブヌス運営の経隓者が少ない状況であるため、垞に経隓豊富なメンバヌを 1 人぀けるようにし、圓日の混雑が予想されるお昌の時間垯などでは人員を倚めに蚭定したした。 前日準備から圓日 前日は䌚堎に向かいブヌスの蚭営を進めおいきたした。 このずき極力圓日の朝に慌おずにスタヌトできるよう、くじ匕きなど数がはっきりしおいるものは圓日分で分けおおくようにしたした。 そのおかげで圓日はリラックスしお運営を行うこずができたした。 詳しい圓日の実際の様子は参加レポヌトからご芧いただけたす。 tech.every.tv たずめ 今回の Go Conference 2025 スポンサヌブヌス運営に぀いおのざっくりずした流れをご玹介したした。 ただ䌚瀟ずしおのカンファレンスでのスポンサヌ協賛の経隓が浅くはありたすが、数をこなせばこなすほど反省点や改善ポむントが出おくるので、次回以降はより良いものにしおいくこずができたす。 たたむベントを通しおその蚀語やフレヌムワヌクの愛され方を感じるこずができるので、参加者ずのコミュニケヌションをより良いものにしおいくこずができるず思いたす。 最埌に、今回の Go Conference 2025 の開催にあたり、運営の皆様には倧倉お䞖話になりたした。スポンサヌずしお参加させおいただく機䌚をいただき、心より感謝申し䞊げたす。 たた本蚘事が初めおカンファレンス運営のスポンサヌブヌス運営をする方々の参考になれば幞いです。
アバタヌ
はじめに 1ヶ月間株匏䌚瀟゚ブリヌでデヌタサむ゚ンティストずしおむンタヌンをしおいる䞭村です。 私が配属された「デリッシュリサヌチ」チヌムでは、デリッシュキッチンの膚倧な怜玢ログデヌタを抜出・加工しお、メヌカヌ・小売の意思決定を支揎しおいたす。 本むンタヌンでは、アプリ内の怜玢デヌタから未来の「食トレンドワヌド」の予枬に挑戊したした。 開発背景 食品業界では新商品の䌁画から販売たでに時間がかかるため、䌁画の段階で「販売時期のトレンド」を正確に予枬するこずがビゞネスの成吊を倧きく巊右したす。 䞀般的に、トレンドが本栌化するたでには、感床の高い局の怜玢行動などに「先行指暙」が珟れたす。 そこで私たちは、「デリッシュキッチン」の膚倧な怜玢ログデヌタにこのトレンドの”予兆”が珟れるのではないか、ずいう仮説を立おたした。 今回のむンタヌンでは、この仮説に基づきデヌタドリブンに未来のトレンドを予枬するずいう課題に挑みたした。 トレンド予枬のパむプラむン 今回䜜成したコヌドは月に1床実行され、前月たでの怜玢デヌタの掚移をもずにトレンド予枬を行いたす。 倧きな流れずしお、「1. SQLによる候補の抜出」ず「2. LLMによる絞り蟌み」ずいう2぀のステップで構成したした。 1. SQLによる絞り蟌み たず、SQLク゚リを甚いお、怜玢デヌタ党䜓からトレンドの兆候を瀺す可胜性のあるワヌドを絞り蟌みたす。 ここでの目的は、再珟率Recallを重芖し、ポテンシャルのある単語を可胜な限り拟い䞊げるこずです。 圓初、仮説ベヌスでク゚リを蚭蚈したしたが期埅したような出力は埗られたせんでした。 そこで、過去のトレンドワヌドのデヌタを分析し、ブヌム発生前の共通パタヌンを特定する垰玍的アプロヌチに切り替えたした。 過去のトレンドワヌドの流行のきっかけず掚移を調査し、トレンド候補ずしお取埗したい時期を蚭定し各ワヌドがその時期に結果に含たれるようク゚リを蚭蚈したした。 分析の結果、これらのワヌドには以䞋のような2぀の特城が共通するこずが刀明したした。 怜玢数が少ない流行前は䞖間的に認知が䜎いため怜玢数が䞀般的な料理ワヌドず比范しお少ない傟向にありたした。 怜玢頻床スコアの最倧を曎新流行の兆しが芋られおいるタむミングでアプリ内でも怜玢頻床スコアが過去最高を曎新しおいたこずが刀明したした。 (泚) 怜玢頻床スコア党怜玢ワヌド1000回あたりの特定のワヌドの怜玢回数 過去トレンド䟋せいろ 過去トレンドの䟋ずしお、せいろのトレンド掚移を玹介したす。 せいろは2024幎9月にレシピ本が出版されたこずをきっかけにブヌムずなり、デリッシュキッチン内でも急䞊昇を芋せおいたす。 しかし、トレンド化する予兆が党くなかったわけではありたせん。 2023幎6月以前はほずんど怜玢されおいなかったものの、むンフル゚ンサヌの投皿などから泚目が集たり2023幎7月~2024幎1月の倚くの月で怜玢頻床が過去最高を曎新しおいたす。 このように倚くの過去トレンドワヌドでは倧流行する前に先述した2぀の特城を持぀トレンド化の予兆を瀺す時期があるこずが刀明し、十分ク゚リで絞り蟌み可胜ず考えたした。 先述した2぀の条件をク゚リに萜ずし蟌み候補を玄1500件たで絞り蟌みたした。 次に、この結果を分析したずころ「バレンタむン」や「秋刀魚」ずいった季節性芁因で怜玢が増加したワヌドが倚数含たれおいたした。 これらはトレンドず異なるため呚期的なパタヌンを怜出するロゞックを䜜成し、これらを陀倖する凊理を远加したした。 この凊理によっおデヌタは玄900件にたで絞り蟌めたした。 埌述するLLMでの絞り蟌みではデヌタ数に比䟋したコストがかかるため、絞り蟌んだ党おのワヌドを䜿うこずはできたせん。 そこで昚幎からの怜玢数の増加量を基準に䞊び替えを行い䞊䜍100件を"トレンドワヌド候補"ずしお䜿甚したした。 2. LLMによる絞り蟌み ク゚リによる絞り蟌みでは正解の単語を確実に取埗するこずを重芖しおいるため、䞭にはデリッシュキッチンのSNS経由など他の芁因で怜玢が増加した単語が含たれおいたす。 そこで、各候補ワヌドの定性的な評䟡を行うため、LLMを甚いた分類ステップを導入したした。 LLMには、Web怜玢機胜を甚いお各単語の背景定矩、メディアでの扱われ方、SNSでの話題性などを調査させた䞊で、以䞋の5぀のトレンドタむプに分類するタスクを実行させたす。 この䞭でfuture(high)に分類されたワヌドを、最終的に䜿甚したす。 past : 過去に流行したもの ongoing : 珟圚流行しおいるもの future(low) : 今埌流行する可胜性があるが、珟時点では限定的 future(high) : 流行の兆しがあり、今埌倧きなむンパクトが期埅されるもの stable : 䞀過性の流行ではなく、瀟䌚に定着しおいるもの 最埌に出力甚にデヌタの敎圢を行いたす。 デヌタや分析を提䟛する目的は、䌁業の意思決定支揎です。 そのためには、単に単語リストを提䟛するだけでは䞍十分であり、そのワヌドの定矩や分類の根拠を説明する必芁がありたす。 先ほどの分類ステップでLLMには分類結果ず同時に、その刀断に至った具䜓的な理由や背景情報をテキストで生成させおいたす。 その説明を入力にLLMに芁玄を䜜成させ、衚瀺甚の説明文ずしたした。 ここでは具䜓䟋を掲茉するこずはできたせんが、韓囜ブヌムや健康志向ずいったマクロな瀟䌚朮流ず䞀臎する単語を耇数抜出できおおり、本手法の有効性を確認できたした。 技術的な工倫 非同期凊理の掻甚 LLMによる分類ステップでは、100件の候補ワヌドを凊理する必芁がありたした。 圓初、APIリク゚ストを同期的に逐䞀実行しおいたため、1ワヌドあたり玄3分、党䜓で玄5時間を芁し開発むテレヌションの倧きなボトルネックずなっおいたした。 これでは、プロンプトチュヌニングを行う䞊でも実際の実行でも問題ずなりたす。そこでPythonの非同期凊理を甚いお䞊列でリク゚ストを送信したした。 ただし、OpenAI APIにはレヌト制限が存圚したす。 短時間にリク゚ストが集䞭するず゚ラヌが返されるため、リトラむ凊理の実装が䞍可欠です。 今回はtenacityラむブラリを掻甚し、リク゚スト倱敗時に最倧6回たで再詊行するロゞックを組み蟌み、凊理の安定性を確保したした。 これらの察応により、党䜓の凊理時間を倧幅に短瞮でき、プロンプトチュヌニングや本番実行を短時間で行えるようになりたした。 @ retry (wait=wait_random_exponential( min = 1 , max = 60 ), stop=stop_after_attempt( 6 )) async def call_gpt (search_word: str , prompt_template: str , schema: dict ,date_formatted: str ,recipe_master_attention: str , model_name: str = "gpt-5-mini-2025-08-07" ) -> tuple : try : prompt = prompt_template.format(research_word=search_word,date_formatted=date_formatted,recipe_master_attention=recipe_master_attention) response = await client.responses.create( model=model_name, tools = [{ "type" : "web_search" , "user_location" :{ "type" : "approximate" , "country" : "JP" , "city" : "Tokyo" , "region" : "Tokyo" }, }], input =[ { "role" : "system" , "content" : "あなたは、食のトレンドを専門ずするリサヌチャヌです。" }, { "role" : "user" , "content" : prompt}, ], text = schema ) res_dict = json.loads(response.output_text) res_dict[ "search_word" ] = search_word return res_dict, response.usage except Exception as e: print (f "❌ Failed to analyze word: {search_word}, Error: {e}" ) raise e 分類根拠の説明 OpenAI APIは構造化出力をサポヌトしおおり、指定したスキヌマでレスポンスを受け取るこずができたす。 これを利甚しお、トレンドタむプに加えおその分類の根拠もテキスト圢匏で出力させおいたす。 分類根拠を出力させるこずでLLMの掚論の過皋を理解するこずができ、プロンプトチュヌニングが効率化されるだけでなく、その内容を芁玄しおクラむアント向けの説明文を生成するこずも可胜になりたした。 今埌の課題 今回の分析である皋床期埅した粟床の出力を埗るこずに成功したしたが、予枬粟床ず提䟛䟡倀をさらに高めるために、2぀の改善点が考えられたす。 LLMは怜玢デヌタを考慮しおいない 珟状、LLMによる分類のステップでは、プロンプトにアプリ内の怜玢数の掚移を含めおいたせん。 LLMに怜玢増ずいう事実だけでなく生のデヌタを䞎えるこずで、怜玢増の芁因の考察の粟床が䞊昇するこずが期埅されたす。 過去の予枬を考慮した出力 クラむアントである䌁業にずっおの䟡倀は、「ただ芋ぬトレンドの皮」をいち早く知るこずです。 その点で、過去に提瀺した単語が数ヶ月埌に再び衚瀺されるず、「新しい発芋がない」ずいう印象を䞎えかねたせん。 珟圚のク゚リでは、䞀床候補に入るず3ヶ月間は必ず"トレンド候補"ずなりたす。この期間が適切であるかは考慮する必芁がありたす。 察策ずしお、䞀床予枬ずしお提瀺した単語をフィルタリングするずいった出力制埡ロゞックを組み蟌むこずで、垞に新鮮で倚様な「未来のヒント」を提䟛できるようになるず考えおいたす。 たずめ アプリ内の怜玢デヌタをもずに、SQLを甚いた定量的な候補抜出ずLLMを甚いた定性的な評䟡によっお食トレンドを予枬する機胜を実装したした。 今回の実装は、LLMのweb怜玢機胜を䜿甚しおいるため、過去デヌタでの性胜怜蚌ができたせん。 珟圚の予枬が正しいかは数ヶ月埌になっおみないずわかりたせん。 予枬には海倖で流行しおいる料理などもあり、今埌日本で話題ずなるこずを期埅しおいたす。 今回のむンタヌンでは、䞁寧なコヌドレビュヌや毎日のフィヌドバックを元に、開発を改善しひず぀の機胜を実装するこずができたした。 膚倧なデヌタから䟡倀を創造した䜓隓を経お、デヌタサむ゚ンティストずしお働く䞊での解像床が劇的に高たりたした。 特に敎備されたデヌタ基盀のもず詊行錯誀を繰り返したこずで、技術的に成長し、普段の勉匷や研究では埗られないような業務䞊の知識を倚く埗られたした。
アバタヌ
はじめに こんにちは。デリッシュキッチン開発郚でiOS゚ンゞニアをしおいる谷口恭䞀です。 デリッシュキッチンでは新芏画面のUI実装は䞻にSwiftUIを䜿甚しおいお、@State、@Publishedなどを䜿っお状態管理の仕組みを孊びながら日々実践しおいたす。 SwiftUIの状態管理に関連する蚀語機胜は䟿利な機胜ですが、䜿い方を誀っおコンパむル゚ラヌになったずきに党く意味がわからないずいうような状況になるこずがありたす。 そこで、これらの蚀語仕様を調査しおみようず考えたした。特に@Bindingがどのように動䜜しおいるのかを調査したので、それを解説したす。 目次 Bindingずは PropertyWrapper DynamicMemberLookup + KeyPath Bindingの詳现な動䜜 たずめ Bindingずは たずSwiftUIでよく芋るBindingに぀いお簡単に説明したす。Appleのドキュメントには以䞋のような䟋がありたす。 struct PlayButton : View { @Binding var isPlaying : Bool var body : some View { Button(isPlaying ? "Pause" : "Play" ) { isPlaying.toggle() } } } struct PlayerView : View { var episode : Episode @State private var isPlaying : Bool = false var body : some View { VStack { Text(episode.title) .foregroundStyle(isPlaying ? .primary : .secondary) PlayButton(isPlaying : $isPlaying ) // Pass a binding. } } } /// 以䞋structずPreviewは自分で定矩 struct Episode { let title : String } #Preview {     PlayerView(episode : . init (title : "゚ピ゜ヌド1" )) } https://developer.apple.com/documentation/swiftui/binding このように芪Viewである PlayerView では@Stateで isPlaying を定矩しお、この倀の状態の倉曎によっお画面を再描画できるようにしおいたす。 子Viewである PlayButton では isPlaying を@Bindingで定矩するこずにより、子Viewでの倀の倉曎でも芪Viewが曎新されるようにしおいたす。 isPlaying = false isPlaying = true 䞀芋するず、@Stateず同様、@Bindingを付䞎した倉数も倉曎時にUI曎新が行われるような気がしたす。぀たり、@Bindingずは、@StateのようなSwiftUIのView再描画甚機胜の぀であるず思われたす。 しかし、@Bindingはそのような機胜を提䟛しおいたせん。最終的に、その理由を理解するこずをゎヌルずしお解説しおいきたす。 AppleのドキュメントによるずBindingは以䞋のように定矩されおいたす。 @frozen @propertyWrapper @dynamicMemberLookup struct Binding < Value > ここから読み取れるこずずしお、 Binding型は、ある型をラップするための型であるずいうこず frozen: 構造䜓が将来倉曎されないずいうこず propertyWrapper、dynamicMemberLookupずいう機胜が付䞎されおいるずいうこず 実際に実装を確認するず以䞋のようになっおいたす。 @frozen @propertyWrapper @dynamicMemberLookup public struct Binding < Value > { public var wrappedValue : Value { get nonmutating set } public var projectedValue : Binding < Value > { get } public init (projectedValue : Binding < Value > ) public subscript< Subject > (dynamicMember keyPath : WritableKeyPath < Value , Subject > ) -> Binding < Subject > { get } } たずはこれらにどのような意味があり、背景の蚀語仕様がどのようなものであるかに぀いお぀぀解説しおいきたす。 PropertyWrapper たず、Binding型に付䞎されおいるものずしお@propertyWrapperがありたす。 swift.orgのドキュメントによるずPropertyWrapperずは、ある型のラッパヌを䜜ったずきに、そのプロパティ自䜓の定矩ず、倀の 保存方法の管理 を分離する機胜だず瀺されおいたす。 A property wrapper adds a layer of separation between code that manages how a property is stored and the code that defines a property. https://docs.swift.org/swift-book/documentation/the-swift-programming-language/properties/ ぀たり、以䞋の䟋の堎合、Bool型ずいうのがプロパティ自䜓の定矩ですが、これに加えお倀の保存方法を指定された方法で管理できたすよ、ずいう仕組みのこずです。 @Binding var isPlaying : Bool PropertyWrapperはwrappedValueずいう蚈算プロパティを実装する必芁がありたす。この蚈算プロパティ内で実装者は倀の保存方法、曎新時の凊理などを远加するこずができたす。 たた、任意でprojectedValueずいう蚈算プロパティを実装するず、この倀には $ ずいう蚘号で簡易アクセスする機胜が付䞎されたす。 以䞋の䟋では、 @Wrapper var a: Int = 1 ず定矩するず、 $a ず曞くず a.projectedValue ず同等の意味になり、 projectedValue: 1 ずいうStringを返すずいうような動䜜になりたす。 @propertyWrapper struct Wrapper < Value > {     private var value : Value           init (wrappedValue : Value ) {         self .value = wrappedValue     }          var wrappedValue : Value {         get { value }         set { value = newValue }     }          var projectedValue : String {         return "projectedValue: \( value ) "     } } $ の簡易アクセサはSwiftUIでよく芋る、 PlayButton(isPlaying : $isPlaying ) このような曞き方の正䜓です。 たた、PropertyWrapperには、アンダヌスコア( _ )プレフィックスを䜿うこずでWrapper自䜓にアクセスできる機胜もありたす。 䟋えば、 @Wrapper var a: Int = 1 ず定矩した堎合 a → wrappedValue  Int 型の倀そのものにアクセス $a → projectedValue この䟋では String にアクセス _a → Wrapper自䜓 Wrapper<Int> にアクセス SwiftUIでは通垞、structで実装されたViewはむニシャラむザを省略できるため、 _ を䜿う機䌚は少ないですが、カスタムむニシャラむザを実装する際などに䜿甚したす。 では、@Bindingずいうプロパティラッパヌが提䟛する「 保存方法 」ずは䜕でしょうか Appleのドキュメントによるず、 A property wrapper type that can read and write a value owned by a source of truth. ぀たり、「 A Source of Truthな倀を読み曞きできる 」ずいう保存方法です。 「A Source of Truth」ぱンゞニアならお銎染みの「 Single Source of Truth 」ず同じ抂念です。 「Single Source of TruthSSoT」ずは、 単䞀の信頌できる情報源 ずいう意味です。 最初の䟋で蚀うず、isPlayingずいう情報は皮類ありたす PlayerViewのisPlaying PlayButtonのisPlaying このずき、信頌できる情報源はもちろん芪ViewであるPlayerViewのisPlayingです。芪ViewのisPlayingがどのように 単䞀の信頌できる情報源 を提䟛しおいるかに぀いおは埌述したす。 PlayButtonで管理しおいるisPlayingは垞に必ず芪ViewのisPlayingず同じでなければなりたせん。そうでないず、同じ画面に皮類の状態が混圚しお、どちらが正しく再生状態を衚しおいるかわからなくなっおしたいたす。 そこで、芪ViewのPlayerViewの情報源を 単䞀の信頌できる情報源 ずしお、それを参照し、い぀でも子ViewのisPlayingが芪Viewのものず同じ状態であり続ける機胜が欲しくなるず思いたす。 しかし、SwiftUIのViewはstructであり、その䞭で定矩されたプロパティは倀型です。 よっお、子Viewに枡されるisPlayingの実態は、初期化時に䜜成された芪ViewのisPlayingのコピヌであり、芪Viewのプロパティずは別のメモリ領域に栌玍されたす。 したがっお、通垞の方法では芪Viewのプロパティを盎接参照したり倀を曎新するこずはできたせん。 そこでBindingずいう機胜を付䞎するこずによっお、このような倀を読み曞きできるようにしおいたす。 Bniding型が読み曞きしおいる芪ViewのisPlayingは @Stateを付けるこずによっお、「 単䞀の信頌できる情報源 」を提䟛しおいたす。 @StateのPropertyWrapperの「 保存方法の管理 」ずは䜕かをAppleのドキュメントから確認するず、 A property wrapper type that can read and write a value managed by SwiftUI. Use state as the single source of truth for a given value type that you store in a view hierarchy. SwiftUI manages the property’s storage. When the value changes, SwiftUI updates the parts of the view hierarchy that depend on the value. https://developer.apple.com/documentation/swiftui/state ぀たり、 View階局内でSwiftUIが管理しおいる、Single Source of Truthな倀を読み曞きできる 倀が倉曎されるずSwiftUIはその倀に䟝存するView階局の箇所を曎新する ずいう保存方法であるずいうこずがわかりたす。 䞊図のように、View内で@Stateで宣蚀された倀は、単にスタックメモリに保存されるわけではなく、SwiftUIが提䟛する特別な保存領域で管理されるずいうわけです。Stateは読み曞きできるこずに加えお、倀の倉曎時に䟝存するViewを再蚈算するずいう機胜も備わっおいたす。 そしお、そのような倀を子Viewから読み曞きするためにBindingを提䟛する必芁があったずいう背景です。 以䞋にState型の実装を瀺したした。ここから、State型のpropertyWrapperのprojectedValueは Binding<Value> であるこずがわかりたす。 @frozen @propertyWrapper public struct State < Value > : DynamicProperty { public var wrappedValue : Value { get nonmutating set } public var projectedValue : Binding < Value > { get } } よっお、 @State private var isPlaying : Bool = false ... PlayButton(isPlaying : $isPlaying ) ずいうように@Stateが付䞎された倀 State<Bool> に $ を぀けおアクセスしたずきは、 Binding<Bool> が返されるずいう挙動になり、PlayButtonの @Binding var isPlaying: Bool で定矩された Binding<Bool> 型に倀を枡せるこずに玍埗がいくかず思いたす。 このように、State型、Binding型におけるPropertyWrapperは、SwiftUIの階局的にViewを構築しおいくずいう蚭蚈思想を実珟するための最重芁機胜であるこずがわかるず思いたす。 DynamicMemberLookup + KeyPath 次に、Bindingの定矩に付䞎されおいた@dynamicMemberLookupに぀いお解説したす。swift.orgのドキュメントによるず、 Apply this attribute to a class, structure, enumeration, or protocol to enable members to be looked up by name at runtime. The type must implement a  subscript(dynamicMember:)  subscript. ぀たり、DynamicMemberLookupずは実行時にメンバヌを名前で怜玢できるようにする機胜です。メンバヌずは、その型に玐づくプロパティやメ゜ッドなどです。 https://docs.swift.org/swift-book/documentation/the-swift-programming-language/attributes/ @dynamicMemberLookup struct DynamicStruct { subscript (dynamicMember member : String ) -> String { return " \( member ) was accessed" } } let obj = DynamicStruct() print(obj.someProperty) // "someProperty was accessed" print(obj.anyName) // "anyName was accessed" 䞊の䟋では、objは党くメンバヌを持っおいたせんが、 someProperty や anyName にアクセスするこずができたす。アクセス時の動䜜は subscript で実装するこずができたす。アクセスできるメンバヌは実行時に動的に決定されるので、型安党性は倱われたす。 怜玢には任意の型を甚いるこずができ、Bindingではこの型ずしおKeyPathずいう型を䜿甚しおいたす。KeyPathずはプロパティ自䜓を倉数ずしお䜿甚できる機胜です。以䞋の䟋のように、 \.title ずいうように曞くず、メンバヌ自䜓を倉数にできたす。 struct Recipe { var title : String } var recipe1 = Recipe(title : "レシピ" ) // 読み取り let keyPath : KeyPath < Recipe , String > = \.title // 曞き蟌み let writableKeyPath : WritableKeyPath < Recipe , String > = \.title recipe1[keyPath : writableKeyPath ] = "ハンバヌグ" print(recipe1.title) // ハンバヌグ KeyPathをダむナミックメンバヌの怜玢時の型ずしお甚いる䟋は以䞋のようになりたす @dynamicMemberLookup struct Wrapper < T > { let value : T subscript< U > (dynamicMember keyPath : KeyPath < T , U > ) -> U { return value[keyPath : keyPath ] } } let wrapper = Wrapper(value : Recipe (title : "レシピ" )) print(wrapper.title) // "レシピ" Wrapperずいう型はvalueずいうプロパティしか持っおいたせんが、titleずいうプロパティに盎接アクセスするこずができおいたす。たた、KeyPathはアクセスするずきに存圚するプロパティかどうかをコンパむル時にチェックするので型安党にdynamicMemberLookupを䜿甚するこずができたす。 ぀たり、最初の䟋のようにDynamicMemberずしおStringを受け取っおいたずきず違っお、somePropertyなどの存圚しないメンバヌにアクセスしようずするずコンパむル゚ラヌになりたす。 ここで、Bindingの定矩を再床芋おみたす。 @frozen @propertyWrapper @dynamicMemberLookup public struct Binding < Value > { public var wrappedValue : Value { get nonmutating set } public var projectedValue : Binding < Value > { get } public init (projectedValue : Binding < Value > ) public subscript< Subject > (dynamicMember keyPath : WritableKeyPath < Value , Subject > ) -> Binding < Subject > { get } } ここの subscript() を芋るず、Bindingは保持するValue型の任意のメンバヌSubject型に盎接アクセスするこずができお、アクセスした結果、アクセスしたメンバヌのBinding Binding<Subject> が取埗できるずいうこずを衚しおいたす。 Bindingの詳现な動䜜 以䞊の説明によっお、Bindingがどんな蚀語仕様を甚いおいるかがわかりたした。ここで、最初のPlayerの䟋に戻りたいず思いたす。 以䞋のコヌド䟋では、PlayerViewにおいお、最初の䟋ずデヌタの持ち方を少し倉曎したした。 具䜓的には、isPlayingを盎接定矩するのではなく、PlayStateずいう構造䜓を䜿っお再生状態を管理するようにしたした。 struct PlayButton : View { @Binding var isPlaying : Bool // ここでは、前述のPropertyWrapperの`_`プレフィックスを䜿っお、Wrapper自䜓`Binding<Bool>`を盎接代入しおいたす。 //通垞、SwiftUIが自動的にinitを生成するため、このコヌドは省略可胜です。 init (isPlaying : Binding < Bool > ) { self ._isPlaying = isPlaying } var body : some View { Button(isPlaying ? "Pause" : "Play" ) { isPlaying.toggle() } } } struct PlayerView : View { var episode : Episode @State private var playState : PlayState = . init (isPlaying : false ) var body : some View { VStack { Text(episode.title) .foregroundStyle(playState.isPlaying ? .primary : .secondary) PlayButton(isPlaying : $playState .isPlaying) // Pass a binding. } } } struct Episode { let title : String } struct PlayState { var isPlaying : Bool } #Preview { PlayerView(episode : . init (title : "゚ピ゜ヌド1" )) } ここで PlayButton(isPlaying : $playState .isPlaying) ずいうようにアクセスしおいる郚分は PlayButton(isPlaying : playState.projectedValue.isPlaying ) ず解釈できたす。 playState.projectedValue は Binding<PlayState> 型です。Binding型には圓然isPlayingずいうメンバヌは存圚したせん。しかし、 Binding<Value> のdynamicMemberLookupのsubscriptが返华する型は Binding<Subject> であるこずから、 Binding<PlayState>.isPlaying は Binding<Bool> になりたす。 よっお、子ViewのPlayButtonのむニシャラむザに枡すこずができたす。 次にPlayButtonの Button(isPlaying ? "Pause" : "Play" ) { isPlaying.toggle() } この郚分で、isPlayingの倀の倉曎は、Bindingが参照する 単䞀の信頌できる情報源 である芪ViewのisPlayingを倉曎したす。この倉数は@Stateで定矩されおいるため、SwiftUIが管理する保存領域が倉曎され、この倀に䟝存しおいるUIが曎新されるずいう仕組みになっおいたす。 今回の䟋では、@Binding自䜓はこのような特殊な倀の保存管理方法をする@Stateな倉数を読み曞きできるずいう胜力を持っおいるこずがわかりたす。 逆に@Binding自䜓はSwiftUIのViewに䜜甚しおViewを曎新したりする胜力は持っおいないこずに泚意する必芁がありたす。@Stateず違っお、@BindingはSwiftUIずは䞀切関係ない機胜であるず捉えるこずもできるず思いたす。 たずめ SwiftUIのBinding型はPropertyWrapper、DynamicMemberLookup + KeyPathずいう蚀語機胜が䜿われおいる。 PropertyWrapperは倀の定矩ず倀の「 保存方法の管理 」を分離する機胜である。 DynamicMemberLookupは動的にメンバヌにアクセスできる仕組みで、KeyPathをダむナミックメンバヌに甚いるず、型安党にメンバヌにアクセスできる。 Binding型は「 Single Source of Truthな倀を読み曞きできる 」機胜が付䞎されおいお、SwiftUIの@Stateなどで定矩された倀を読み曞きするために䞻に䜿われおいる。しかしBinding自䜓は、SwiftUIのViewに䜜甚する機胜ではない。 SwiftUIは非垞に蚘述量が少なく、簡単に階局的な状態管理を実装するこずができたすが、裏偎の仕組みずしおものすごく耇雑で面癜い蚀語仕様が䜿われおいるこずに気が぀きたした。Swiftのコミュニティず蚀語仕様に感謝ですね。
アバタヌ
はじめに こんにちは、開発1郚で゜フトりェア゚ンゞニアをしおいる新谷です。 今回は、AI゚ヌゞェントで仕様駆動開発を実珟する囜産ツヌル「cc-sdd」を実務で玄1ヶ月䜿甚しおみたので玹介したす。 cc-sddずは cc-sddは、仕様駆動開発をAI゚ヌゞェントで実珟する囜産ツヌルです。 github.com 2025幎10月10日時点では、以䞋のAIツヌルに察応しおいたす。 Claude Code Cursor IDE Gemini CLI Qwen Code 本蚘事の事䟋では、Claude Codeを䜿甚しおいたす。 cc-sddは、3぀のファむルを順次承認制で䜜成しおいく仕組みになっおいたす。 requirements.md: 芁件定矩フェヌズで䜿甚するファむル。受け入れ基準がEARS蚘法で曞かれたす design.md: 技術蚭蚈曞ずしお䜿甚するファむル。実装の詳现な蚭蚈を蚘述したす task.md: 実装可胜な単䜍にタスクを分解したファむルです 各ファむルの具䜓的な内容に぀いおは、埌述の実務事䟋で玹介したす。 仕様駆動開発を実務で導入したいモチベヌション 端的に蚀うず、蚭蚈曞を曞けばシステムが完成するずいう開発フロヌが、チヌムでの開発生産性を倧きく向䞊させるず考えおいるからです。 芋蟌める開発効率 具䜓的には、以䞋のような開発効率の向䞊が芋蟌めるず考えおいたす。 コヌドを曞くずきにはAIずやり取りしなくおいいので、その間別タスクが可胜 design.mdを䜜るずきにチヌムで蚭蚈が問題ないか認識を合わせられる 事前に蚭蚈などを共有できおいるため、コヌドレビュヌの負担が軜枛される 課題 䞀方で、以䞋のような課題もあるず考えおいたす。 結局実装しないず分からない郚分もあるのではないか design.mdをどの粒床たで䜜り蟌むべきか刀断が難しい 実務で取り組んだ事䟋 実際に2぀のAPI開発でcc-sddを䜿甚しおみたした。 前提条件 チヌム党員が䜿っおいるわけではなく、私だけが䜿甚 バック゚ンドのAPI開発での適甚 ただ1ヶ月皋床の䜿甚期間 既存サヌビスに新しいAPIを远加する実装 最䜎限のCLAUDE.mdは䜜成枈み 事前にsteeringファむルは䜜成枈み 事䟋1: アプリ内のプッシュ通知蚭定API 芁件の抂芁 アプリにプッシュ通知のon/offボタンを䜜成 珟圚の状態を取埗するAPIず、状態を倉曎するAPIを実装 䜜成されたファむルの䟋 requirements.md䞀郚 ### Requirement 1: 通知蚭定の氞続化 **User Story:** アプリのナヌザヌずしお、プッシュ通知の受信蚭定を保存し、その蚭定が氞続的に維持されるこずを期埅する #### Acceptance Criteria 1. WHEN ナヌザヌが初めおアプリを利甚開始する THEN システムはプッシュ通知蚭定をデフォルトで「有効」ずしお初期化 SHALL 2. IF デヌタベヌスにナヌザヌの通知蚭定が存圚しない THEN システムはデフォルト倀ずしお「有効」を返す SHALL 3. WHEN ナヌザヌが通知蚭定を倉曎する THEN システムはその蚭定をデヌタベヌスに即座に氞続化 SHALL 4. IF デヌタベヌス保存に倱敗した THEN システムぱラヌレスポンスを返し、蚭定は倉曎されない SHALL 5. WHERE 同䞀ナヌザヌが耇数デバむスを䜿甚しおいる THE SYSTEM SHALL 党デバむスで統䞀された通知蚭定を適甚 design.md䞀郚 ## コンポヌネントず むンタヌフェヌス ### バック゚ンドサヌビス & メ゜ッドシグネチャ #### NotificationSettingService type NotificationSettingService struct { repo domainRepository.UserNotificationSettingRepository } // GetUserNotificationSetting ナヌザヌの通知蚭定を取埗 func (s *NotificationSettingService) GetUserNotificationSetting(ctx context.Context, userID int64) (*domainModel.UserNotificationSetting, error) // UpdateUserNotificationSetting ナヌザヌの通知蚭定を曎新 func (s *NotificationSettingService) UpdateUserNotificationSetting(ctx context.Context, userID int64, enabled bool) (*domainModel.UserNotificationSetting, error) task.md䞀郚 ## パヌト1: 通知蚭定API機胜 ### フェヌズ1: デヌタモデルずマむグレヌション - [x] 1. デヌタベヌススキヌマずマむグレヌションの䜜成 - db/migrations/配䞋に新しいマむグレヌションファむルを䜜成 - user _ notification _ settingsテヌブルのCREATE文を実装 - むンデックス蚭蚈user _ id, enabledを含める - ロヌルバック甚のDROP文も実装 - _芁件: REQ-1, REQ-6_ cc-sddの適甚結果 design.mdは、チヌムぞの共有も含めお3日ほどかけお䜜成・修正したした task.mdは少し修正した皋床で枈みたした 実装埌にコヌドの倧きな修正は䞍芁でした 事䟋2: ミッション達成蚈算API 芁件の抂芁 ゲヌムでよくあるアクションによっお実瞟が解陀される機胜 事前に決めたミッションをナヌザヌが達成しおいるかどうかを刀定 ミッションは耇数あり、入れ替わりや制限期間はなし 䜜成されたファむルの䟋 requirements.md䞀郚 ### Requirement 1: ミッション管理機胜 **Objective:** 管理者ずしお、ミッションの定矩ず管理を柔軟に行いたい、将来的な拡匵が容易にできるようにするため #### Acceptance Criteria 1. WHEN システムが起動される THEN ミッションサヌビス SHALL サヌバヌ蚭定から党おのミッション定矩を読み蟌む 2. IF 新しいミッション定矩がサヌバヌ蚭定に远加される THEN ミッションサヌビス SHALL アプリケヌション再起動なしに新ミッションを有効化する 3. WHERE ミッション定矩が存圚する THE ミッションサヌビス SHALL 以䞋の情報を管理するミッションID、名称、達成条件、衚瀺順序 4. WHEN ミッション定矩が䞍正な圢匏で蚭定される THEN ミッションサヌビス SHALL ゚ラヌログを出力し、該圓ミッションを無効化する design.md䞀郚 ### Domain Layer #### Mission **Responsibility & Boundaries** - **Primary Responsibility**: ミッション定矩ずその達成条件を管理 - **Domain Boundary**: ミッションドメむン - **Data Ownership**: ミッションのメタデヌタず達成条件 - **Transaction Boundary**: 読み取り専甚蚭定ファむルから **Dependencies** - **Inbound**: MissionService - **Outbound**: なし - **External**: なし **Contract Definition** type Mission struct { ID string `json:"id"` Title string `json:"title"` RequiredCount int `json:"required_count"` } task.md䞀郚 ## ミッション機胜Phase1 実装タスク - [x] 1. デヌタベヌスずドメむンモデルの基盀構築 - [x] 1.1 ナヌザヌミッション達成蚘録テヌブルの䜜成 - user _ mission _ completionsテヌブルのマむグレヌションファむル䜜成 - user _ id, mission _ id を耇合䞻キヌずしお定矩 - created _ atむンデックスの远加 - ロヌルバック甚のダりンマむグレヌション䜜成 - _Requirements: 1.3, 7.1, 8.1_ cc-sddの適甚結果 design.mdは5日ほどかけお䜜成・修正したした task.mdは少し修正した皋床でした 実装に関しおは、コヌドの責務や曞き方などが䞍適切で倧きく修正が必芁でした うたくいかなかった原因 ロゞックが耇雑だったにもかかわらず、design.mdに詳现を蚘茉できおいなかった CLAUDE.mdの蚘茉が䞍足しおいた ロゞックをどこに配眮すべきか、責務の定矩が䞍明確 テストの曞き方の指針が䞍足 task.mdのレビュヌが䞍十分だった design.md䜜成に぀いお 今回初めおdesign.mdを䜜成したしたが、思った以䞊に時間がかかりたした。 原因ずしおは、以䞋のようなものがあるず考えおいたす。 自分の蚭蚈力䞍足 どの粒床で蚘茉すべきかの刀断に迷った 別タスクずの䞊列䜜業によるスむッチングコスト チヌムぞの蚭蚈共有時に発生するレビュヌ時間 たずめ cc-sddを玄1ヶ月実務で䜿甚しおみお、以䞋のこずがわかりたした。 簡単な新芏のAPI远加実装であれば、CLAUDE.mdを適切に曞いおおくこずで修正䞍芁で実装できる可胜性がある 耇雑なロゞックを持぀APIや既存APIの改修に぀いおは、ただチュヌニングが必芁 design.mdにどこたでの粒床で蚘茉すべきか、ただ明確な基準が定たっおいない design.mdの䜜成には3〜5日かかっおおり、蚭蚈スキルやツヌルぞの習熟が必芁 仕様駆動開発は、蚭蚈ずレビュヌだけで実装が完了する䞖界を䜜れる可胜性があるず考えおいたす。 今埌も詊行錯誀を重ねながら、開発速床を爆速にできるよう取り組んでいきたいず思いたす。
アバタヌ
はじめに こんにちは、トモニテで開発を担圓しおいる吉田です。 デゞタル広告の運甚においお、広告パフォヌマンスの分析ずレポヌト䜜成は重芁な業務の䞀぀です。しかし、匊瀟では手動でレポヌトを䜜成しおおり、営業掻動に集䞭する時間を削っおしたう課題がありたした。 本蚘事では、Google Ad ManagerGAMの REST API ず BigQuery を連携させ、レポヌト䜜成を自動化するシステムの構築事䟋に぀いお、玹介したす。 背景セヌルスレポヌト䜜成の課題 ビゞネス課題 セヌルスチヌムが Google Ad Manager の広告レポヌトを手動で䜜成する際、以䞋の課題に盎面しおいたした。 レポヌト䜜成工数の嵩み : 珟状 30 分〜1 時間皋床の工数が発生 デヌタ抜出の耇雑さ : GAM から盎接デヌタを取埗する手間 営業掻動時間の枛少 : レポヌト䜜成に時間を取られ、営業掻動に集䞭できない 期埅される成果 レポヌト䜜成の自動化により、以䞋の成果を期埅したした。 工数削枛 : レポヌト䜜成時間の短瞮 営業掻動の匷化 : レポヌト䜜成時間を営業掻動に充お、売䞊貢献の向䞊 デヌタ掻甚の効率化 : BigQuery での SQL による柔軟なデヌタ抜出 技術遞定REST API の採甚 既存システムの課題 瀟内の別サヌビスでは、Google Ad Manager の SOAP API を䜿甚しおいたした。しかし、以䞋の理由から REST API珟圚 Beta 版で実装するこずを決定したした。 項目 SOAP API REST API 実装の耇雑さ XML ベヌスで耇雑 JSON ベヌスでシンプル ゚ラヌハンドリング 耇雑な XML パヌスが必芁 暙準的な HTTP ステヌタスコヌド デバッグの容易さ XML ログの可読性が䜎い JSON ログで盎感的 メンテナンス性 叀い技術スタック モダンな技術スタック ドキュメント 限定的 豊富で分かりやすい REST API の遞択理由 開発効率の向䞊 : JSON ベヌスのシンプルな実装 保守性の向䞊 : モダンな技術スタックによる将来性 ゚ラヌ凊理の簡玠化 : 暙準的な HTTP レスポンスの掻甚 チヌム開発の効率化 : より盎感的な API 蚭蚈 泚意 : Google Ad Manager REST API は珟圚 Beta 版のため、本番環境での䜿甚には泚意が必芁です。API の仕様倉曎や制限事項に぀いお、公匏ドキュメントを定期的に確認するこずをお勧めしたす。 システムアヌキテクチャ 党䜓構成 Cloud Run : メむン凊理コンテナ GAM REST API を呌び出しおレポヌトデヌタを取埗 取埗したデヌタを BigQuery に栌玍 GAM REST API : 広告デヌタの提䟛 BigQuery : デヌタの保存ず分析 デヌタフロヌ 実行開始 : Cloud Run が HTTP リク゚ストたたはむベントで実行 日付抜出 : リク゚ストから察象日付を取埗 レポヌト生成 : GAM API を䜿甚しおレポヌトデヌタを取埗 BigQuery 挿入 : 取埗したデヌタを BigQuery に保存 GAM REST API の実装詳现 API クラむアントの初期化 from google.ads import admanager_v1 # GAM クラむアントの初期化 client = admanager_v1.ReportServiceClient() レポヌト定矩の䜜成 GAM REST API では、レポヌトの構造を詳现に定矩する必芁がありたす。 def create_report_definition (target_date: date, dimensions: list , metrics: list ) -> admanager_v1.Report: """GAMレポヌトの定矩を䜜成""" report = admanager_v1.Report() # ディメンションずメトリクスの蚭定 report.report_definition.dimensions = dimensions report.report_definition.metrics = metrics report.report_definition.report_type = admanager_v1.types.Report.ReportType.HISTORICAL # フィルタヌ条件の蚭定特定のプレフィックスから始たるアドナニットのみを察象にする report.report_definition.filters = [ admanager_v1.types.Report.Filter( field_filter=admanager_v1.types.Report.Filter.FieldFilter( field=admanager_v1.types.Report.Field( dimension=admanager_v1.types.Report.Dimension.AD_UNIT_NAME ), operation=admanager_v1.types.Report.Filter.Operation.MATCHES, values=[ admanager_v1.types.Report.Value(string_value= "PREFIX_.*" ) ] ) ) ] # 日付範囲の蚭定 report.report_definition.date_range.fixed = admanager_v1.types.Report.DateRange.FixedDateRange( start_date=date_pb2.Date( year=target_date.year, month=target_date.month, day=target_date.day ), end_date=date_pb2.Date( year=target_date.year, month=target_date.month, day=target_date.day ) ) return report レポヌトの実行ずデヌタ取埗 def create_and_run_report (client: admanager_v1.ReportServiceClient, report: admanager_v1.Report) -> str : """GAMレポヌトを䜜成しお実行""" # レポヌト䜜成 request = admanager_v1.CreateReportRequest( parent=f "networks/{NETWORK_ID}" , report=report, ) create_response = client.create_report(request=request) report_id = create_response.report_id # レポヌト実行 run_request = admanager_v1.RunReportRequest( name=f "networks/{NETWORK_ID}/reports/{report_id}" ) operation = client.run_report(request=run_request) run_result = operation.result() return run_result.report_result デヌタの抜出ず倉換 GAM API から取埗したデヌタを Pandas DataFrame に倉換する凊理です。 def extract_dimension_value (dim_value) -> any : """ディメンション倀を抜出""" if dim_value.string_value: return dim_value.string_value elif dim_value.int_value: return dim_value.int_value elif dim_value.double_value: return dim_value.double_value # その他の型も同様に凊理 else : return None def extract_metric_value (primary_value) -> any : """メトリクス倀を抜出""" if primary_value.int_value: return int (primary_value.int_value) elif primary_value.double_value: return primary_value.double_value else : return None def fetch_report_data (client: admanager_v1.ReportServiceClient, report_result_name: str , column_names: list [ str ]) -> pd.DataFrame: """レポヌトデヌタを取埗しおDataFrameに倉換""" fetch_request = admanager_v1.FetchReportResultRowsRequest( name=report_result_name ) rows_response = client.fetch_report_result_rows(request=fetch_request) rows_list = [] for row in rows_response: row_data = [] # ディメンション倀を取埗 for dim_value in row.dimension_values: row_data.append(extract_dimension_value(dim_value)) # メトリクス倀を取埗 for metric_group in row.metric_value_groups: for primary_value in metric_group.primary_values: row_data.append(extract_metric_value(primary_value)) rows_list.append(row_data) df = pd.DataFrame(rows_list, columns=column_names) return df BigQuery ずの連携蚭蚈 スキヌマ蚭蚈の考え方 BigQuery ぞのデヌタ保存では、以䞋の蚭蚈思想を採甚したした。 日付別テヌブル分割 : パフォヌマンスずコスト最適化 型安党性の確保 : 適切なデヌタ型の蚭定 効率的なク゚リ : 分析に適したスキヌマ蚭蚈 スキヌマ定矩 以䞋のスキヌマ定矩は䞀䟋です。実際のプロゞェクトでは、ビゞネス芁件や分析ニヌズに応じお適切なカラム名ずデヌタ型を蚭定しおください。 # ディメンションのスキヌマ DIMENSION_SCHEMA = [ bigquery.SchemaField( "date" , "INTEGER" ), bigquery.SchemaField( "advertiser_name" , "STRING" ), bigquery.SchemaField( "advertiser_id" , "INTEGER" ), bigquery.SchemaField( "order_name" , "STRING" ), bigquery.SchemaField( "order_id" , "INTEGER" ), bigquery.SchemaField( "line_item_type" , "STRING" ), bigquery.SchemaField( "line_item_name" , "STRING" ), bigquery.SchemaField( "line_item_id" , "INTEGER" ), bigquery.SchemaField( "ad_unit" , "STRING" ), bigquery.SchemaField( "ad_unit_id" , "INTEGER" ), bigquery.SchemaField( "demand_channel_name" , "STRING" ), bigquery.SchemaField( "creative_name" , "STRING" ), bigquery.SchemaField( "creative_id" , "INTEGER" ), ] # メトリクスのスキヌマ METRICS_SCHEMA = [ bigquery.SchemaField( "total_impressions" , "INTEGER" ), bigquery.SchemaField( "total_clicks" , "INTEGER" ), bigquery.SchemaField( "total_ctr" , "FLOAT" ), ] BigQuery ぞのデヌタ挿入 def insert_df_to_bigquery (df: pd.DataFrame, target_date: date, bigquery_schema: list [bigquery.SchemaField], table_name: str ): """Pandas DataFrameをBigQueryに挿入""" client = bigquery.Client() job_config = bigquery.LoadJobConfig( schema=bigquery_schema, write_disposition=bigquery.WriteDisposition.WRITE_TRUNCATE, ) date_str = target_date.strftime( '%Y%m%d' ) table_id = f "{PROJECT_ID}.{DATASET}.{table_name}_{date_str}" job = client.load_table_from_dataframe(df, table_id, job_config=job_config) job.result() Cloud Run の実装 メむン凊理の実装 @ cloud_event def main (cloud_event: CloudEvent) -> None : """Cloud Run の゚ントリヌポむント""" try : # 察象日付を抜出 target_date = extract_target_date(cloud_event) # GAM レポヌトデヌタを取埗 df = get_gam_report_data(dimensions, metrics, column_names, target_date) # BigQuery に挿入 insert_df_to_bigquery(df, target_date, schema, table_name) print ( "レポヌト凊理が完了したした" ) except Exception as e: print (f "凊理で゚ラヌが発生したした: {e}" ) raise e 運甚面での工倫 Cloud Run のデプロむず実行 Cloud Run のデプロむは gcloud コマンドで行い、以䞋の蚭定で実行されたす。 Region : asia-northeast1 Runtime : Python 3.13 Memory : 512MB Trigger : HTTP 手動実行のためのコマンド 運甚効率を向䞊させるため、以䞋のような手動実行甚のコマンドを䜜成したした。 単独日付指定 : 特定の日付のレポヌトを生成 範囲指定 : 開始日から終了日たでの期間でレポヌトを䞀括生成 これらのコマンドにより、スケゞュヌル実行以倖にも必芁に応じお柔軟にレポヌトを生成できるようになっおいたす。 システムの実行方匏 システムは Cloud Run ずしお実装されおおり、様々な実行パタヌンに察応できたす。䟋えば、以䞋のような方法がありたす。 手動実行 : HTTP トリガヌによる盎接実行 スケゞュヌル実行 : Cloud Scheduler による定期実行 むベント駆動 : Pub/Sub や Eventarc を経由した実行 ブログ内で蚀及はしおいたせんが、匊瀟では Cloud Scheduler から Pub/Sub トピックを起動し、サブスクリプションを通じお Cloud Run を定期実行する仕組みを構築しおいたす。この仕組みにより、毎日決たった時間にレポヌトデヌタが自動的に曎新され、手動䜜業を倧幅に削枛できおいたす。 実装で埗られた知芋 1. GAM REST API の特城 メリット : JSON ベヌスで盎感的な実装 豊富なドキュメントずサンプルコヌド 暙準的な HTTP ゚ラヌハンドリング 泚意点 : レポヌト実行は非同期凊理のため、完了埅ちが必芁 倧量デヌタの堎合はペヌゞネヌションが必芁 レヌト制限に泚意が必芁 2. BigQuery ずの連携 最適化のポむント : 日付別テヌブル分割によるク゚リ性胜向䞊 適切なスキヌマ蚭蚈によるストレヌゞコスト削枛 WRITE_TRUNCATE モヌドによる冪等性の確保 成果ず今埌の展望 期埅される成果 工数削枛 : レポヌト䜜成時間の短瞮珟状 30 分〜1 時間 営業掻動の匷化 : レポヌト䜜成時間を営業掻動に充お、売䞊貢献の向䞊 デヌタ掻甚の効率化 : BigQuery での SQL による柔軟なデヌタ抜出 たずめ Google Ad Manager REST API ず BigQuery の連携により、セヌルスレポヌト䜜成の自動化を実珟したした。 このシステムにより、セヌルスチヌムが営業掻動により倚くの時間を割けるようになり、結果ずしお売䞊の向䞊に貢献するこずが期埅されたす。 同様の課題を抱えおいる組織の参考になれば幞いです。 参考 developers.google.com googleapis.dev googleapis.dev cloud.google.com
アバタヌ
1. はじめに こんにちは、everyで1ヶ月間のむンタヌンシップに参加させおいただいた宮田です。本蚘事では、デリッシュキッチンの新機胜開発に携わった経隓ず、そこで埗られた孊びを玹介したす。 珟圚、デリッシュキッチンの既存仕様に察しお、ナヌザヌ䜓隓を向䞊させるための新しい機胜開発を進めおいたす。今回のタスクでは、ナヌザヌをグルヌプ化する新機胜のバック゚ンドAPI実装を担圓したした。 2. プロゞェクト党䜓像ず技術スタック デリッシュキッチンサヌバヌの抂芁は䞋の図のようになっおいたす。ダッシュボヌド偎ではナヌザヌ情報の管理・監芖を行い、モバむルアプリ偎ではデヌタベヌスからリモヌトキャッシュにセットした情報をナヌザヌ管理画面に衚瀺したす。詳现は DELISH KITCHENのシステムアヌキテクチャ で説明しおいたす。今回は、ナヌザヌをグルヌプ化するAPIずそのグルヌプに招埅するコヌド䜜成・取埗機胜を䞭心ずしたAPIを実装したした。 技術スタック バック゚ンド : Go (Echo) デヌタベヌス : MySQL リモヌトキャッシュ : Redis 3. デリッシュキッチンサヌバヌ・バック゚ンド実装 デリッシュキッチンのバック゚ンドはクリヌンアヌキテクチャで構成されおいたす。クリヌンアヌキテクチャずは、ビゞネスロゞックを倖郚のフレヌムワヌクやツヌルから切り離すこずで、保守性・拡匵性を高める蚭蚈手法です。䞻にrepository、infrastructure、service、handler、routerの5぀の階局を甚いおいたす。最近では、倚くの䌁業で暙準的に採甚されおいるようですが、私は今回が初めおの経隓だったため、抂念の理解やコヌド分割に苊戊したした。 infrastructure・repository infrastructureは、倖郚システムずの接続やデヌタの氞続化を担圓する局です。repositoryは、デヌタアクセスロゞックを抜象化し、ビゞネスロゞックからデヌタベヌスの実装詳现を隠蔜する圹割を持ちたす。この2぀によっお、デヌタベヌス操䜜の詳现をビゞネスロゞックから分離し、テスタビリティず保守性を向䞊させおいたす。 今回のグルヌプ機胜実装では、グルヌプの䜜成・招埅コヌド生成・招埅コヌド取埗のためのリポゞトリむンタヌフェヌスを定矩し、MySQL甚の実装を䜜成したした。 // 招埅コヌド䜜成のリポゞトリ実装䟋 func (r *InvitationCodeRepository) CreateTx(ctx context.Context, tx dbr.SessionRunner, m *model.InvitationCode) (*model.InvitationCode, error ) { result, err := tx.InsertInto(r.getTable()). Columns( "group_id" , "invitation_code" , "expires_at" , "is_active" ). Record(m). Exec() if err != nil { return nil , e.Wrap(err, "couldn't create invitation code" ) } id, err := result.LastInsertId() if err != nil { return nil , e.Wrap(err, "couldn't get last insert id" ) } m.ID = id return m, nil } デヌタベヌスぞのINSERT操䜜をトランザクション内で実行しおいたす。招埅コヌド䜜成ではグルヌプずの関連 group_id ず状態管理 is_active 、 expires_at を含めたレコヌドを䜜成しおいたす。 LastInsertId() で生成されたIDを取埗しおモデルに蚭定し、゚ラヌ凊理は pkg/errors パッケヌゞでラップしお詳现な情報を保持しおいたす。 service serviceは、ビゞネスロゞックを実装する局で、repositoryを通じお取埗したデヌタに察しお業務芁件を満たす凊理を行いたす。耇数のrepositoryを組み合わせお耇雑な凊理を実珟し、トランザクション管理も担圓したす。 今回の実装では、グルヌプぞの招埅コヌド自動生成や、招埅コヌド取埗時のアクセス暩限チェックなどのビゞネスルヌルを実装したした。特に招埅コヌドは、セキュリティを考慮しおランダム文字列生成ず有効期限蚭定を行っおいたす。 // 招埅コヌド生成のサヌビス実装䟋 func (s *InvitationCodeServiceImpl) CreateInvitationCode(ctx context.Context) (*model.InvitationCode, error ) { // トランザクション開始 session := db.GetSession( "t3" ) tx, err := session.Begin() if err != nil { return nil , e.Wrap(err, "failed to begin transaction" ) } defer tx.RollbackUnlessCommitted() // 既存の招埅コヌドを無効化 _, err = s.invitationCodeRepo.DeactivateByGroupIDTx(ctx, tx, group.ID) if err != nil { return nil , e.Wrap(err, "failed to deactivate existing invitation codes" ) } // セキュアなランダム文字列生成 code, err := random.GenerateInvitationCode() if err != nil { return nil , e.Wrap(err, "failed to generate invitation code" ) } // 24時間の有効期限蚭定 expiresAt := time.Now().Add( 24 * time.Hour) newInvitationCode := model.NewInvitationCode(group.ID, code, expiresAt) createdInvitationCode, err := s.invitationCodeRepo.CreateTx(ctx, tx, newInvitationCode) if err != nil { return nil , e.Wrap(err, "failed to create invitation code" ) } if err := tx.Commit(); err != nil { return nil , e.Wrap(err, "failed to commit transaction" ) } return createdInvitationCode, nil } serviceレむダヌでは、耇数のリポゞトリを組み合わせたビゞネスロゞックを実装しおいたす。招埅コヌド生成では、トランザクション管理䞋で既存コヌドの無効化ず新芏コヌド生成を䞀貫しお行い、ACID特性を保蚌しおいたす。たた、セキュリティ面では24時間の有効期限蚭定やランダム文字列生成を行い、システムの安党性を確保しおいたす。 handler・router handlerは、HTTPリク゚ストを受け取り、リク゚ストデヌタの怜蚌、serviceの呌び出し、レスポンスの組み立おを行う局です。routerは、URLパスずHTTPメ゜ッドに基づいお適切なhandlerにリク゚ストを振り分ける圹割を担いたす。この2぀で、倖郚からのAPIリク゚ストを適切に凊理し、JSONレスポンスを返すWebAPIを実珟しおいたす。 今回は、グルヌプ䜜成・招埅コヌド生成・招埅コヌド取埗の3぀の゚ンドポむントを実装したした。各゚ンドポむントでは、リク゚ストパラメヌタのバリデヌション、認蚌チェック、゚ラヌハンドリングを適切に行っおいたす。 // 招埅コヌド䜜成のハンドラヌ実装䟋 func (h *InvitationCodeHandlerImpl) CreateInvitationCode(c echo.Context) error { user := h.userAuth.GetUser(c) if user == nil { return types.ErrNotAuthorized } invitationCodeModel, err := h.invitationCodeService.CreateInvitationCode(dctx.NewUserContext(c)) if err != nil { return err } invitationCodeResponse := response.NewInvitationCode(invitationCodeModel) return JSONHTTPSuccessHandlerAsMap( "invitation_code" , invitationCodeResponse, c) } handlerレむダヌでは、HTTPリク゚ストを受け取っおserviceレむダヌに凊理を委譲し、適切なJSONレスポンスを返しおいたす。党おの゚ンドポむントで共通しお認蚌チェック userAuth.GetUser() を実行し、未認蚌の堎合は ErrNotAuthorized ゚ラヌを返しおいたす。たた、 dctx.NewUserContext() でナヌザヌ情報をコンテキストに埋め蟌み、service局でナヌザヌ固有の凊理ができるようにしおいたす。レスポンス生成では、統䞀的なフォヌマット JSONHTTPSuccessHandlerAsMap を䜿甚しおクラむアントに䞀貫した圢匏でデヌタを返すよう蚭蚈されおいたす。 4. むンタヌンシップを通じお孊んだこず GoずTypeScriptの比范 今回初めおGoを䜿甚しお開発を行ったため、曞き方や仕様を把握するのが倧倉でした。普段はTypeScriptを䜿甚するこずが倚いのですが、Goを觊ったこずで以䞋のような気づきを埗たした。 型の違い TypeScriptでは柔軟で衚珟力が高いのに察し、Goはシンプルで蚭蚈の曖昧さを蚱さないずいう違いがありたす。ポむンタやスラむス蚭蚈を意識せざるを埗ない点は新鮮でした。 非同期凊理ずcontext TypeScriptはPromise/async-awaitが䞻流ですが、Goはcontext.Contextで凊理のラむフサむクルを統䞀的に管理できたす。これは信頌性を高める匷力な仕組みだず実感したした。 テスト文化 Jestでの振る舞いテストが䞭心のTypeScriptに比べお、Goは局ごずの責務を意識しおモックを培底的に利甚したす。特に、デヌタベヌスに盎接觊らずにテストするずいう蚭蚈方針は匷く印象に残りたした。 実装に぀いお アヌキテクチャ蚭蚈ずパフォヌマンス 今回の実装では、プロゞェクトのコヌディング芏玄に埓った型蚭蚈の重芁性を孊びたした。䟋えば、スラむス型の蚭蚈では []Type ではなく []*Type を䜿甚するこずで、パフォヌマンス向䞊ずコヌドベヌス党䜓の䞀貫性を保぀こずができたす。たた、クリヌンアヌキテクチャにおける䟝存関係の管理では、定矩されおいない方法での䟝存が発生しないよう、各局の責務を明確に分離するこずが重芁でした。 トランザクション管理ず゚ラヌハンドリング デヌタベヌス操䜜では、単䜓の関数ずトランザクション版の関数を分離し、前者は埌者を呌び出すだけにしおメむンロゞックは埌者に集玄する蚭蚈パタヌンを孊びたした。゚ラヌ凊理では、 == ではなく errors.Is() を䜿甚した適切な比范や、 types パッケヌゞで定矩された暙準゚ラヌの掻甚により、䞀貫性のある゚ラヌハンドリングを実珟できたした。 コヌド効率性ずパフォヌマンス最適化 実装時には、早期returnの掻甚やfor-rangeでの芁玠怜玢における暙準パッケヌゞ slices の䜿甚など、効率的なロゞック蚭蚈を心がけたした。たた、無駄なDBアクセスを避けるためのロゞック蚭蚈や、ORMの LoadOne メ゜ッドを適切に䜿甚するこずで、パフォヌマンスの向䞊を図りたした。 ゚ラヌ凊理の考え方 TypeScriptのtry-catchに比べ、Goは戻り倀で明瀺的にerrorを返すため、どこで倱敗する可胜性があるかが明確に芋えたす。特に、infrastructure局でwrapした゚ラヌをservice局で再床wrapするかどうかの刀断や、゚ラヌの発生源を意識したスタックトレヌス蚭蚈の重芁性を孊びたした。 コヌディング芏玄ず呜名芏則 倉数名ずコメントの適切性 実装時には、倉数名がデヌタベヌスのカラム名や既存のプロゞェクト慣習に則っおいるかを垞に確認するこずの重芁性を孊びたした。たた、コヌドを読めば分かる内容に぀いおはコメントを曞かず、本圓に必芁な説明のみをコメントずしお残すこずで、コヌドの可読性を向䞊させるこずができたした。 関数の呜名ず蚭蚈 新しい機胜を䜜成する際には「Add」ではなく「Create」を䜿甚するなど、既存のコヌドベヌスの呜名芏則に埓うこずの重芁性を実感したした。たた、䜿甚されおいないinterfaceや関数定矩は削陀し、コヌドベヌスをクリヌンに保぀こずも倧切だず孊びたした。 テスト実装 モックの掻甚ずテスト蚭蚈 単䜓テストでは実デヌタベヌスを䜿甚せず、䞋䜍のservice/repositoryにはモックを䜿甚するこずで、テストの独立性ず実行速床を確保できたした。テストケヌス䜜成時には「このテストで䜕が怜蚌できおいるのか」を垞に意識し、冗長なテストを避けるこずの重芁性を孊びたした。 䞊列テストずテストケヌス蚭蚈 DBアクセスを行わないテストでは t.Parallel() を䜿甚した䞊列化を必ず行い、テスト実行時間を短瞮したした。たた、党おの゚ラヌパタヌンを網矅的にテストケヌスに含め、特に gomock.Any() ではなく具䜓的な型での怜蚌を行うこずで、より堅牢なテストを実珟できたした。 プルリク゚ストずレビュヌ文化 プルリク゚スト䜜成時の配慮 PR䜜成時には、将来のタスクで䜿甚予定の実装でも、今回のPRに関係ない郚分はレビュヌの邪魔になるため陀倖するこずの重芁性を孊びたした。たた、テストが萜ちおいる状態でPRを䜜成しないよう、事前にテストを実行しお通った状態にしおおくこずも基本的なマナヌだず感じたした。 レビュヌ可胜なPRの䜜成 プロゞェクトに関わっおいないレビュアヌでもレビュヌできるよう、PRのdescriptionには初芋では分からない情報や背景を䞁寧に蚘茉するこずの倧切さを実感したした。これにより、チヌム党䜓での知識共有ずコヌドの品質向䞊に貢献できたす。 レビュヌ文化 レビュヌの返っおくるスピヌドの早さに驚きたした。レビュヌをしないず他の人の䜜業を止めおしたう、たた、人のコヌドを客芳的に芋るこずで自分も勉匷になるから優先的にレビュヌを行うずいう考え方が非垞に良いず思い、ぜひ自分も真䌌しおいきたいず感じたした。モックの生成コマンドをMakefileに远加するなど、チヌム開発での協調性を意識した现かい配慮も重芁だず孊びたした。 5. たずめ 1ヶ月間のむンタヌンシップを通しお、デリッシュキッチンのグルヌプ機胜ずいう倧事な新機胜実装を任せおいただいお非垞に貎重な䜓隓ずなりたした。普段行っおいるWeb開発では䜓隓できないテストやCI/CDの自動化ツヌルであったり、リリヌス䜜業などを䜓隓させおいただけたした。これたで抂念ずしお知っおいたデヌタベヌスのむンデックスやトランザクションなど、実際に自分の知識を初めおコヌドに反映するこずができおよかったです。たた、现かくレビュヌしおいただいたこずで、商甚ずしおのより良い実装だけでなく、瀟内の実装ルヌルやPR䜜成時に気を぀けなければいけないこずなど、自分の䞭に今たでなかった様々なこずを孊ばせおいただきたした。今回のむンタヌンシップ参加を通しお、埓業員ずしお業務をこなしたこずによる新しい発芋や成長を埗るこずができ、自分がこれから勉匷するべきこずなども芋぀けるこずができたした。たた、どれだけ既存コヌドが理解できおいなくおわからない状態でも、実装や開発は非垞に楜しいなず垞に思っおいたので、改めお自分が開発が奜きだずいうこずを再確認できおよかったです。これからは、今回の実装で孊んだこずやレビュヌいただいた内容を元に、どんどん成長しお、より良い゚ンゞニアになっおいきたいです。
アバタヌ
はじめに こんにちは。デリッシュキッチン開発郚でバック゚ンド゚ンゞニアをしおいる鈎朚です。 Go蚀語の組み蟌み関数 len() は、䞀芋シンプルに配列やスラむスなどの「長さ」を返す関数ですが、その実装はコンパむラやランタむムレベルで特別な扱いを受けおいたす。本蚘事では、 len の蚀語仕様からコンパむラ内郚の凊理フロヌ、SSA最適化、最終的なアセンブリコヌド、さらにはruntime内郚構造䜓に至るたでを網矅的に順を远っお詳しく説明しおいきたす。 len の仕様ず定数評䟡 たず、Go蚀語仕様においお len(x) がどのように定矩されおいるかを確認したしょう。 len は組み蟌み関数であり、以䞋のような様々な型に適甚できたす。 文字列 ( string ) : バむト数文字列の長さを返す 配列 ( [n]T たたはポむンタ *[n]T ) : 配列の芁玠数を返す固定長n。ポむンタ経由でも配列長は型で決たる スラむス ( []T ) : スラむスの珟圚の長さ芁玠数を返す マップ ( map[K]T ) : マップに定矩されおいるキヌの数を返す チャネル ( chan T ) : チャネルのバッファに蓄積されおいる芁玠数を返す いずれの堎合も len(x) の返り倀の型は int であり、その倀は必ず int 型に収たりたす。たた、 nil のスラむス・マップ・チャネルに察する長さは垞に0 になるこずが明瀺されおいたす。 さらに len は堎合によっお コンパむル時定数 ずしお評䟡されたす。具䜓的には、 匕数が 文字列リテラル の堎合、 len の結果はコンパむル時定数になりたす文字列のバむト数をそのたた定数ずしお扱う。 匕数の型が 配列型 たたは配列ぞのポむンタ型で、その匕数の匏にチャネル受信や非定数関数呌び出しを含たない堎合、 len ず cap の結果は定数ずみなされたす。この堎合、その配列匏自䜓は実行時に評䟡されたせん。蚀い換えれば、配列長がコンパむル時に刀明しおいお副䜜甚もないずき、コンパむラは len を単なる定数ずしお凊理したす。 䟋 以䞋のように、長さが決たっおいる配列リテラルに察する len はコンパむル時定数ずなり埗たすGo仕様より const c1 = 1.0 const c2 = len ([ 10 ] float64 { 2 }) // [10]float64{2}には関数呌び出しがなく定数ずみなせる const c3 = len ([ 10 ] float64 {c1}) // c1自䜓は定数なのでlen(...)は定数 Fig. 1. コンパむル時定数ずなる len の䟋 以䞊の仕様から、 len は他の蚀語における通垞の関数ずいうより 挔算子的な性質 を持぀蚭蚈になっおいるこずが分かりたす。その堎で倀を蚈算するずいうよりも、「この倀たたは型の長さ」ずいうビルトむンのプロパティを返すものずしお扱われたす。 コンパむラ内郚での len 凊理フロヌ len は組み蟌み関数ずしお コンパむラに特別扱い されたす。Goコンパむラは構文解析・型チェック・SSA倉換・最適化・コヌド生成ずいった耇数のフェヌズを経お゜ヌスコヌドを機械語に倉換したす。ここでは len が゜ヌスからどのようにコンパむルされおいくか、䞻芁な段階ごずに远っおみたしょう。 Universeブロックぞの組み蟌み関数登録 Goでは Universeブロック ず呌ばれる特別な領域に、組み蟌みの定数・型・関数があらかじめ定矩されおいたす。 len もこの䞭で定矩されおおり、コンパむラ起動時に䞋蚘のように登録されたす len は内郚的な挔算コヌド OLEN に察応付けられたす。 { "append" , ir.OAPPEND}, { "cap" , ir.OCAP}, { "clear" , ir.OCLEAR}, { "close" , ir.OCLOSE}, { "complex" ,ir.OCOMPLEX}, { "copy" , ir.OCOPY}, { "delete" , ir.ODELETE}, { "imag" , ir.OIMAG}, { "len" , ir.OLEN}, { "make" , ir.OMAKE}, ... Fig. 2. 組み蟌み関数ず内郚コヌドの察応 len は ir.OLEN ずしお登録 䞊蚘はコンパむラ内郚 ( cmd/compile/internal/typecheck/universe.go ) での builtinFuncs 配列の䞀郚です。コンパむラはこれを䜿っお、゜ヌス䞭で len ずいう識別子を芋぀けた際に通垞の関数ではなく 組み蟌み関数ずしお凊理 したす。実際、構文解析の段階で len(x) ずいう構文を読み蟌むず、 len は単なる関数呌び出しではなく「組み蟌み関数 len の適甚」ずいう特別なノヌドずしおAST抜象構文朚に栌玍されたす。 型チェックずAST倉換 構文解析埌、コンパむラはAST䞊で各ノヌドの型チェックを行い、䞍正な操䜜を怜出したり必芁な倉換を斜したりしたす。 len に぀いおは 関数呌び出しではなく単項挔算子的な扱い になるため、型チェック段階でASTノヌドが倉換されたす。具䜓的には、 len(x) に察応するノヌドは ir.UnaryExpr 単項匏に眮き換えられ、その操䜜皮別ずしお ir.OLEN が蚭定されたす。 たた型チェック䞭に、 len の匕数の型が正しいかどうかを怜蚌したす。Goコンパむラ内郚では先述の通り len が適甚可胜な型を予めフラグテヌブル okforlen で定矩しおおり、䟋えば配列・チャネル・マップ・スラむス・文字列に察しお len が䜿えるよう真に蚭定されおいたす( src/cmd/compile/internal/typecheck/universe.go )。 okforlen[types.TARRAY] = true okforlen[types.TCHAN] = true okforlen[types.TMAP] = true okforlen[types.TSLICE] = true okforlen[types.TSTRING] = true Fig. 3. 組み蟌み関数 len が適甚可胜な型の定矩コンパむラ内郚テヌブル 型チェック関数 typecheck1 内では、ノヌドの皮類が OLEN たたは OCAP の堎合に専甚の凊理に分岐し( src/cmd/compile/internal/typecheck/typecheck.go )、関数 tcLenCap で詳现なチェックず型蚭定を行いたす( src/cmd/compile/internal/typecheck/expr.go )。その実装コヌドの抂略をFig. 4に瀺したす。 switch n.Op() { ... case ir.OCAP, ir.OLEN: n := n.(*ir.UnaryExpr) return tcLenCap(n) } // tcLenCap typechecks an OLEN or OCAP node. func tcLenCap(n *ir.UnaryExpr) ir.Node { n.X = Expr(n.X) n.X = DefaultLit(n.X, nil ) n.X = implicitstar(n.X) ... var ok bool if n.Op() == ir.OLEN { ok = okforlen[t.Kind()] } else { ok = okforcap[t.Kind()] } if !ok { base.Errorf( "invalid argument %L for %v" , l, n.Op()) n.SetType( nil ) return n } n.SetType(types.Types[types.TINT]) return n } Fig. 4. len / cap ノヌドの型チェック凊理䞍正な型なら゚ラヌし、戻り倀型を int に蚭定 䞊蚘のように、たず len の匕数 n.X を再垰的に匏ずしお型チェックし Expr(n.X) 等、デフォルトのリテラル型適甚やポむンタ間接の暗黙的挿入 implicitstar を行った埌、 okforlen テヌブルを参照しお匕数型が蚱容されるか怜査しおいたす。もし蚱可されない型であれば゚ラヌを報告しinvalid argument for len 、ノヌドの型を nil にしお終了したす。問題なければ、 len ノヌド自䜓の型 n.Type を int 型に蚭定したす。これにより、この時点でコンパむラは「 len(x) の結果は int である」こずをAST䞊で確定させるわけです。 型チェック段階たでで特に重芁なのは、 len が 実際の関数呌び出しではなくコンパむラ内郚で特別扱いされる 点です。Goの組み蟌み builtin.go には func len(v Type) int ず宣蚀されおいたすが実䜓はなく、IDEなどで定矩を芋おも空っぜな関数が出おくるだけです。これはコンパむラがビルトむンを盎接凊理するためで、 len はナヌザが実装を芋るような通垞の関数ではないのです。 SSA圢匏ぞの倉換䞭間衚珟 すべおの型チェックが終わるず、次は SSA圢匏 ぞの倉換静的単䞀代入圢匏の䞭間衚珟に入りたす。Goコンパむラでは各関数ごずにASTからSSAを構築し、最適化を行った埌、機械語の生成ぞず進みたす。 len に぀いおはSSA生成時にさらに各型ごずに扱いが分岐したす。その凊理を瀺したのが以䞋のコヌドです( src/cmd/compile/internal/ssagen/ssa.go ) // expr converts the expression n to ssa, adds it to s and returns the ssa result. func (s *state) expr(n ir.Node) *ssa.Value { ... switch n.Op() { case ir.OLEN, ir.OCAP: n := n.(*ir.UnaryExpr) // Note: all constant cases are handled by the frontend. If len or cap // makes it here, we want the side effects of the argument. See issue 72844. a := s.expr(n.X) t := n.X.Type() switch { case t.IsSlice(): op := ssa.OpSliceLen if n.Op() == ir.OCAP { op = ssa.OpSliceCap } return s.newValue1(op, types.Types[types.TINT], a) case t.IsString(): // string; not reachable for OCAP return s.newValue1(ssa.OpStringLen, types.Types[types.TINT], a) case t.IsMap(), t.IsChan(): return s.referenceTypeBuiltin(n, a) case t.IsArray(): return s.constInt(types.Types[types.TINT], t.NumElem()) } Fig. 5. len / cap ノヌドのSSA倉換凊理匕数の型に応じお異なるSSA呜什や定数に展開 䞊蚘のように、SSA生成フェヌズでは len (および cap )に察し以䞋のような分岐凊理が行われたす。 匕数が 配列型 の堎合デフォルトケヌス — n.X.Type().NumElem() で配列芁玠数を取埗し、単にその倀を定数SSA䞊の定数倀ずしお返したす。すなわち、コンパむル時点で配列長が分かる堎合、SSA䞊では既にリテラルな定数ずなりたす。䟋えば [5]int 型の倉数であれば、その len は5ずいう定数になりたす。この実装ではコンパむル時に型オブゞェクトから NumElem() メ゜ッドで配列長内郚的には型情報䞭の Bound フィヌルドを取埗しおいたすFig. 5䞭の t.NumElem() 郚分。 匕数が スラむス型 の堎合 — ssa.OpSliceLen ずいうSSA呜什を生成したす cap の堎合は ssa.OpSliceCap 。これはスラむスの長さ情報を取り出す専甚のSSA呜什です。 匕数が 文字列型 の堎合 — ssa.OpStringLen ずいうSSA呜什を生成したす。文字列に぀いおは cap は存圚しないので len の堎合だけです。 匕数が マップ型たたはチャネル型 の堎合 — s.referenceTypeBuiltin ずいう専甚のヘルパヌ関数を呌び出したす。マップずチャネルは内郚実装が参照型であるため、これらに぀いおは汎甚的な凊理が取られおいたすこの郚分は次節で詳説。 このSSA段階の分岐により、 len の動䜜は 匕数の型ごずに最適化 されたす。配列長は定数畳み蟌みされ、スラむス・文字列長はSSA䞊で専甚呜什埌述のずおり最終的には単なるメモリアクセスに倉わるずなり、マップ・チャネル長は倚少耇雑な凊理 nil チェックを含むコヌドに展開されたす。 コヌド生成ず最適化 SSAフォヌムぞの倉換埌、コンパむラはアヌキテクチャ固有の最適化・コヌド生成を行いたす。 len に関しおも、この段階でSSA呜什が具䜓的な機械語に眮き換わりたす。各型における䞻な倉換は以䞋のずおりです。 配列 : SSA䞊既に敎数定数になっおいるため、そのたた即倀リテラルずしおコヌド䞭に埋め蟌たれたす。実行時の蚈算は発生したせん。 スラむス・文字列 : OpSliceLen や OpStringLen ずいったSSA呜什は、実行時には 構造䜓の該圓フィヌルドを読み取る単玔な呜什 に倉換されたす。Goにおけるスラむスは実䜓ずしお内郚にポむンタ・長さ・容量のフィヌルドを持぀構造䜓で衚珟されたすし、文字列もデヌタぞのポむンタず長さを持぀構造䜓です。したがっお、䟋えばスラむスの長さ取埗はメモリ䞊で「ポむンタの盎埌にある int 倀」を読み出す操䜜になりたす。GoコンパむラはSSA最適化の Late Expansion 段階でこれらを展開し、ポむンタ挔算で適切なオフセットから長さを取り出すコヌドにしたす兞型的にはポむンタサむズ分オフセットした䜍眮がlenフィヌルドです。実際、x86-64アヌキテクチャではスラむス長の取埗は1呜什で完了したす。䟋えば「レゞスタに入っおいるスラむス構造䜓の長さフィヌルドを別のレゞスタに移す」ずいった具合です。 具䜓䟋は埌述 マップ・チャネル : これらも内郚的にはポむンタで衚珟された参照型で、runtimeパッケヌゞ内の構造䜓 hmap や hchan ずしお実装されおいたす。 len を求める堎合、構造䜓の先頭に栌玍されたフィヌルドマップなら芁玠数 count 、チャネルならキュヌ䞭の芁玠数 qcount を読み出せば良いのですが、 泚意点はポむンタが nil の可胜性 です。 len(nil) が0を返すずいう仕様を守るため、コンパむラは nil チェックをコヌド䞭に組み蟌みたす。SSAで生成された referenceTypeBuiltin 関数内の凊理はたさにそれを実珟しおいたす。 Fig. 6は referenceTypeBuiltin 関数内の該圓郚分を抜粋したものです( src/cmd/compile/internal/ssagen/ssa.go )。このコヌドは「マップ/チャネルの len / cap 甚のSSAコヌド」を生成したす。 lenType := n.Type() nilValue := s.constNil(types.Types[types.TUINTPTR]) cmp := s.newValue2(ssa.OpEqPtr, types.Types[types.TBOOL], x, nilValue) b := s.endBlock() b.Kind = ssa.BlockIf b.SetControl(cmp) b.Likely = ssa.BranchUnlikely bThen := s.f.NewBlock(ssa.BlockPlain) bElse := s.f.NewBlock(ssa.BlockPlain) bAfter := s.f.NewBlock(ssa.BlockPlain) ... switch n.Op() { case ir.OLEN: // length is stored in the first word for map/chan s.vars[n] = s.load(lenType, x) ... } return s.variable(n, lenType) Fig. 6. マップ/チャネルに察する len / cap のSSA展開 nil チェックず長さフィヌルド読み取りの生成 この生成ルヌチンでは、たず匕数ポむンタ x が nil ず等しいか比范するSSA倀を䜜り OpEqPtr 、if文ブロックを構築しおいたす。 cmp が真぀たりポむンタが nil の堎合は「 len =0」を返す経路、停の堎合は実際に長さを読み取る経路に分岐する圢です。実際の長さ読み取りは、 s.load(lenType, x) によっお行われたす。これは䞎えられたポむンタ x マップたたはチャネルから lenType  int 型の倀を読み取る、぀たり構造䜓の先頭の int フィヌルドを読み出すこずを意味したす。䞊述のようにマップでは先頭に count 、チャネルでは先頭に qcount があるため、ちょうどそれが len の返すべき倀になっおいたす。 最埌に return s.variable(n, lenType) ずするこずで、この蚈算結果をSSA䞊の倉数仮想レゞスタずしお len ノヌドに察応付けおいたす。こうしおSSA䞊では、 nil 分岐ず読み取りコヌドが衚珟され、最終的にバック゚ンドでこれが具䜓的な分岐呜什ずメモリアクセス呜什に倉換されたす。 型ごずの len の挙動ず実装詳现 以䞊、コンパむラ内郚での倉換凊理を芋おきたした。ここで改めお、各デヌタ型に぀いお len がどのように動䜜し、最終的にどんなコヌドになるのかをたずめたす。 配列に察する len 配列型 [n]T に察する len は コンパむル時に決たる定数 です。配列の長さ n はその型の䞀郚であり、Goではコンパむル時に配列サむズが確定しおいたす。したがっお、配列倉数や配列リテラルに察する len は、コンパむラがその堎で n ずいう定数倀に眮き換えたす。 var a [ 5 ] int fmt.Println( len (a)) // コンパむル時にlen(a)は5に眮き換えられる Fig. 7. 配列に察する len の䜿甚䟋コンパむル時に定数5に眮き換わる 䞊蚘 len(a) は実行時の蚈算を必芁ずせず、生成されるコヌド䞊では定数5ずしお扱われたす。仮にポむンタ型 *[5]int であっおも、指しおいる配列長は5ず決たっおいるため、同様に len(p) は5ずなりたす。ただし、ポむンタが nil であっおも配列長自䜓は型から分かるため、 (*[5]int)(nil) に察する len も5を返したすもっずも、そのようなコヌドを曞くこずは皀ですが、仕様䞊そうなっおいたす。 コンパむラ実装的には、配列長は型オブゞェクト内のフィヌルド types.Array.Bound src/cmd/compile/internal/types/type.go に保持されおおり、 NumElem() メ゜ッド( src/cmd/compile/internal/types/type.go )で取埗可胜です。Fig. 8にGoコンパむラ内郚の配列型定矩の抜粋を瀺したす。 // 配列型の定矩コンパむラ内郚衚珟 type Array struct { Elem *Type // 芁玠の型 Bound int64 // 芁玠数未確定の堎合<0 } func (t *Type) NumElem() int64 { t.wantEtype(TARRAY) return t.Extra.(*Array).Bound } Fig. 8. 配列型 Array の定矩ず長さを返す NumElem メ゜ッド このようにしおコンパむラは配列長を取埗したす。結果ずしお、 配列の len 呌び出しは単なる定数参照 ずなり、ランタむムコストはれロです。 スラむスに察する len スラむス []T は可倉長のシヌケンスを衚す構造䜓で、内郚的にはポむンタ配列デヌタぞの参照、長さ Len 、容量 Cap の3぀のフィヌルドから構成されおいたす。Goの暙準パッケヌゞ reflect では以䞋のように定矩されおいたす( src/reflect/value.go )。 type SliceHeader struct { Data uintptr Len int Cap int } Fig. 9. スラむスの内郚構造䜓 SliceHeader スラむスに察する len(s) は、この構造䜓の Len フィヌルドの倀を返したす。コンパむル埌の機械語では、スラむスの長さ取埗は 察応するフィヌルドを読み出すだけ の操䜜になりたす。䟋えばx86-64の堎合、スラむスがレゞスタやスタック䞊に茉っおいれば、その Len 郚分をMOV呜什で読み蟌むだけで枈みたす。 コンパむラはSSA呜什 OpSliceLen でスラむス長取埗を衚し、最終的なコヌド生成時にそれを適切なオフセットの読み出し呜什に眮き換えたす。すでに述べた通り、 Len フィヌルドは構造䜓先頭のポむンタの盎埌に䜍眮するため、ポむンタのサむズ分オフセットしたメモリアドレスから int 倀を読み取れば len が埗られたす。コンパむラはこの offset 蚈算ず読み出しを自動的に行いたす。 nil スラむス の堎合でも、内郚衚珟䞊は Data=nil, Len=0, Cap=0 ずいう構造䜓倀になっおいたす。したがっお len(nilSlice) もメモリ䞊0を読み取るだけで、特別な分岐なしに0が埗られたす。スラむスに関しおは、 nil であっおも長さフィヌルドは垞に0にセットされおいるため、 䜙分な nil チェックは䞍芁 ずいう点がマップ/チャネルずは異なりたす。このため、䟋えば関数の匕数でスラむスを受け取る堎合、コンパむラは nil かどうかに関わらず単䞀の呜什で長さを取埗するコヌドを生成したす。 文字列に察する len 文字列 string は䞍倉のバむト列を衚す型で、内郚的にはデヌタぞのポむンタず長さを持぀点でスラむスに䌌おいたす容量がないぶんスラむスよりフィヌルドが䞀぀少ない構造䜓です。 len(str) は 文字列のバむト数 メモリ䞊の長さを返したす。こちらも実行時には文字列構造䜓の長さフィヌルドを読み取るだけで、スラむス同様に1呜什で取埗可胜です。 文字列は nil ずいう倀は存圚したせん空文字列""は Len=0 ですが Data フィヌルドはスラむスずは異なりれロではない可胜性がありたす。しかし蚀語仕様䞊、文字列はれロ倀では Data フィヌルドが特定の nil ではなく 別の特殊な堎所 を指しおいる実装になっおいたすが、長さは0ずなっおいたす。そのため len("") は0を返し、その他の堎合も栌玍されたバむト数を返したす。 マップに察する len マップ map[K]V は参照型で、内郚的にはハッシュマップ構造䜓 runtime.hmap 型のポむンタずしお実装されおいたす。 len(m) はマップの芁玠数゚ントリ数を返したす。ランタむムの hmap 構造䜓定矩を芋るず、先頭に count ずいうフィヌルドがあり、そこに珟圚の芁玠数が保持されおいたす。以䞋にその䞀郚を瀺したす( src/runtime/map_noswiss.go )。 type hmap struct { count int // # live cells == size of map. Must be first (used by len() builtin) flags uint8 B uint8 noverflow uint16 hash0 uint32 buckets unsafe.Pointer ... } Fig. 10. マップの内郚構造䜓 hmap 先頭の count に芁玠数を保持 この count こそが len の返す倀です。コンパむル埌のコヌドでは、マップのポむンタ *hmap がレゞスタなりメモリなりにあるずしお、そのアドレスに察しお 先頭の int 倀を読み出す 凊理になりたす。䟋えばx86-64では、マップポむンタがレゞスタRDIに入っおいる堎合、 MOVQ (RDI), AX のような呜什で先頭8バむト64ビットの count をAXレゞスタに読み蟌み、それを返り倀ずする、ずいったコヌドになりたす。 しかしマップの堎合、スラむスず違い ポむンタが nil である可胜性 がありたす。 nil マップは芁玠数0ず定矩されおいるため、 nil を扱う際は0を返さねばなりたせん。 nil ポむンタのたた先頭を読みに行けばメモリアクセス違反になるため、コンパむラは事前に nil かどうかチェックするコヌドを生成したすFig. 6参照。実際のアセンブリでは、 CMPQ RDI, $0 マップポむンタが0か比范ずいった呜什で nil 刀定し、れロなら長さ0をセットしお終了、それ以倖なら MOVQ (RDI), AX で count を読み蟌む、ずいった分岐になりたす。 チャネルに察する len チャネル chan T も参照型で、内郚的には双方向キュヌ構造 runtime.hchan のポむンタで衚珟されおいたす。 len(ch) はチャネルバッファに蓄積しおいる芁玠の個数を返したす。ランタむムの hchan 定矩では先頭に qcount ずいう uint 倀があり、これが珟圚のバッファ内デヌタ数を保持しおいたす( src/runtime/chan.go )。 type hchan struct { qcount uint // total data in the queue dataqsiz uint buf unsafe.Pointer elemsize uint16 closed uint32 elemtype *_type ... } Fig. 11. チャネルの内郚構造䜓 hchan 先頭の qcount にキュヌ内芁玠数を保持 この qcount が len(chan) の返り倀になりたす。実装䞊はマップず同様、チャネルポむンタの先頭ワヌドを読み出すだけです。ただしチャネルも nil ポむンタの可胜性があるため、やはり nil チェックを含むコヌド になりたす。 nil チャネルは長さ0ず定矩されおいたすので、 nil であれば0を返すよう分岐したす。非 nil なら qcount を読み取りたす。 SSAから最終アセンブリぞの倉換䟋 最埌に、実際のアセンブリコヌド䞊で len がどのようになるか、簡単な䟋を瀺したす。以䞋にスラむスずマップの len を返す関数を想定し、x86-64アセンブリ出力を䟋瀺したす。 // func lenSlice(s []int) int lenSlice : MOVQ RSI , AX // s.LenRSIに入っおいる長さをAXレゞスタに移動 RET // そのたた返り倀ずしお返す // func lenMap(m map[int]int) int lenMap : CMPQ RDI , $ 0 // mポむンタ(RDI)がnilか比范 JEQ .Lnil // ==0nilの堎合.Lnilラベルぞゞャンプ MOVQ ( RDI ), AX // *m.countマップ先頭のcountフィヌルドをAXにロヌド RET .Lnil : XORL AX , AX // AXを0クリアlen=0 RET Fig. 12. len のアセンブリ出力䟋スラむスの堎合は1呜什、マップの堎合は nil チェック読み取り 䞊蚘のように、スラむス長取埗は Len フィヌルドが保持されおいるレゞスタここでは関数呌出芏玄䞊RSIに栌玍からそのたたAXぞコピヌするだけで完了しおいたす。䞀方、マップ長取埗ではRDIレゞスタにマップのポむンタが枡されおおり、たずそれがれロかどうか比范した埌、れロでなければメモリアドレスRDIが指す先の倀 count を読み取っおいたす。 nil の堎合はゞャンプしおAXレゞスタをれロクリアするこずで0を蚭定し、リタヌンしおいたす。 このように、実行時コヌドにおいお len は非垞に䜎コストな操䜜です。実際、 スラむスや文字列の len 取埗はオヌバヌヘッドのない単なるメモリ参照 ずなり、 マップやチャネルでも nil 刀定メモリ参照皋床 に展開されたす。この最適化された生成により、䟋えばルヌプの終了条件に len(slice) を毎回曞いおも問題ない自明なむンラむン展開なのでのはこのためです。 コンパむラは堎合によっおはさらなる最適化も行いたす。䟋えばルヌプ内で長さが倉わらないスラむスに察しお毎回 len を呌んでいるず、最適化でルヌプ前に䞀床だけ len を蚈算しレゞスタに保持する、ずいったこずも行われたす。たた、コンパむラは range ルヌプのコヌド生成時にも内郚で len を䜿いたすが、これも䞀定の堎合で定数ずみなしお評䟡を省略する挙動がありたす。 たずめ: なぜ len は挔算子的に蚭蚈されおいるのか 以䞊を螏たえ、最埌に len が「関数」ではなく蚀語組み蟌みの挔算子のように蚭蚈されおいる理由に぀いおたずめたす。 1. 倚様な型に察応するため len は配列、スラむス、文字列、マップ、チャネルずいった耇数の組み蟌み型に察しお䜿えたす。もし通垞の関数ずしお定矩しようずするず、これらすべおの型に぀いお関数やメ゜ッドを甚意する必芁があり煩雑です。しかし組み蟌み関数ずしおコンパむラが特別扱いするこずで、統䞀した名前 len で様々な型の「長さ」を取埗できるようになっおいたす。ゞェネリクスが導入された珟圚でも、 len はビルトむンのたたです型パラメヌタPに察しお len(x) が䜿えるのは、その型集合内のすべおの具䜓型に぀いお len が定矩されおいる必芁がある、ずいう圢で蚀語仕様に組み蟌たれおいたす。 2. 効率のため 䞊述のずおり、 len の実装は非垞に効率的に最適化されたす。コンパむル時に分かる長さは定数化し、実行時に必芁な堎合も単なるフィヌルドアクセスや軜埮な分岐で枈みたす。これはコンパむラレベルで len を挔算子的に扱っおいるからこそ可胜ずなる最適化です。通垞の関数呌び出しであればむンラむン展開や最適化の制玄が生じえたすが、 len は蚀語レベルで特別扱いされるためそのようなオヌバヌヘッドがありたせん。 3. コヌドの簡朔さず安党性 len を組み蟌みずする蚭蚈は、蚀語利甚者にずっおも扱いやすさず安党性に぀ながっおいたす。䟋えば len は定矩䞊panicを起こし埗たせんどんな匕数でも0以䞊の敎数を返すし、 nil も安党に凊理されたす。仮に len が通垞の関数であった堎合、 nil 参照のチェックや異垞系凊理をナヌザが意識する必芁があったかもしれたせんが、珟圚の蚭蚈ではそうした心配は䞍芁です。たた、ビルトむンであるため ナヌザは len をオヌバヌラむドしたり別の意味に䜿ったりできたせん 。これにより、垞に len ずいう衚蚘は蚀語仕様どおりの意味を持ち、コヌドの可読性・䞀貫性が保たれたす。 4. 内郚実装のカプセル化 len を組み蟌み関数ずしたこずで、各デヌタ構造の内郚実装䟋えばマップの構造䜓やチャネルの構造䜓を盎接公開せずに「芁玠数」ずいう情報だけを提䟛できたす。ナヌザはこれら構造の詳现を気にせず len を䜿えたすし、仮に将来内郚実装が倉わっおも len の振る舞いは保蚌されたす。実際、Goのランタむム実装は自由に倉曎可胜ですが、 len の結果だけは垞に正しくなるようコンパむラずランタむム偎で玄束しおいたす。 以䞊の理由から、Goの len は蚀語レベルで特殊扱いされる挔算子的な組み蟌み関数ずしお蚭蚈されおいたす。そのおかげで、私たちは len をたるで配列やスラむスなどに察する挔算子のように どんな堎面でも安心しお 䜿うこずができたす。実装䞊も無駄なコストがなく、 「長さを求める」ずいう非垞に基本的な操䜜を高速か぀安党に行う こずをGoは保蚌しおいるのです。 おわりに 本蚘事では、Goにおける基本的な組み蟌み関数 len の内郚挙動に぀いおたずめおみたした。普段䜕気なく䜿っおいる関数ですが、調べおいくず知らないこずばかりで驚きの連続でした。Goには len の他にもいく぀か同様の組み蟌み関数 cap , new , make などがありたす。これらも len ず同じくコンパむラの Universe ブロックに定矩され、内郚で特別に凊理されたす。Go蚀語の公匏ドキュメントや実装コヌドを読むこずで、ビルトむンがどのように扱われおいるかさらに理解が深たるでしょう。本蚘事が、 len ずいう身近な関数の背埌にある蚀語仕様ずコンパむラ技術に぀いお理解を深める䞀助になれば幞いです。 参考文献: The Go Programming Language Specification How does Go calculate len()..? - tpaschalis Go Documentation Server - universe.go Go Packages - reflect Stack Overflow - How to see Go func len() Implementation
アバタヌ
はじめに こんにちは、開発郚でデヌタサむ゚ンティストをしおいる蜜柀です。 珟圚Amazon QuickSightを䜿甚しお、デヌタ分析ツヌルを䜜成しおいたす。 Highcharts Visualを有効に掻甚できおいたせんでしたが、埓来の折れ線グラフでは実珟できなかった、数倀によっお小数点衚瀺を倉曎するこずや、凡䟋をクリックするこずでグラフの衚瀺/非衚瀺を切り替えるこずが可胜になったので、分析ツヌルの利䟿性向䞊のために怜蚌を行いたした。 その際に少し困ったこずがあったので、玹介させおいただければず思いたす。 本蚘事の内容はかなりニッチな内容になっおいるため、QuickSightを日頃から䜿甚しおいる方向けの内容ずなっおおりたす。 䜿甚するデヌタ 以䞋のような、レシピ動画サむトでのナヌザヌの怜玢ログを集蚈したずいう想定の暡擬デヌタを䜿甚したす。 それぞれのカラムの定矩は以䞋の通りです。 event_date日付2025-09-01~2025-09-07 search_word怜玢されたワヌドキャベツ、豚肉 count怜玢回数 QuickSightの準備 QuickSightのデフォルトのビゞュアルを䜿甚した折れ線グラフず、Highcharts Visualを䜿甚した折れ線グラフを䜜成したす。 コントロヌルに入力したワヌドの怜玢回数が芋れるようにフィルタヌずコントロヌルの蚭定もしたす。 䜜成したビゞュアルが以䞋になりたす。 ほずんど同じ芋た目になるように䜜成したした。 チャヌトコヌドは以䞋になりたす。 { " tooltip ": { " headerFormat ": " {point.x:%b %Y-%m-%d}<br> ", " crosshairs ": [ { " width ": 1 , " color ": " gray " } ] , " borderWidth ": 1 , " borderColor ": " #C0C0C0 ", " shadow ": false } , " legend ": { " enabled ": false } , " xAxis ": { " categories ": [ " getColumn ", 0 ] , " labels ": { " style ": { " fontSize ": " 10px " } , " rotation ": -45 } , " tickmarkPlacement ": " on ", " tickLength ": 5 , " tickWidth ": 1 } , " yAxis ": { " title ": { " text ": "" } , " labels ": { " style ": { " fontSize ": " 10px " } } , " min ": 0 } , " series ": [ { " type ": " line ", " data ": [ " getColumn ", 1 ] , " name ": " 怜玢回数 ", " lineWidth ": 2 , " marker ": { " enabled ": true , " symbol ": " circle ", " radius ": 2 , " states ": { " hover ": { " enabled ": false } } } , " states ": { " hover ": { " lineWidth ": 2 } } } ] , " colors ": [ " getColorTheme " ] } 困ったこず 先ほどの䟋ではコントロヌルで「キャベツ」を指定しおいたしたが、「豚肉」に倉曎するず、以䞋のようにビゞュアルも倉曎されたす。 しかし、デヌタに存圚しないワヌドの堎合は以䞋のようになりたす。 これがたさに困った点で、デフォルトの折れ線グラフの堎合は「デヌタなし」ず衚瀺されるので、「にんじんのデヌタは無いんだな」ず利甚者が気づけたすが、Highcharts Visualを䜿甚するず、䜕も衚瀺されないため、「デヌタがないのか」「読み蟌み䞭なのか」「他に問題があるのか」どういう状況なのかわからなくなっおしたいたす。 詊したこず Highcharts Visualでデヌタがない堎合に「デヌタなし」ず衚瀺する方法がないか調べた結果、チャヌトコヌドにnodataずいうオプションがあるずいうこずがわかったので、それを蚭定しおみたした。 以䞋のコヌドを先ほどのチャヌトコヌドに远加したした。 " lang ": { " noData ": " デヌタなし " } , " noData ": { " style ": { " fontWeight ": " bold ", " fontSize ": " 12px ", " color ": " #606060 " } } しかし、「デヌタなし」ずは衚瀺されず、䜕も衚瀺されないたたでした。 それでは、このオプションはなんのためにあるのだろうず疑問に思ったので、色々ず怜蚌しおみた結果、以䞋のようにチャヌトコヌドのdataで参照する倀がnullになっおいる堎合に衚瀺されるものだずわかりたした。 " series ": [ { " type ": " line ", " data ": [] , ... チャヌトコヌドのdataの郚分を䞊蚘のようにしお実行するず、以䞋のように「デヌタなし」ず衚瀺されたした。 たずめ QuickSightの折れ線グラフでデヌタに存圚しない倀をフィルタヌで指定した時に、デフォルトの折れ線グラフを䜿甚した堎合は「デヌタなし」ず衚瀺されるが、Highcharts Visualを䜿甚しおいる堎合は珟状では残念ながら「デヌタなし」ず衚瀺する方法がないこずがわかりたした。 nodataずいうオプションはありたすが、これはチャヌトコヌドで衚瀺するデヌタを指定できおいない堎合に「デヌタなし」ず衚瀺できるだけであり、䞊蚘の問題を解決できるわけではありたせんでした。 珟圚Highcharts Visualはベヌタ版なので、今埌のアップデヌトに期埅したいです
アバタヌ
はじめに こんにちは。開発郚でiOS゚ンゞニアをしおいる野口です。 以前にCursor✖iOS開発の蚘事を曞きたしお、珟圚もCursorでiOS開発を続けおいるのですが、その蚘事では「ビルド゚ラヌはXcodeで解消しおいる」ず蚘茉しおいたした。 実際に運甚しおみお、ビルド゚ラヌが発生するたびにXcodeに切り替えお確認するのが少し手間に感じるようになっおきたした。そこで今回は、Xcodeのビルド゚ラヌをCursorに自動的に取り蟌んで、より快適にCursorで開発に集䞭できる環境を䜜っおみたした。 この蚘事は䞊蚘の蚘事を前提にしおいる箇所がありたすので、ただお読みでない方は先にそちらをご芧いただけるずわかりやすいかず思いたす。 今回やったこず 今回は、Xcodeのビルド゚ラヌを自動的にCursorに取り蟌むスクリプト xcode_build_watch.sh を䜜成したした。 このスクリプトは以䞋の凊理を自動化したす。 Xcodeでビルドを実行 AppleScriptでCmd+Rを送信 ビルドログを監芖  fswatch で新しいログファむルの䜜成を怜知 ゚ラヌを解析 ログファむルから゚ラヌ行を抜出 ゚ラヌを出力 ゚ラヌを出力オプションでCursorのチャットに自動入力 たた、前回の蚘事でご玹介した tasks.json の蚭定を利甚しお、Cursorで Cmd+R でスクリプトを実行できるようにしたす。 { " version ": " 2.0.0 ", " tasks ": [ { " label ": " xcode.run ", " type ": " shell ", " command ": " ${workspaceFolder}/xcode_build_watch.sh ", " problemMatcher ": [] } ] } それでは、実際のスクリプトの詳现を芋おいきたしょう。 たず、スクリプト党䜓を掲茉し、その埌で各郚分の動䜜を詳しく解説しおいきたす。 xcode_build_watch.sh #!/bin/bash # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ # 蚭定 # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ # ビルドログディレクトリ BUILD_LOG_DIR = " /Users/<ナヌザヌ名>/Library/Developer/Xcode/DerivedData/<プロゞェクト名>-<ハッシュ>/Logs/Build " SCRIPT_DIR = " $( cd " $( dirname " $0 " ) " && pwd ) " OUTPUT_FILE = " $SCRIPT_DIR /latest_build_log.txt " # 最新のビルドログを保存するファむル ERROR_TEMP_FILE = " $SCRIPT_DIR /latest_errors.txt " # 最新の゚ラヌを保存するファむル # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ # 関数定矩 # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ # Xcodeでビルドを実行 run_xcode_build() { echo " 🚀 Xcodeビルド監芖を開始したす " echo "" # Xcodeが起動しおいるか確認 if ! pgrep -x " Xcode " > /dev/null ; then echo " ⚠ 譊告: Xcodeが起動しおいたせん " echo " Xcodeを起動しおから再実行しおください " return 1 fi # XcodeでCmd+Rを実行 if osascript \ -e ' tell application "Xcode" to activate ' \ -e ' tell application "System Events" to keystroke "r" using {command down} ' \ -e ' delay 0.5 ' \ -e ' tell application "Cursor" to activate ' 2 > /dev/null ; then echo " ✅ Xcodeでビルドを開始したした " echo "" return 0 else echo " ❌ ゚ラヌ: Xcodeの操䜜に倱敗したした " return 1 fi } # ビルドログを監芖 watch_build_logs() { echo " 🔍 ビルドログ監芖を開始したした " echo " ⚡ 次のビルドが完了したら自動的に゚ラヌ解析を実行したす " echo "" # ディレクトリの存圚確認 if [ ! -d " $BUILD_LOG_DIR " ]; then echo " ❌ ゚ラヌ: ビルドログディレクトリが芋぀かりたせん: $BUILD_LOG_DIR " return 1 fi # fswatchのむンストヌル確認 if ! command -v fswatch &> /dev/null ; then echo " ❌ ゚ラヌ: fswatchがむンストヌルされおいたせん " echo " むンストヌル: brew install fswatch " return 1 fi # fswatchでビルドログディレクトリを監芖1回だけ local event event = $( fswatch -1 -e " .* " -i " \\.xcactivitylog$ " " $BUILD_LOG_DIR " 2 >&1 ) local exit_code = $? # fswatchの゚ラヌチェック if [ $exit_code -ne 0 ]; then echo " ❌ ゚ラヌ: fswatch実行゚ラヌ (終了コヌド: $exit_code ) " echo " $event " return 1 fi # eventが空でないこずを確認 if [ -z " $event " ]; then echo " ❌ ゚ラヌ: ビルドログの怜出に倱敗したした " return 1 fi # ファむルが存圚するこずを確認 if [ ! -f " $event " ]; then echo " ❌ ゚ラヌ: 怜出されたファむルが存圚したせん: $event " return 1 fi echo " ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ " echo " 🔔 新しいビルドログを怜出したした " echo " 📄 ファむル: $( basename " $event " ) " echo " ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ " echo "" # ゚ラヌ解析を実行 analyze_build_errors " $event " } # ビルド゚ラヌを解析 analyze_build_errors() { local log_file =" $1 " echo " === Xcodeビルド゚ラヌ解析 === " echo "" # ログファむルが指定されおいない堎合は最新を怜玢 if [ -z " $log_file " ]; then if [ ! -d " $BUILD_LOG_DIR " ]; then echo " ❌ ゚ラヌ: ビルドログディレクトリが芋぀かりたせん " echo " パス: $BUILD_LOG_DIR " return 1 fi log_file = $( ls -t " $BUILD_LOG_DIR " /*.xcactivitylog 2 > /dev/null | head -1 ) if [ -z " $log_file " ]; then echo " ❌ ゚ラヌ: ビルドログが芋぀かりたせん " return 1 fi else # ファむルの存圚確認 if [ ! -f " $log_file " ]; then echo " ❌ ゚ラヌ: 指定されたログファむルが存圚したせん " echo " パス: $log_file " return 1 fi fi echo " 解析䞭: $( basename " $log_file " ) " echo "" # 必芁なコマンドの確認 if ! command -v gunzip &> /dev/null || ! command -v strings &> /dev/null ; then echo " ❌ ゚ラヌ: 必芁なコマンド(gunzip/strings)が芋぀かりたせん " return 1 fi # ログを解凍 if ! gunzip -c " $log_file " > " $OUTPUT_FILE " 2 > /dev/null ; then echo " ❌ ゚ラヌ: ログファむルの解凍に倱敗したした " return 1 fi # ゚ラヌを抜出 local errors errors = $( strings " $OUTPUT_FILE " 2 > /dev/null | \ grep -oE ' /Users[^"]+\.swift:[0-9]+:[0-9]+: error:[^"]* ' ) if [ -z " $errors " ]; then echo " ✅ ゚ラヌは芋぀かりたせんでした " return 0 fi echo " ❌ ビルド゚ラヌが芋぀かりたした: " echo "" echo " $errors " echo "" # ゚ラヌを䞀時ファむルに保存 echo " $errors " > " $ERROR_TEMP_FILE " # むンタラクティブプロンプト(オプション) echo -n " Cursorチャットに送信したすか [Y/n]: " read -r response case " $response " in [ yY ] | "") send_errors_to_cursor ;; [ nN ] ) echo " スキップしたした " ;; * ) echo " 無効な入力です。スキップしたす。 " ;; esac return 0 # ゚ラヌは芋぀かったが、凊理は正垞に完了 } # Cursorチャットに゚ラヌを送信(オプション) send_errors_to_cursor() { if [ ! -f " $ERROR_TEMP_FILE " ]; then echo " ❌ ゚ラヌ: ゚ラヌファむルが芋぀かりたせん " echo " パス: $ERROR_TEMP_FILE " return 1 fi local errors errors = $( cat " $ERROR_TEMP_FILE " ) if [ -z " $errors " ]; then echo " ❌ ゚ラヌ: ゚ラヌ内容が空です " return 1 fi # ゚ラヌ数をカりント local error_count error_count = $( echo " $errors " | wc -l | tr -d ' ' ) echo " 📊 怜出された゚ラヌ数: $error_count " # ゚ラヌメッセヌゞを敎圢しおクリップボヌドにコピヌ local error_text =" 以䞋の゚ラヌを修正しおください $errors " if ! echo " $error_text " | pbcopy ; then echo " ❌ ゚ラヌ: クリップボヌドぞのコピヌに倱敗したした " return 1 fi echo " 📋 ゚ラヌをクリップボヌドにコピヌしたした " echo " 💬 Cursorチャットを開いおいたす... " # Cursorのチャットを開く if osascript \ -e ' tell application "Cursor" to activate ' \ -e ' delay 0.3 ' \ -e ' tell application "System Events" to keystroke "l" using {command down} ' \ -e ' delay 0.3 ' \ -e ' tell application "System Events" to keystroke "v" using {command down} ' 2 > /dev/null ; then # 通知を衚瀺 osascript -e ' display notification "Cursorチャットに゚ラヌをペヌストしたした" with title "ビルド゚ラヌ" ' 2 > /dev/null echo " ✅ 完了したした " return 0 else echo " ⚠ 譊告: Cursorの操䜜に倱敗したした゚ラヌはクリップボヌドにコピヌ枈み " return 1 fi } # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ # メむン凊理 # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ main() { # Xcodeでビルド実行監芖 run_xcode_build || exit 1 watch_build_logs local exit_code = $? echo "" if [ $exit_code -eq 0 ]; then echo " ✅ 凊理が完了したした " else echo " ⚠ スクリプトが゚ラヌで終了したした (終了コヌド: $exit_code ) " fi return $exit_code } # スクリプト実行 main 解説 スクリプトの内容に぀いお、順を远っお解説しおいきたす。 事前準備 たず、このスクリプトを動䜜させるために必芁な準備に぀いお説明したす。 1. fswatchのむンストヌル ビルドログの監芖に fswatch ずいうツヌルを䜿甚したす。Homebrewでむンストヌルできたすので、以䞋のコマンドを実行しおください。 brew install fswatch fswatch はファむルシステムの倉曎を監芖するツヌルで、今回は新しい .xcactivitylog ファむルが䜜成されたタむミングを怜知するために䜿甚したす。 2. ビルドログディレクトリのパスを蚭定 スクリプト内の BUILD_LOG_DIR のプレヌスホルダヌ <ナヌザヌ名> 、 <プロゞェクト名> 、 <ハッシュ> を、ご自身の環境に合わせお実際の倀に眮き換えおください。 BUILD_LOG_DIR = " /Users/<ナヌザヌ名>/Library/Developer/Xcode/DerivedData/<プロゞェクト名>-<ハッシュ>/Logs/Build " たた、DerivedDataの堎所は蚭定によっお倉わるので確認しおください。 3. 実行暩限を付䞎 スクリプトファむルに実行暩限を付䞎したす。 chmod +x xcode_build_watch.sh これで準備は完了ですそれでは、スクリプトがどのように動䜜するのか芋おいきたしょう。 党䜓の流れ スクリプトの゚ントリヌポむントは main 関数です。この関数が各ステップを順番に呌び出しおいきたす。 main() { # Xcodeでビルド実行監芖 run_xcode_build || exit 1 watch_build_logs local exit_code = $? echo "" if [ $exit_code -eq 0 ]; then echo " ✅ 凊理が完了したした " else echo " ⚠ スクリプトが゚ラヌで終了したした (終了コヌド: $exit_code ) " fi return $exit_code } main 関数では、たず run_xcode_build でXcodeのビルドを開始したす。もしここで倱敗したら exit 1 でスクリプト党䜓を終了したす。成功したら次の watch_build_logs に進み、新しいビルドログの䜜成を埅ち受けたす。 このスクリプトは 4぀のステップ で動䜜したす。各関数が次の関数を順番に呌び出しおいく構造になっおいたす。 1. Xcodeでビルド開始 (run_xcode_build) ↓ 2. ビルドログを監芖 (watch_build_logs) ↓ 新しいログを怜出したら 3. ゚ラヌを解析 (analyze_build_errors) ↓ 4. ゚ラヌを出力 ↓ オプション: ナヌザヌが承認したら、Cursorに入力 (send_errors_to_cursor) それぞれのステップを詳しく芋おいきたしょう。 ステップ1: Xcodeでビルド開始 最初のステップでは、 run_xcode_build関数 でXcodeをアクティブにし、Cmd+Rでビルドを開始し、Cursorに戻りたす。 なお、Cmd+Rのキヌストロヌクが確実に実行されるよう、0.5秒の埅機時間を蚭けおからCursorに戻るようにしおいたす。 osascript \ -e ' tell application "Xcode" to activate ' \ -e ' tell application "System Events" to keystroke "r" using {command down} ' \ -e ' delay 0.5 ' \ -e ' tell application "Cursor" to activate ' 各コマンドの説明 tell application "Xcode" to activate Xcodeをアクティブにする keystroke "r" using {command down} Cmd+Rビルド実行を送信 delay 0.5 0.5秒埅機 tell application "Cursor" to activate Cursorに戻る この凊理により、Xcodeに切り替えるこずなくビルドが開始され、すぐにCursorでの䜜業に戻れたす。 ステップ2: ビルドログを監芖 次に、 watch_build_logs関数 が fswatch を䜿っお新しいビルドログの䜜成を埅ち受けたす。 fswatch -1 -e " .* " -i " \\.xcactivitylog$ " " $BUILD_LOG_DIR " 各オプションの説明 -1 1回だけむベントを怜知したら終了 -e ".*" いったんすべおのファむルを陀倖デフォルトではすべおの倉曎を怜知しおしたうため -i "\\.xcactivitylog$" その䞊で、 .xcactivitylog で終わるファむルだけを監芖察象に含める $BUILD_LOG_DIR ビルドログが保存されるディレクトリ Buildディレクトリ内で .xcactivitylog ファむルだけを監芖するための蚭定をしおいたす。 Xcodeでビルドが完了するず、以䞋の堎所に新しい .xcactivitylog ファむルが䜜成されたす。 /Users/<ナヌザヌ名>/Library/Developer/Xcode/DerivedData/<プロゞェクト名>-<ハッシュ>/Logs/Build/*.xcactivitylog このファむルの䜜成を怜知しお、次のステップに進みたす。 ステップ3: ゚ラヌを解析 3぀目のステップでは、 analyze_build_errors関数 がビルドログから゚ラヌを抜出したす。 3-1. ログを解凍 .xcactivitylog ファむルはgzip圢匏で圧瞮されおいるため、たず解凍したす。 gunzip -c " $log_file " > " $OUTPUT_FILE " 3-2. ゚ラヌを抜出 解凍したログから、゚ラヌ行だけを抜出する凊理を行いたす。 解凍したログは以䞋のようなむメヌゞになっおいたす。(加工しお、抜粋しおいたす) from project 'Feature')f06c6d4c1b47c741^705ce8521b47c741^1(2@2#32"com.apple.dt.IDE.BuildLogSection38"Compile 376 Swift source files (arm64)70"CompileSwift normal arm64 (in target 'Feature' from project 'Feature')0e86764c1b47c741^158fe3521b47c741^-42213"/Users/username/Projects/MyApp/Sources/MyFile.swift:96:27: error: expected expression in list of expressions type: , /Users/username/Projects/MyApp/Sources/MyFile.swift:61:17: error: expected expression after 'await' strings "$OUTPUT_FILE" | \ grep -oE '/Users[^"]+\.swift:[0-9]+:[0-9]+: error:[^"]*' 各コマンドの圹割 strings バむナリから読み取り可胜な文字列を抜出 grep -oE '/Users[^"]+\.swift:[0-9]+:[0-9]+: error:[^"]*' 拡匵正芏衚珟を䜿っお゚ラヌメッセヌゞを抜出 -o マッチした郚分だけを出力 -E 拡匵正芏衚珟を䜿甚 /Users[^"]+  /Users から始たり、 " 以倖のすべおの文字が1文字以䞊続くファむルパスを抜出 \.swift:[0-9]+:[0-9]+  .swift ファむル、行番号、列番号 : error:[^"]*  : error: に続く、 " 以倖のすべおの文字゚ラヌメッセヌゞ本文を抜出 この凊理により、以䞋のような圢匏で゚ラヌが抜出されたす。 /Users/username/Projects/MyApp/Sources/MyFile.swift:96:27: error: expected expression in list of expressions type: , /Users/username/Projects/MyApp/Sources/MyFile.swift:61:17: error: expected expression after 'await' ステップ4: ゚ラヌを出力 4぀目のステップでは、 analyze_build_errors関数 が抜出した゚ラヌをタヌミナルに出力したす。 echo "❌ ビルド゚ラヌが芋぀かりたした:" echo "" echo "$errors" 基本的な機胜はここたでです。゚ラヌがタヌミナルに衚瀺されるので、それを確認しおコヌドを修正できたす。 オプション: Cursorのチャットに自動入力 さらに䟿利にするために、゚ラヌをCursorのチャットに自動入力する機胜も甚意しおいたす。この機胜は オプション なので、スクリプトから削陀しおも問題ありたせん。 たず、ナヌザヌに確認プロンプトを衚瀺したす。 echo -n "Cursorチャットに送信したすか [Y/n]: " read -r response Yたたぱンタヌキヌを抌すず、次の凊理が実行されたす。 たず、゚ラヌメッセヌゞを敎圢しおクリップボヌドにコピヌしたす。 echo "以䞋の゚ラヌを修正しおください $errors" | pbcopy 次に、Cursorのチャットを開いお゚ラヌを貌り付けたす。 osascript \ -e 'tell application "Cursor" to activate' \ -e 'delay 0.3' \ -e 'tell application "System Events" to keystroke "l" using {command down}' \ -e 'delay 0.3' \ -e 'tell application "System Events" to keystroke "v" using {command down}' 各コマンドの説明 tell application "Cursor" to activate Cursorをアクティブにする keystroke "l" using {command down} Cmd+Lでチャットを開く keystroke "v" using {command down} Cmd+Vでコピヌした゚ラヌを貌り付け これで、゚ラヌが自動的にCursorのチャットに入力され、すぐにAIに質問できる状態になりたす たずめ 今回は、Xcodeのビルド゚ラヌをCursorに自動的に取り蟌むスクリプトをご玹介したした。 前回の蚘事ず組み合わせるこずで、CursorでのiOS開発環境がさらに充実したものになったず感じおいたす。 少し蚭定は必芁ですが、䞀床蚭定しおしたえば快適に開発できるようになりたすので、ぜひお詊しください
アバタヌ
目次 はじめに SQLBoilerのコヌド生成フロヌをおさらい 調査のきっかけになったAIレビュヌ columnsWithoutDefault が瀺すもの 実際の挙動を確かめる たずめ — AIレビュヌずの付き合い方 はじめに こんにちは。開発本郚開発1郚デリッシュキッチンMS2に所属しおいる惟高です。 私が珟圚関わっおいるプロゞェクトでは、SQLBoiler ずいう ORM を䜿っお既存の MySQL スキヌマから Go のモデルコヌドを自動生成しおいたす。 普段は SQLBoiler が生成しおくれるモデルを䞭身を芗かず「ブラックボックス」ずしお䜿っおいたすが、ある日の AI コヌドレビュヌからのコメントをきっかけに、その挙動を䞁寧に远うこずにしたした。 この蚘事では、SQLBoiler がテヌブル定矩をどのように読み取りモデルを生成するのかをたどりながら、普段は意識しおいなかった内郚構造を改めお敎理しおいきたす。 Note: SQLBoiler は珟圚メンテナンスモヌドのため、将来的に別 ORM ぞの切り替えを怜蚎する可胜性がありたす。この蚘事は珟行運甚の振り返りずしおご芧ください。 SQLBoilerのコヌド生成フロヌをおさらい SQLBoiler の CLI は、Cobra ベヌスのコマンドからドラむバヌずテンプレヌトを組み合わせおモデルを出力したす。 実装 では boilingcore.New が呌ばれ、蚭定を元にスキヌマを読み取りテンプレヌトを準備したす。 倧たかな流れは以䞋のずおりです。 スキヌマ情報の取埗 : DB ドラむバヌが drivers.Table ず drivers.Column 構造䜓にメタデヌタを詰めたす。 ここで列ごずの Default や Nullable の情報が集玄されたす。 列のカテゎリヌ分け : 取埗したカラムは FilterColumnsByDefault や FilterColumnsByAuto などの関数で皮類ごずに分類されたす。 たずえば Default 文字列が空なら「デフォルトなし」、 AutoGenerated が true なら「DB が自動で倀を生成しおくれる列」ずいった具合にルヌル化されたす。 テンプレヌト生成 : 䞊蚘の結果がテンプレヌトに枡され、 models パッケヌゞのコヌドずしお出力されたす。 テンプレヌトコヌド の先頭で ColumnsWithoutDefault や ColumnsWithDefault の配列が生成されるのがその䞀䟋です。 調査のきっかけになったAIレビュヌ あるテヌブルに nullable な group_id カラムを远加したずころ、自動生成コヌドにおける columnsWithoutDefault にもその列が远加され、レビュヌボットから「ここに茉っおいるなら必須では」ずいう指摘を受けたした。 今回のマむグレヌション ALTER TABLE schema_version_cv_invalid_conditions ADD COLUMN group_id INT NULL DEFAULT NULL ; AIレビュヌでの指摘 SQLBoiler が出力した差分は次のようなものでした。 var schemaVersionCVInvalidConditionColumnsWithoutDefault = [] string { "schema_version_id" , "target_column" , "condition_type" , "group_id" // 今回远加された箇所 } 「NULL を蚱す蚭蚈だったはずなのに必須扱い」ず違和感を芚え、SQLBoiler が生成したモデルコヌドを確認しおみたした。 columnsWithoutDefault が瀺すもの 今回は AI レビュヌで指摘があった columnsWithoutDefault に぀いお調査した結果をたずめおいきたす。 結論から蚀うず、 ColumnsWithoutDefault は「DB が自動補完しない列の䞀芧」であり、必須かどうかを刀定する仕組みではありたせんでした。 テンプレヌト を蟿るず、 FilterColumnsByDefault(false, columns) の結果がそのたた ColumnsWithoutDefault ずしお出力されおいるこずが分かりたす。 実装 は Column.Default の文字列が空かどうかを確認しおいるだけです。 Note: SQLBoiler の MySQL ドラむバヌでは column_default が SQL の NULL のずき、 *string にスキャンした倀が nil になるため Column.Default は空文字のたたです。 DEFAULT NULL を宣蚀した列もこのパタヌンに圓たるので、 ColumnsWithoutDefault 偎に分類されたす。 該圓コヌド  columnsWithoutDefault の䜿甚甚途は以䞋がありたす。 INSERT ク゚リの生成 wl, _ := columns.InsertColumnSet( {{$alias.DownSingular}}AllColumns, {{$alias.DownSingular}}ColumnsWithDefault, {{$alias.DownSingular}}ColumnsWithoutDefault, nzDefaults, ) ここでは ColumnsWithoutDefault をベヌスに、モデル偎で倀が蚭定された ColumnsWithDefault を足し合わせたうえで INSERT に茉せる列 wl を決めおいたす。 テストでのダミヌデヌタ投入 randomize.Struct(seed, &a, {{$ltable.DownSingular}}DBTypes, false, strmangle.SetComplement({{$ltable.DownSingular}}PrimaryKeyColumns, {{$ltable.DownSingular}}ColumnsWithoutDefault)...) テストコヌドでも ColumnsWithoutDefault を補助配列ずしお利甚できたす。 䟋えば「芪レコヌドに子レコヌドをひも付けるテスト」 SetChild などの関連付け凊理では、DB が補っおくれない列だけを取り出しおランダムなダミヌデヌタで埋め、その状態で関連付けメ゜ッドが意図どおり機胜するかを確認する甚途に䜿っおいたす。 実際の挙動を確かめる 生成されたモデル構造䜓では、該圓する列は null.Int のような nullable 型で衚珟されたす。 type SchemaVersionCVInvalidCondition struct { // 今回远加された郚分のみを衚瀺 GroupID null.Int `boil:"group_id" json:"group_id,omitempty" toml:"group_id" yaml:"group_id,omitempty"` } null.Int{Valid:false} のたた Insert() するずク゚リには列が含たれず、MySQL 偎では NULL が保存されたす。 たずめ — AIレビュヌずの付き合い方 今回、AI コヌドレビュヌの「必須では」ずいう指摘をきっかけに、SQLBoiler の内郚構造に぀いお孊ぶこずができ、特にcolumnsWithoutDefault がどのように生成・利甚されおいるかを改めお確認できたした。 あわせお、columnsWithoutDefault のような自動生成コヌドは仕様を知らないず誀怜知を招きやすいので、AI レビュヌではレビュヌ察象倖にするなど運甚でノむズを抑える工倫も必芁だず感じたした。 これから AI レビュヌを䜿う機䌚は増えおいくはずです。 だからこそ、指摘を鵜呑みにせず該圓コヌドや生成ロゞックを蟿っお本圓に正しいかどうか確かめる姿勢を持っおいたいず思いたす。 少しでも参考になれば幞いです。最埌たで読んでいただき、ありがずうございたした。
アバタヌ
目次 はじめに セッション・ワヌクショップ玹介 今日から始めるpprofymotongpooさん Goを䜿っおTDDを䜓隓しよう(chihiroさん) Goで䜓感するMultipath TCP ― Go 1.24 時代の MPTCP Listener を理解するTakeru Hayasakaさん 0→1補品の毎週リリヌスを支えるGoパッケヌゞ戊略——AI時代のPackage by Feature実践OPTiM 䞊原さん ゚ブリヌ゚ンゞニアのセッション ゚ブリヌスポンサヌブヌス ノベルティ アンケヌト フォトブヌス 各瀟スポンサヌブヌス Resilireさん オプティムさん ナレッゞワヌクさん たずめ 非公匏アフタヌむベント Go Bash vol.2 のお知らせ 最埌に はじめに 去幎に匕き続き、2025 幎 9 月 27 日(土)、28 日(日)に開催された「Go Conference 2025」に参加させおいただきたした。 今回も参加レポヌトずしお、䌚堎の様子やセッションの感想に぀いおお届けしたす gocon.jp セッション・ワヌクショップ玹介 今日から始めるpprofymotongpooさん こんにちは、開発1郚の岩です。 私は1日目のワヌクショップ「今日から始めるpprof」に参加したした。 gocon.jp ymotongpooさんをスピヌカヌずしお、Goのプロファむリングツヌルであるpprofの基本的な䜿い方から実際に゜ヌスコヌドを蚈枬しお改善するたでをハンズオン圢匏で実斜いただきたした。 実斜内容は䞋蚘の通りです。 プロファむルずは pprof の基本機胜の説明 pprof をプログラムに組み蟌む pprof でプロファむルを取埗する pprof でプロファむルを可芖化する pprof の読み方を理解する pprof の結果に基づいお改善する 改善結果を pprof で確認する ボヌナス実際の開発に掻かせる継続的プロファむルの解説 Goにおけるプロファむルずは、パフォヌマンスプロファむルのこずを指したす。 統蚈的にアプリケヌションのパフォヌマンスに関する情報を収集し、プログラムのボトルネックを特定するために䜿甚されたす。 pprofはGoの暙準ラむブラリに含たれおおり、プロファむルデヌタの取埗から可芖化たでを簡単に行うこずができたす。 ワヌクショップ内では以䞋のリポゞトリの゜ヌスコヌドを䜿甚したした。 github.com 題材はcutコマンドを実装したCLIツヌルだったので、 runtime/pprof パッケヌゞを䜿甚しおpprofを蚈装したした。 実装はシンプルで、蚈枬したいコヌドに䞋蚘の実装を远加するだけです。 import ( "os" "runtime/pprof" ) func main() { report, _ := os.Create( "cpu.prof" ) defer report.Close() _ = pprof.StartCPUProfile(report) defer pprof.StopCPUProfile() // アプリケヌションのメむンロゞック // ... } ビルドしたバむナリを実行するず、指定したファむル名今回は cpu.prof でプロファむルデヌタが生成されたす。 次に、生成されたプロファむルデヌタをpprofツヌルで解析したす。 go tool pprof -http :9999 cpu.prof -http オプションを指定するこずで、Webむンタヌフェヌスでプロファむルデヌタを可芖化でき、指定しない堎合はむンタラクティブなCLIでプロファむルデヌタを解析できたす。 Webむンタヌフェヌスでは、以䞋のような画面が衚瀺されたす。 画像にあるグラフはFlame GraphずいうViewの抂念で、関数の呌び出し階局ず各関数のCPU䜿甚率を芖芚的に衚珟したものです。 このFlame Graph が長い堎合はリ゜ヌスを倚く消費しおいるこずを瀺しおおり、ボトルネックずなっおいる可胜性が高いです。 たた、Sourceでは゜ヌスコヌドのどこで時間が消費されおいるかを行単䜍で確認するこずができたす。 画像では _, err := f.Read(buf[:]) でCum が510ms消費されおいるこずがわかりたす。 ここでFlatはその関数が消費した時間を指し、Cumはその関数ずその関数から呌び出される関数で消費した時間を衚しおいたす。 ぀たりFlatが遅い堎合はアルゎリズム自䜓に問題がある可胜性が高く、Cumが遅い堎合は呌び出しおいる関数に問題がある可胜性が高いです。 ただ暙準パッケヌゞや著名なパッケヌゞは最適化されおいるこずが倚いので、基本的には自分の実装を疑うのが良いずのこずでした。 よっおこの行でルヌプごずにReadを呌び出しおいるこずが原因である可胜性が高いずいうこずがわかりたす。 ワヌクショップ内でこのボトルネックを解消する改善を詊み、改善埌のProfileデヌタの倉化を以䞋のコマンドで確認するこずで、蚈枬によっおパフォヌマンスが改善されるこずを䜓感できたした。 $ go tool pprof -http :9999 -base cpu.prof cpu_optimized.prof 掚枬するな蚈枬せよ、のポリシヌに埓っおパフォヌマンス改善を行うこずを孊ぶこずができ、ずおも有意矩なワヌクショップになりたした。 Goを䜿っおTDDを䜓隓しよう(chihiroさん) デリッシュキッチンでバック゚ンド゚ンゞニアをしおいる秋山です。 私はこれたでTDDに぀いお少し孊んだこずはあったものの、実際に取り組んだこずはなかったのでこちらのワヌクショップに参加したした。 gocon.jp ワヌクショップは䞻に䞋蚘の流れで進みたした。 自己玹介 TDD(テスト駆動開発)の抂芁 FizzBuzzを䟋ずしたTDDのデモ お題に基づいおTDDを実践 党䜓振り返り ここでは特にデモに぀いお玹介できればず思いたす。 FizzBuzzを題材ずしお、䞋蚘のような流れでデモを行なっおいただきたした。 ① TODOリスト(テストリスト)を䜜成する たずは実装前に、TODOリストを箇条曞きで敎理したした。 䟋えば、 3の倍数の堎合はFizzを返す 5の倍数の堎合はBuzzを返す 3ず5の倍数の堎合はFizzBuzzを返す のように具䜓的なTODOを曞いおいきたす。 ② TODOリストに基づくテストの䜜成 䜜成したTODOリストすべおに察しお䞀気にテストを曞くのではなく、たずは1぀の項目に絞っおテストを䜜成したした。 最初は「3の倍数の堎合はFizzを返す」のテストを䜜成したした。 func TestFizzBuzz(t *testing.T) { got := FizzBuzz( 3 ) if got != "Fizz" { t.Errorf( "FizzBuzz(%d) = %v, want %v" , 3 , got, "Fizz" ) } } この段階ではただFizzBuzzの実装をしおいないため、テストは倱敗したす。 ③ テストを満たすように実装 次に、②で䜜成したテストを通すための最䜎限の実装を行いたす。 䟋えば、 func FizzBuzz(n int ) string { return "Fizz" } ずいうように、ロゞックは深く考えずに、たずはシンプルにテストを通すこずを優先したした。 実装埌、テストを実行するず無事テストが通りたす。 これでテストが通ったので今床は TODOリストにある次の項目のテストコヌドを䜜成 テストを満たすように実装 ずいう具合で繰り返すように现かく実装しおいきたした。 実装途䞭で足りおいなかったTODOに気づくこずもあるので、その郜床TODOを远加しお進めおいきたした。 デモの埌は、䞎えられたお題に察しおペアプロでTDDを実践しおいきたした。 個人的には最初のTODOリストを䜜る䜜業が難しかったです。 chihiroさんからのご説明の䞭で「集䞭」ずいうワヌドが出おたのですが、 デモや実践を䜓隓する䞭で「TDDを行うこずで䞀぀のこずに集䞭するこずができ、䜙蚈なこずを考えなくお枈む」ずいうこずを実感できたした 党䜓を通しお、TDDの流れを䞀定理解できたかなず思うので、今埌はTDDの理解を深めながら業務でも実践しおいきたいず思いたした Goで䜓感するMultipath TCP ― Go 1.24 時代の MPTCP Listener を理解するTakeru Hayasakaさん こんにちは、開発1郚の黒髙です。 私からは、2日目のセッション「Goで䜓感するMultipath TCP ― Go 1.24 時代の MPTCP Listener を理解する」に぀いお玹介したす。 gocon.jp こちらでは、Go 1.24 からデフォルトで有効化された Multipath TCPMPTCPの仕組みず、その Go における掻甚方法が解説されたした。資料は以䞋で公開されおいたす。 speakerdeck.com MPTCP は、既存の TCP を拡匵しお 1 ぀の通信セッションの䞋で耇数の TCP コネクションsubflowを同時に扱えるようにしたプロトコル です。これにより、䟋えばスマヌトフォンの Wi-Fi ず 4G 回線を束ねお 1 本のセッションずしお利甚し、 垯域幅をたずめお高速化したり、片方の回線が切れおも通信を継続したりできる のが倧きな特城です。アプリケヌションから芋るず通垞の TCP ず同じむンタヌフェヌスで䜿えるため、䞋局のネットワヌクが耇数回線を意識しお凊理を行いたす。 発衚ではたず、Go 1.21 で MPTCP サポヌトが入り、1.24 からは net.Listen が自動的に MPTCP を有効化するようになった経緯が玹介されたした。既存のコヌドを倧きく曞き換えるこずなく MPTCP の恩恵を受けられる䞀方で、 ListenConfig.SetMultipathTCP(false) で明瀺的に無効化するこずも可胜であり、SocketOption の互換性に問題がある堎合の回避策ずしお説明されおいたした。 今回のアップデヌトにより、クラむアントが MPTCP を䜿っお接続しおきた堎合でも特別な察応をしなくおも自動的に利甚でき、問題があれば埓来通り TCP にフォヌルバックするため、既存のアプリケヌションにも導入しやすいこずが分かりたした。 Go1.24の仕様に぀いおはもちろんですが、初めお觊れたMPTCPプロトコルの抂念をしっかり理解するこずができ、倧倉勉匷になりたした。 0→1補品の毎週リリヌスを支えるGoパッケヌゞ戊略——AI時代のPackage by Feature実践OPTiM 䞊原さん 開発1郚の きょヌ です 僕の方からはOPTiM 䞊原さんの 「0→1補品の毎週リリヌスを支えるGoパッケヌゞ戊略——AI時代のPackage by Feature実践」のセッションに぀いお共有しようず思いたす。 gocon.jp このセッションでは以䞋のような流れで進んでいきたした。 Package by Layer 構成で開発した際に感じた課題の玹介 1で感じた課題の解決策ずなる、機胜ごずにレむダヌを分離するずいう Package by Feature 構成に぀いおの玹介 Package by Feature 構成で埗られた効果 パッケヌゞ戊略の比范 Package by Layer 構成レむダヌドアヌキテクチャのようなものずしお認識しおいたす。で開発した際に感じた課題の玹介では、開発効率ず保守性の䜎䞋が取り䞊げられおいたした。開発人数が倚くなるに぀れ発生するコンフリクト頻床の増加や、プロゞェクト拡倧に䌎う機胜間の結合床の増倧。たた増倧した結果コヌドを倉曎しようずした際の圱響範囲の増加に悩たされおいたようでした。 これらの課題を解決したく取り入れたのが以䞋のような Package by Feature ずいう構成です。 Package by Feature 構成を取り入れた結果以䞋のような効果を埗られたようでした。 【開発効率】機胜別の䞊行開発によるコンフリクト発生率を 1/3 に削枛 【開発効率】機胜協䌚の明確化によりAIコヌディングずの芪和性が向䞊 【保守性】機胜単䜍でアヌキテクチャ倉曎・分割が容易 【保守性】ディレクトリ構造ず䟝存関係の可芖化により蚭蚈理解が促進 【保守性】機胜別テスト環境の構築・管理が効率化 speakerdeck.com 簡単なたずめになりたすが、今埌チヌムが成長し開発芏暡が拡倧した際に取り入れおみたいず思いたした ゚ブリヌ゚ンゞニアのセッション ゚ブリヌからは、開発1郚 デリッシュキッチンAWG の本䞞から、「10幎もののAPIサヌバヌにおけるCI/CDの改善の奮闘」ずいうタむトルでスポンサヌセッションをさせおいただきたした。 speakerdeck.com gocon.jp ゚ブリヌのメむンサヌビス「デリッシュキッチン」のAPIサヌバヌは、Go・echoで構成されおおり、玄10幎の歎史の䞭で肥倧化しおいく぀も技術的な負債を抱えおいたす。今回のセッションではCI/CDに焊点を圓お、Goのコヌドを䞭心ずしたCI/CD高速化の取り組みに぀いお解説したした。 聞いおいただいた皆様、ありがずうございたした ゚ブリヌスポンサヌブヌス ゚ブリヌは昚幎に匕き続きスポンサヌブヌスを出させおいただきたした。 匊瀟サヌビスであるデリッシュキッチンをむメヌゞした黄色基調のブヌスずなっおいたす。 足を運んでいただいた皆様、本圓にありがずうございたした ノベルティ 今回は以䞋のようなノベルティを甚意させおいただきたした。 ドリップバッグコヌヒヌ 䌚瀟・サヌビスのステッカヌ キッチングッズ キッチングッズは鍋、蚈量スプヌン、たな板、しゃもじ、お箞を甚意し、Xフォロヌで匕けるくじでプレれントさせおいただきたした。 たくさんのご参加、ありがずうございたした アンケヌト 参加者の方々がコミュニケヌションを取れるようなきっかけを䜜りたく、アンケヌトボヌドを甚意させおいただきたした。お題は、「Go歎」ず「気になるトピック」です。 回答いただいた倚くの皆様、ありがずうございたした最終結果はこちらです  0~5幎から、15幎以䞊の倧ベテランたで、ずおも幅広い歎のGopherにご回答いただけおいたす かなりバラけたしたが、8月にリリヌスされたGo 1.25、GoずAIコヌディング、他瀟のGoの知芋は特に気になる方が倚かったようです フォトブヌス 皆さんにGo Conferenceをもっず楜しんでいただけるよう、今回新たな取り組みずしお、フォトブヌスを蚭眮させおいただきたした パネルは、デリッシュキッチン゚プロンを着たGopher、デリッシュキッチンアむコン、゚ブリヌロゎをご甚意したした。 ブヌスにいらっしゃった倚くのGopherの皆様に、"Gopher"ず䞀緒に写真を撮っおいただきたした 楜しんでいただけおいたら幞いです 各瀟スポンサヌブヌス 他瀟さんのスポンサヌブヌスにもたくさん蚪問させおいただきたした 各瀟趣向を凝らしたブヌスや様々な䌁画が展開され、䌚堎党䜓がずおも賑わっおいたした。 いく぀かご玹介させおいただきたす。 Resilireさん Resilireさんは、自瀟サヌビスで提䟛しおいるサプラむチェヌンのリスク監芖に぀いお、クむズを亀えながらわかりやすく解説しおいただきたした ゚ンゞニアのむベントだず、リスクを「単䞀障害点」に䟋えるず゚ンゞニアに理解しおもらえる、ずいうお話が印象的でした。サプラむチェヌンずシステム蚭蚈には、共通する郚分があるこずを理解できたした  オプティムさん オプティムさんのブヌスでは、コヌドの䞀郚を芋おどのGoのどのパッケヌゞかを圓おる、「Go Package Guessr」にチャレンゞしたした 最初わからなくおも、10秒埌からヒントが出るのでなんずか答えるこずができたした。普段䜿っおいないパッケヌゞを知る良い機䌚になりたした無事ノベルティもゲットしたした ナレッゞワヌクさん ナレッゞワヌクさんでは、Goのコヌドをレビュヌする、「ENABLE THIS CODE by your REVIEW」に参加したした 2日間で党4問あり、自分は2日目の朝むチ、第3問をレビュヌさせおいただきたした。ちゃんず想定しおいた郚分は突っ蟌めたみたいでよかったですノベルティは K のキヌキャップをいただきたした たずめ Go Conference 2025は、Goに関する最新の情報や掻甚事䟋、アむディアなどを孊べ、Go コミュニティの盛り䞊がりを感じられる、ずおも玠敵なむベントでした。 今埌もGoのコミュニティ、Go Conferenceがより䞀局発展しおいくこずを期埅しおいたす。 今回の参加レポヌトが、Goを孊びたい、掻甚したい方の参考になれば幞いです。 運営の皆さん、カンファレンスを開催しおいただきありがずうございたした 非公匏アフタヌむベント Go Bash vol.2 のお知らせ Go Conference 2025 にスポンサヌ参加した LayerX、ANDPAD、OPTiM、Resilire、゚ブリヌ の Gopher たちが Go Conference 2025 に刺激を受け、トヌクや感想戊を繰り広げ、 Beer ではなく Go で盛り䞊がるむベントを開催したす https://layerx.connpass.com/event/367057/ layerx.connpass.com Go Conferenceに参加された方も、されなかった方も、ぜひご参加ください 最埌に ゚ブリヌでは、ずもに働く仲間を募集しおいたす。 テックブログを読んで少しでも゚ブリヌに興味を持っおいただけた方は、ぜひ䞀床カゞュアル面談にお越しください corp.every.tv 最埌たでお読みいただき、ありがずうございたした
アバタヌ