All five of the top hotspots in jackc/pgx live in pgconn/pgconn.go: connectOne, CopyFrom, NextResult, CopyTo and CancelRequest. Anyone touching connection setup, COPY, batch result reading or query cancellation is working inside the same high-risk module. pgx is a Go PostgreSQL driver, and my analysis of commit adf514b covers 1,610 functions, 203 of them in the critical band. I would start with connectOne: cyclomatic complexity of 106, fan-out of 42, and an activity-weighted risk score of 3.37 in a file touched 49 times in the last year, most recently 3 days ago. That makes it a strong candidate for early review.
The table below ranks functions by activity-weighted risk — a score that multiplies structural complexity by recent commit frequency. A function that is both hard to understand (high cyclomatic complexity) and actively changing is a higher priority than one that is complex but untouched. CC = cyclomatic complexity (independent execution paths); ND = max nesting depth; FO = fan-out (distinct callees).
Top 5 Hotspots
| Function | File | Risk | CC | ND | FO |
|---|---|---|---|---|---|
connectOne | pgconn/pgconn.go | 3.4 | 106 | 5 | 42 |
CopyFrom | pgconn/pgconn.go | 3.2 | 41 | 3 | 29 |
NextResult | pgconn/pgconn.go | 3.2 | 27 | 4 | 10 |
CopyTo | pgconn/pgconn.go | 3.2 | 26 | 3 | 15 |
CancelRequest | pgconn/pgconn.go | 3.2 | 21 | 3 | 21 |
The file is the story here, so I will start with the tier picture and then go function by function.
1,610 functions analyzed
Across the repository, 774 functions are in the active tier and 836 are quiet. All five top hotspots are active. The file they share has 49 touches in the last year, 3 days since its last change, and 6 distinct authors in the last 90 days. Those figures are file-level, so every function below shares them. What separates the five is structure, not activity. The file-level history also shows 34 total commits with a bug-fix rate of 32.35%, no bug-linked commits and no reverts. I read that as context for prioritizing review, not as evidence that any particular function is defective.
connectOne — pgconn.go
Cyclomatic complexity of 106 is well into the extreme range, and fan-out of 42 means it directly calls 42 distinct functions. From the excerpt, it handles the whole connection handshake in one body: protocol version parsing, dialing, TLS negotiation (direct or via a start-TLS step), the AfterNetConnect hook, context watcher setup, and the start of the startup sequence. Nearly every step has its own error return, so the branch count comes from many sequential failure paths rather than from deep nesting. Nesting depth is 5, the highest of the five.
The excerpt also has a comment about a race: the application-supplied context watcher handler may read fields on the connection that this function writes while handling backend key data, so a deadline-only watcher is used during connect. Go concurrency hazards like that are not visible in structural metrics, and they are the kind of constraint a 106-path function makes easy to break by accident.
My recommendation is to extract the stages into named helpers: protocol-version resolution, TLS upgrade, and the startup message loop. Pull the TLS block out first, since the same TLS setup logic also appears in CancelRequest. Keep the watcher-ordering comment attached to whichever helper owns that sequencing.
CopyFrom — pgconn.go
CopyFrom has cyclomatic complexity of 41 and fan-out of 29. The excerpt shows a function that takes the connection lock, sends the COPY query, spawns a goroutine that reads from the supplied reader and writes encoded copy data, and then runs a select loop over an error channel and a message-signal channel in the calling goroutine. A comment in the code explains why the writer goroutine cannot use the normal async close path: it would race with the main goroutine.
This is the clearest case in the list where metrics undersell the risk. Cyclomatic complexity counts branches, but not the interleaving of two goroutines sharing one connection. I would start with this function if I were reviewing for correctness rather than size, because every change to error handling has to be reasoned about on both sides of the channel.
My recommendation is to split the writer goroutine body into a named method on a small copy-writer type, so its error and abort contract can be tested in isolation. The select loop can then stay in CopyFrom with a much lower branch count.
NextResult — pgconn.go
NextResult on MultiResultReader has cyclomatic complexity of 27 and nesting depth of 4, with a low fan-out of 10. That profile says the difficulty is in control flow, not coupling. The excerpt shows a loop that peeks at messages and then a type switch over protocol message kinds such as BindComplete, RowDescription and CommandComplete. The comments describe a queue of statement descriptions and result formats that has to stay aligned with the server’s message order, including for commands that return no rows.
That bookkeeping is the risky part. It tracks state across loop iterations, so a mistake in one case branch can misalign every later result in a batch. I would be most wary of edits here that add a new message type without a test covering the empty-result case.
My recommendation is to move each case of the type switch into its own small method, and consider a tiny struct for the statement-description and result-format queue so the pop operation lives in one place.
CopyTo — pgconn.go
CopyTo has cyclomatic complexity of 26 and fan-out of 15. It is the mirror image of CopyFrom: it sends a COPY query, then loops receiving messages and writing copy data to the supplied writer until ReadyForQuery arrives. Unlike CopyFrom, it does not use a deferred unlock. The excerpt shows explicit unlock calls on several return paths and no unlock on the asyncClose error paths.
I am not claiming that is a defect. The excerpt does not show enough to say, and the asymmetry may be intentional. But a function whose lock release is spread across multiple return paths is harder to change safely than one that defers it, and with 26 paths that is a review burden.
My recommendation is to read this function side by side with CopyFrom and decide deliberately whether the two should share a locking convention. Then extract the message-switch into a helper so the loop body shrinks.
CancelRequest — pgconn.go
CancelRequest has cyclomatic complexity of 21 and fan-out of 21, the highest fan-out relative to complexity of the five. It opens a separate connection to the server, with special handling for unix sockets and a fallback to configured host and port, then optionally wraps that connection in TLS to mirror the primary connection, writes a cancel packet containing the backend process ID and secret key, and waits for the server to acknowledge.
Because the excerpt includes a TLS setup path very close to the one in connectOne, I would check whether the two can share a helper. Duplicated security-relevant setup is a place where one copy tends to get updated and the other does not.
My recommendation is to extract the address-resolution logic (the unix-socket branch and its fallback dial) into its own function. That alone removes several branches and makes the cancel packet construction readable on its own.
Where to start
I would review pgconn/pgconn.go as one module. First, connectOne: extract the TLS upgrade and protocol-version steps, since they have the highest branch count and are shared in spirit with CancelRequest. Second, CopyFrom, where the goroutine and channel interplay deserves a focused concurrency review regardless of its branch count. Third, align the locking approach in CopyTo with CopyFrom, or document why they differ. The file-level history shows no reverts and no bug-linked commits, so I read this as maintainability debt in fast-moving code rather than a known problem area.
For contrast, the next tier down in my data is all quiet: high-complexity functions elsewhere in the repository that have rarely changed in the last year. They are structural debt, but they are not where I would spend review time first.
Reproduce This Analysis
git clone https://github.com/jackc/pgx
cd pgx
git checkout adf514b283858adc96a816b60ade2b424cd57768
hotspots analyze . --mode snapshot --force
To run the same analysis on your own codebase, run hotspots analyze . --mode snapshot in any local git repo — no configuration required.
I use Hotspots to highlight structural and activity risk — not “bad code.” I treat these findings as a prioritization aid, not a bug predictor. Editorial policy →