The Cost of Imprecision in Software Design Writing clean code isn't just about aesthetics; it's about being mathematically and logically precise with your data. When developers write imprecise code, they inadvertently create systems that are harder to test, difficult to reuse, and prone to breaking during refactoring. Precision means being mindful of exactly what data a function requires and what it promises to return. By tightening these constraints, you reduce cognitive load for your teammates and ensure your software remains flexible as it scales. Avoiding the Gorilla and the Jungle A common architectural trap is requesting more data than a function actually needs. This violates the **Law of Demeter** (the principle of least knowledge). For example, if a function needs a coordinate but you pass it an entire `Location` object containing metadata, timestamps, and history, you've over-coupled that function. You cannot use it elsewhere without providing that heavy object. As Joe Armstrong, the creator of Erlang, famously noted, you wanted a banana, but you got a gorilla holding the banana and the entire jungle. The solution is to pass the specific sub-component—like a `GeoLocation`—to keep the function focused and decoupled. The Logic of Strict Input and Single-Type Returns Functions that accept a "grab bag" of types (Union types like `str | int | bytes`) often seem convenient but lead to internal complexity. These functions require internal `if/else` logic to handle every variation, complicating your unit tests. Instead, strive for strict input types. If you need convenience, use factory methods like `from_int` or `from_string` to convert data before it hits your core logic. Similarly, return types should be predictable. Returning a `URI` object or a `bool` (for failure) forces the caller to write boilerplate error handling everywhere. A more precise approach uses exceptions. By raising a custom exception for invalid data and returning only the success type, you simplify the calling code from a dozen lines of checks to a clean, three-line execution flow. Leveraging Generic Parameters and Specific Returns While inputs should be strict in *value*, they should be generic in *capability*. In Python, instead of requiring a `list`, you should often require an `Iterable` or `Sized` type. This allows your function to accept tuples, sets, or even custom objects without changing a line of code. However, the return type should remain highly specific. By returning a `list` rather than a generic `Iterable`, you tell the caller exactly what methods (like `.append()` or indexing) are available. This asymmetry—generic inputs and specific outputs—is a hallmark of professional-grade software design.
Law of Demeter
Concepts
- Jul 14, 2023
- Nov 11, 2022
- Oct 7, 2022
- Apr 15, 2022