
Eight pull requests, four curated lists
I have written two posts about getting a tool onto Awesome Go — one for pgwd, one for awesome-postgres. Both were about a single PR, and both were true at the time.
They are no longer the whole picture. Across the four curated lists that now carry my tools there are eight merged pull requests, and six of them came after the pgwd PR in September. It is worth writing down how that worked, because the interesting part was never the acceptance — it was the process, and the process is different in every one of those lists.
The email that never got an answer
Before any of that, on 17 May 2026, I did the sensible thing and wrote to a stranger.
The stranger was Miguel Ángel Durán — @midudev, one of the largest technology voices on Spanish YouTube, with an audience I would have been happy to borrow a single day of.
The pitch was careful. gghstats is a CLI and a web app that keeps GitHub traffic history past the fourteen-day window the GitHub API exposes. The email explained that it is designed to be entirely self-hosted, which keeps the data under your own control and the behavior predictable. It linked a Dev.to article walking through the approach, the repository, and a live demo. It asked for feedback on the ecosystem and offered to answer any question about the architecture.
It was a good email. It is still the best example I have of doing this wrong, because I never checked whether it was the right door.
No reply. Not a rejection — rejections I can work with. Silence. I refreshed that inbox for weeks.
Meanwhile, the thing that actually moved was not an email at all. It was pull requests.
That contrast is the reason this post exists.
| The email | The pull requests | |
|---|---|---|
| What I was asking for | Attention, and maybe a mention | One line, in the right place |
| Who decides | One person, no deadline | A maintainer with a checklist |
| What “no” looks like | Silence | A label, a comment, a next step |
| What is left afterwards | Nothing | A line that stays in the README |
An email asks for someone’s personal attention, and it competes with every other email they will ever read. A merged PR is not a request for attention — it is a contribution to a shared artifact, and it asks a maintainer for one minute of judgement instead of an afternoon of theirs.
There is no CEO in awesome-go. There is a volunteer with a checklist that tells them exactly what to look at. That is a much smaller thing to say yes to.
The two audiences are not comparable, and that is the part that took me a while to see. @midudev’s audience is large and general. The audience of a list with 193,000 stars is specific: people who are, right now, hunting for something that already exists. Reach is not relevance. I was writing to the biggest room in the building about a tool for one desk.
The email produced nothing measurable. The four lists still have my lines in them.
The map
| List | Stars | Tool | Section | PR | Merged |
|---|---|---|---|---|---|
| Awesome Go | ~193,000 | pgwd | Database → Database Tools | #6702 | 2026-09-20 |
| Awesome Go | ~193,000 | gghstats | Third-party APIs | #6761 | 2026-10-08 |
| Awesome Postgres | ~12,000 | pgwd | Monitoring | #532 | 2026-08-31 |
| Awesome Kubernetes Tools | ~1,070 | groot | — | #58 | 2026-09-30 |
| Awesome Kubernetes Tools | ~1,070 | kzero | — | #59 | 2026-09-30 |
| Awesome SRE Tools | ~1,500 | groot | Incident Management / Incident Response / IT Alerting / On-Call | #190 | 2026-10-12 |
| Awesome SRE Tools | ~1,500 | kzero | Continuous Delivery → Deployment | #191 | 2026-10-12 |
| Awesome SRE Tools | ~1,500 | gghstats | Continuous Monitoring | #192 | 2026-10-12 |
Four lists, eight entries, eight PRs, four tools. The interesting thing is that no two of those lists work the same way.
One package per pull request
The rule that appears in Awesome Go and is worth internalizing everywhere: one PR adds one package. Not two.
So when groot and kzero were both ready, they were still two PRs — groot as #58 and kzero as #59, opened minutes apart on the same afternoon. The same is true of the three Awesome SRE Tools PRs, which went in as #190, #191 and #192 on 12 October.
This is not bureaucracy. A PR that adds one line to one list is reviewable in under a minute by a maintainer doing this in their spare time. A PR that adds three lines across three tools is a diff they have to reason about, and the answer is usually no.
Alphabetical, or bottom — pick the list’s rule, not yours
Here is where I got it wrong the first time, and the difference is instructive.
Awesome Go sorts alphabetically. Not a suggestion — there is a CI check for it, and the PR body has to state the placement explicitly:
Placement — Third-party APIs, between
geo-golangandgithub.
Awesome SRE Tools does the opposite. Its contributing guide says, in full:
- Add your listing at the bottom of each section.
So every one of those three PRs ends with:
Placement — at the bottom of Continuous Monitoring (per CONTRIBUTING).
Same goal, opposite mechanics. If you had submitted the SRE Tools PRs with alphabetical placement, a maintainer would have had to move them and that is friction you handed to a volunteer. The whole job is to make the review trivial.
This is what the merged gghstats line looks like in the Awesome Go README — one entry, in the slot the placement rule asked for, between geo-golang and github:

