{"id":29116603,"url":"https://github.com/bobg/decouple","last_synced_at":"2026-03-12T14:17:02.034Z","repository":{"id":64411333,"uuid":"574258687","full_name":"bobg/decouple","owner":"bobg","description":null,"archived":false,"fork":false,"pushed_at":"2025-12-05T22:49:57.000Z","size":70,"stargazers_count":32,"open_issues_count":3,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-12-09T12:41:47.828Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bobg.png","metadata":{"files":{"readme":"Readme.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2022-12-04T22:28:15.000Z","updated_at":"2025-12-05T22:49:38.000Z","dependencies_parsed_at":"2024-01-02T00:26:06.202Z","dependency_job_id":"bcffdbff-8b82-4b23-af76-41cfe038e335","html_url":"https://github.com/bobg/decouple","commit_stats":null,"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"purl":"pkg:github/bobg/decouple","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bobg%2Fdecouple","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bobg%2Fdecouple/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bobg%2Fdecouple/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bobg%2Fdecouple/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bobg","download_url":"https://codeload.github.com/bobg/decouple/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bobg%2Fdecouple/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30428013,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-12T14:00:25.264Z","status":"ssl_error","status_checked_at":"2026-03-12T13:59:52.690Z","response_time":114,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-06-29T11:13:42.873Z","updated_at":"2026-03-12T14:17:02.025Z","avatar_url":"https://github.com/bobg.png","language":"Go","readme":"# Decouple - find overspecified function parameters in Go code\n\n[![Go Reference](https://pkg.go.dev/badge/github.com/bobg/decouple.svg)](https://pkg.go.dev/github.com/bobg/decouple)\n[![Go Report Card](https://goreportcard.com/badge/github.com/bobg/decouple)](https://goreportcard.com/report/github.com/bobg/decouple)\n[![Tests](https://github.com/bobg/decouple/actions/workflows/go.yml/badge.svg)](https://github.com/bobg/decouple/actions/workflows/go.yml)\n[![Coverage Status](https://coveralls.io/repos/github/bobg/decouple/badge.svg?branch=main)](https://coveralls.io/github/bobg/decouple?branch=main)\n[![Mentioned in Awesome Go](https://awesome.re/mentioned-badge.svg)](https://github.com/avelino/awesome-go)\n\nThis is decouple,\na Go package and command that analyzes your Go code\nto find “overspecified” function parameters.\n\nA parameter is overspecified,\nand eligible for “decoupling,”\nif it has a more-specific type than it actually needs.\n\nFor example,\nif your function takes a `*os.File` parameter,\nbut it’s only ever used for its `Read` method,\nit could be specified as an abstract `io.Reader` instead.\n\n## Why decouple?\n\nWhen you decouple a function parameter from its too-specific type,\nyou broaden the set of values on which it can operate.\n\nYou also make it easier to test.\nFor a simple example,\nsuppose you’re testing this function:\n\n```go\nfunc CountLines(f *os.File) (int, error) {\n  var result int\n  sc := bufio.NewScanner(f)\n  for sc.Scan() {\n    result++\n  }\n  return result, sc.Err()\n}\n```\n\nYour unit test will need to open a testdata file and pass it to this function to get a result.\nBut as `decouple` can tell you,\n`f` is only ever used as an `io.Reader`\n(the type of the argument to [bufio.NewScanner](https://pkg.go.dev/bufio#NewScanner)).\n\nIf you were testing `func CountLines(r io.Reader) (int, error)` instead,\nthe unit test can simply pass it something like `strings.NewReader(\"a\\nb\\nc\")`.\n\n## Installation\n\n```sh\ngo install github.com/bobg/decouple/cmd/decouple@latest\n```\n\n## Usage\n\n```sh\ndecouple [-v] [-json] [DIR]\n```\n\nThis produces a report about the Go packages rooted at DIR\n(the current directory by default).\nWith -v,\nvery verbose debugging output is printed along the way.\nWith -json,\nthe output is in JSON format.\n\nThe report will be empty if decouple has no findings.\nOtherwise, it will look something like this (without -json):\n\n```\n$ decouple\n/home/bobg/kodigcs/handle.go:105:18: handleDir\n    req: [Context]\n    w: io.Writer\n/home/bobg/kodigcs/handle.go:167:18: handleNFO\n    req: [Context]\n    w: [Header Write]\n/home/bobg/kodigcs/handle.go:428:6: isStale\n    t: [Before]\n/home/bobg/kodigcs/imdb.go:59:6: parseIMDbPage\n    cl: [Do]\n```\n\nThis is the output when running decouple on [the current commit](https://github.com/bobg/kodigcs/commit/f4e8cf0e44de0ea98fa7ad4f88705324ff446444)\nof [kodigcs](https://github.com/bobg/kodigcs).\nIt’s saying that:\n\n- In the function [handleDir](https://github.com/bobg/kodigcs/blob/f4e8cf0e44de0ea98fa7ad4f88705324ff446444/handle.go#L105),\n  the `req` parameter is being used only for its `Context` method\n  and so could be declared as `interface{ Context() context.Context }`,\n  allowing objects other than `*http.Request` values to be passed in here\n  (or, better still, the function could be rewritten to take a `context.Context` parameter instead);\n- Also in [handleDir](https://github.com/bobg/kodigcs/blob/f4e8cf0e44de0ea98fa7ad4f88705324ff446444/handle.go#L105),\n  `w` could be an `io.Writer`,\n  allowing more types to be used than just `http.ResponseWriter`;\n- Similarly in [handleNFO](https://github.com/bobg/kodigcs/blob/f4e8cf0e44de0ea98fa7ad4f88705324ff446444/handle.go#L167),\n  `req` is used only for its `Context` method,\n  and `w` for its `Write` and `Header` methods\n  (more than `io.Writer`, but less than `http.ResponseWriter`);\n- Anything with a `Before(time.Time) bool` method\n  could be used in [isStale](https://github.com/bobg/kodigcs/blob/f4e8cf0e44de0ea98fa7ad4f88705324ff446444/handle.go#L428),\n  it does not need to be limited to `time.Time`;\n- The `*http.Client` argument of [parseIMDbPage](https://github.com/bobg/kodigcs/blob/f4e8cf0e44de0ea98fa7ad4f88705324ff446444/imdb.go#L59)\n  is being used only for its `Do` method.\n\nNote that,\nin the report,\nthe presence of square brackets means “this is a set of methods,”\nwhile the absence of them means “this is an existing type that already has the right method set”\n(as in the `io.Writer` line in the example above).\nDecouple can’t always find a suitable existing type even when one exists,\nand if two or more types match,\nit doesn’t always choose the best one.\n\nThe same report with `-json` specified looks like this:\n\n```\n{\n  \"PackageName\": \"main\",\n  \"FileName\": \"/home/bobg/kodigcs/handle.go\",\n  \"Line\": 105,\n  \"Column\": 18,\n  \"FuncName\": \"handleDir\",\n  \"Params\": [\n    {\n      \"Name\": \"req\",\n      \"Methods\": [\n        \"Context\"\n      ]\n    },\n    {\n      \"Name\": \"w\",\n      \"Methods\": [\n        \"Write\"\n      ],\n      \"InterfaceName\": \"Writer\",\n      \"InterfacePkg\": \"io\"\n    }\n  ]\n}\n{\n  \"PackageName\": \"main\",\n  \"FileName\": \"/home/bobg/kodigcs/handle.go\",\n  \"Line\": 167,\n  \"Column\": 18,\n  \"FuncName\": \"handleNFO\",\n  \"Params\": [\n    {\n      \"Name\": \"req\",\n      \"Methods\": [\n        \"Context\"\n      ]\n    },\n    {\n      \"Name\": \"w\",\n      \"Methods\": [\n        \"Header\",\n        \"Write\"\n      ]\n    }\n  ]\n}\n{\n  \"PackageName\": \"main\",\n  \"FileName\": \"/home/bobg/kodigcs/handle.go\",\n  \"Line\": 428,\n  \"Column\": 6,\n  \"FuncName\": \"isStale\",\n  \"Params\": [\n    {\n      \"Name\": \"t\",\n      \"Methods\": [\n        \"Before\"\n      ]\n    }\n  ]\n}\n{\n  \"PackageName\": \"main\",\n  \"FileName\": \"/home/bobg/kodigcs/imdb.go\",\n  \"Line\": 59,\n  \"Column\": 6,\n  \"FuncName\": \"parseIMDbPage\",\n  \"Params\": [\n    {\n      \"Name\": \"cl\",\n      \"Methods\": [\n        \"Do\"\n      ]\n    }\n  ]\n}\n```\n\n## Performance note\n\nReplacing overspecified function parameters with more-abstract ones,\nwhich this tool helps you to do,\nis often but not always the right thing,\nand it should not be done blindly.\n\nUsing Go interfaces can impose an _abstraction penalty_ compared to using concrete types.\nFunction arguments that could have been on [the stack](https://en.wikipedia.org/wiki/Stack-based_memory_allocation)\nmay end up in [the heap](https://en.wikipedia.org/wiki/Heap-based_memory_allocation),\nand method calls may involve [a virtual-dispatch step](https://en.wikipedia.org/wiki/Dynamic_dispatch).\n\nIn many cases this penalty is small and can be ignored,\nespecially since the Go compiler may optimize some or all of it away.\nBut in tight inner loops\nand other performance-critical code\nit is often preferable to operate only on concrete types when possible.\n\nThat said,\navoid the fallacy of [premature optimization](https://wiki.c2.com/?PrematureOptimization).\nWrite your code for clarity and utility first.\nThen sacrifice those for the sake of performance\nnot in the places where you _think_ they’ll make a difference,\nbut in the places where you’ve _measured_ that they’re needed.\n","funding_links":[],"categories":["Go Tools","Go 工具"],"sub_categories":["Routers","Search and Analytic Databases","路由器"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbobg%2Fdecouple","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbobg%2Fdecouple","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbobg%2Fdecouple/lists"}