Resource leaks are the older sibling of memory leaks. Every open needs a close. Every acquire needs a release. The kernel and the runtime impose hard caps on how many handles a process can have, and a leaky handler under steady load eventually starts returning errors with names like EMFILE or ECONNREFUSED.

The fix is structured cleanup. Go has defer. Python has with. JavaScript/TypeScript has try/finally (or the new using declarations). Java has try-with-resources. Rust has Drop. The common idea: tie the release to a syntactic block so it fires on every exit path, including exceptions.

The bug shows up most often where there's a resource opened and an early return between the open and the close. A linter that flags this pattern (e.g. errcheck for Go, pyflakes for Python, @typescript-eslint/no-floating-promises for streams) catches a surprising amount of it.

Review heuristic

Read every function with an open, connect, acquire, or lock and ask: on every path out of this function — successful return, error return, exception, panic — does the matching close fire? If you can't quickly answer yes for all paths, the cleanup needs to move into a structured construct.