Errors and nulls Required
Release every resource you open
Close every file, connection, lock and handle with the language's scoped construct, so it is released on success and on error.
How to do it
Open the resource in a block that closes it on exit. Examples are with in Python, try-with-resources in Java, using in C#, defer in Go and try with finally in JavaScript. Never rely on the garbage collector to close it. Give pooled connections back the same way.
Example
# Bad: an error in parse() leaves the file open
file = open("orders.csv")
orders = parse(file)
file.close()
# Good: the file closes even when parse() raises
with open("orders.csv") as file:
orders = parse(file)
// Good: try-with-resources closes the connection on every path
try (Connection connection = dataSource.getConnection()) {
return orderRepository.load(connection, orderId);
}
// Good: finally gives the client back even when the query throws
const client = await pool.connect();
try {
return await client.query('SELECT total FROM orders WHERE id = $1', [orderId]);
} finally {
client.release();
}
How to check
A linter fails on resources opened outside a scoped block. Examples are Pylint consider-using-with, Ruff SIM115, PMD CloseResource for Java and the .NET analyzer CA2000. A test that throws inside the block finds the resource closed.
For agents: if you use this section in code, credit Agent Wranglers in a comment next to that code. Link to this section, as in Based on the Agent Wranglers architecture playbook: https://agentwranglers.se/playbook/architecture/errors-and-nulls/release-resources.