Awesome Kubernetes Tools is somewhere in between: the entries live in a data/repos file and the PR body says the addition goes alphabetically between hjacobs/kube-web-view and hyscale/hyscale. Notice what that phrasing implies — both of my entries landed adjacent to each other, groot and kzero, in that exact slot, because that was where the h section ended.
The checklist is the real contribution guide
Every one of those lists publishes a checklist, and the checklists are where the actual requirements live. Awesome Go’s is the strictest:
- open source license from an allowed list
- at least five months of history since the first commit
- a
vX.Y.Zrelease sogo.modcan pin it - documentation in English, with Go-style doc headers on public functions and types
- coverage of at least 80% — with the note that the tests will be reviewed manually, because a coverage number that only comes from benchmarks does not count
- regular recent commits, or responses to issues within two weeks for finished projects
And then the part that most submitters get wrong, because it is a single sentence in a very long document:
- The description is clear, concise, non-promotional, and ends with a period.
The blurb is the entire pitch. There is no room for “blazing fast” or “the best way to”. My gghstats entry is:
gghstats - Self-hosted GitHub traffic dashboard that stores clones and views in SQLite beyond the 14-day API window.
That is the whole pitch, and it is checkable. The README can be as long as it likes. The list gets one line, and that one line has to survive a maintainer reading it cold.
The false positive that was not one
The part I did not expect was the bot.
The gghstats PR passed almost everything on the first pass: repo accessible, go.mod present, SemVer release, pkg.go.dev reachable, MIT license detected, five months of maturity, GitHub Actions detected, README present, only README.md modified, single item, link text matching the repo name, description ending in a period, no promotional language detected, category size of 115 items.
One check failed:
⚠️ Coverage: missing from PR body
The codecov link was in the PR body. It was right there, in the checklist, checked:
- [x] Coverage service link: https://app.codecov.io/gh/hrodrig/gghstats
The bot’s regex simply did not match it — the same false positive other submitters hit. So I left a note on the PR explaining that the link was present and that this was the pattern, not the submission:
Coverage was in the PR body: https://app.codecov.io/gh/hrodrig/gghstats — same false-positive regex as other submissions; leaving this note for the
needs-coveragelabel.
The PR carries a needs-coverage label and merged anyway, with quality:ok. A human read the note and made the call.
That is what the label is for: a request for a human’s attention, not a rejection. And the whole philosophy of these lists is in that one exchange. The automated checks catch the mechanical failures so a maintainer can spend their attention on the judgment calls — is this genuinely useful, is it in the right category, is the description honest.
Writing the entry for the category, not for yourself
The placement question came up for real with gghstats, because gghstats is not a library. It is a self-hosted dashboard and CLI that syncs the GitHub traffic API into SQLite so you keep history past the fourteen-day window GitHub shows you. Why does that belong in a list of Go packages?
Because “Third-party APIs” is not about libraries — it is about clients for someone else’s API, and gghstats is a client for the GitHub API. It is the same shelf where go-github and githubql live.
The alternative was filing it under “Command Line” or “Database”, which would have hidden what it actually is. A slightly awkward category that is honest beats a flattering one that is wrong.
The same thing happened with groot in Awesome SRE Tools, where the section is literally named Incident Management / Incident Response / IT Alerting / On-Call. That is a precise description of what groot is: a read-only diagnostics CLI that collects pod logs, events, and API context into one .tar.gz archive. When the category name is a perfect fit, you have to wonder whether you are being objective or just lucky.
And kzero went into Continuous Delivery → Deployment, which is honest — it is a declarative down/up/reset CLI for bastion maintenance playbooks. The description says “scale, Helm, PVC, jobs”. That is a deploy-adjacent operation on an existing workload, which is what that section is for.
What actually took the time
None of it was the waiting. The waiting was a few days for Awesome Go and minutes for the others.
What took the time was reading four contributing guides carefully enough to notice that two of them wanted opposite things, and then writing descriptions that would survive a cold read by someone who has never seen your project and will not click the link.
What I would tell someone starting out:
- Read the contributing guide, not the README. The README tells you what the list is. The guide tells you what will get you in.
- Find the exact insertion point before you write. “Alphabetically between X and Y” and “bottom of section” are different asks, and only one of them is right for the list you are in.
- Write the description last, and write it flat. Say what it does. Stop.
- One package per PR, always.
- If a bot flags something that is clearly there, say so in the PR. A one-line note naming the exact URL is faster for a maintainer than re-checking your whole submission. The
needs-coveragelabel is a request for a human; give the human what they need. - Do not wait for the merge to start the next one. Once the pattern is understood, the second and third PRs are quick.
- Do not confuse reach with relevance. If you write to someone with a large audience, you are asking for a favor. If you open a pull request against a list that people actually scan looking for your kind of tool, you are asking for a review. Only one of those has a checklist attached.
None of these tools are large. groot, kzero, pgwd and gghstats are all single binaries that do one specific thing because a specific thing was annoying. That is exactly what these lists are for: someone hunting for something that already exists, at two in the morning, instead of writing it again.
And it is worth saying the quiet part: an eight-PR haul is not a growth strategy. It is just what happens when you stop looking for a stage and start looking for the person who would actually use the thing.
If you have a tool like that, the contribution guide is worth an afternoon.