Apothegms

001
Reducing Cognitive Entropy is the quintessence of programming.

Uncertainty lies in ourselves: in our ability to understand the software system, understand and use abstractions, process input data, adhere to project rules, communicate and apply methodologies… It’s all about how people think. The cognitive entropy refers to doubts in the understanding of the software system that is being developed. Our goal is to constantly reduce this entropy by making decisions based on data and evidence, and by acknowledging the limits of our cognitive processes. A way to understand the extent of uncertainty is by applying cognitive pressure to parts of the system.

002
Objects do not exist.

Humans tend to simplify complex concepts due to cognitive efficiency – processing complexity requires significant mental energy, encouraging simplification. We’re inclined to perceive the world as composed of discrete objects organized into categories with distinct attributes. In reality, however, the world is not inherently structured this way. What we identify as an object is merely one possible composite of individual parts bonded through interaction, offering specific functionality.

003
Avoid NULLs.

There is no semantic in nothingness. null attempts to signify the absence of a value while paradoxically being assigned as a value itself. Using null undermines the effectiveness of compile-time checks, leading to errors only discoverable during runtime. null acts as an exception to strict type definitions, being a gap within an otherwise robust type system, since it can be assigned to any type. null’s ambiguity further complicates its usage as it implies various states—such as an uninitialized state, a missing value, or an error condition.

004
Avoid impure functions.

Functions with non-deterministic output, known as impure functions, rely on external states, making them inherently dependent on factors outside their scope. This dependency makes understanding impure functions more challenging as their behavior is not determined by the code within the function itself. While impure functions are an integral part of programming, it is advisable to limit their use by either minimizing their number or by managing the side effects. Pure functions serve as the foundation elements of a program. Their deterministic nature and self-contained design allow them to be combined in various ways, facilitating straightforward development due to their predictable behavior.

005
Distinguish between "type" terms.

The term “type” is overloaded, encompassing several distinct meanings. 1) Machine type describes the primitive storage format of a value. 2) (Algebraic) data type is a composite of other types, typically represented as product types (e.g., records) or sum types. Finally, 3) Behavior type describes a state along with operations on that state, often incorrectly implemented as classes.

006
LSP is not about inheritance.

In her paper, Liskov B. defines subtyping through the principles of substitutability and invariance. She begins by formalizing the concept of a (behavioral) type, which focuses on the externally observable behavior of an object rather than its internal structure or implementation. Building on this foundation, she introduces the concept of behavioral subtyping, emphasizing that a derived type must be substitutable for its base type without compromising the program’s correctness.

Notably, the types and subtyping relationships discussed in Liskov’s work diverge significantly from their counterparts in mainstream OOP languages. In OOP, types are conflated with classes, and subtyping is often reduced to a rudimentary form of inheritance. However, classes in this context are not equivalent to behavioral types, and inheritance does not embody the rigorous subtyping relationship Liskov envisioned. Consequently, the so-called Liskov Substitution Principle (LSP)—as it is often cited in OOP—is misaligned with the original ideas presented in her paper, rendering its application in these languages largely superficial.

007
Inheritance is just a redefinition.

Inheritance is the redefinition of functions and variables in a sub-scope. Inheritance is NOT a IS-A relationship.

008
Reality will only exist if it conforms to AI.
009
Favor association when modeling relationships.

There are two ways to model relationships between objects: association and aggregation/composition. Association represents a weaker, natural, relationship: essentially a tuple of objects, such as: <Library, [Book]>. Aggregation, on the other hand, represents a stronger relationship, where Library has a collection of Book.

Use association to model the most relationships between objects. Reserve aggregation for relationships that are defining and integral to the objects involved.

010
Communicate responsibilities in a collective.

In a collective, clear boundaries of responsibility are essential. Each person must be aware of its responsibilities and the responsibilities of others. Responsible person is also one of the decision makers, accountable for the outcome. Lakmus test for determining the responsibilities: do you sleep or wake up when an incident happens in the middle of the night?

011
Avoid a fixation on "how".

In the pursuit of knowledge, shifting focus away from an excessive emphasis on “how” can enhance both learning and creativity. In software development, where there is rarely a single correct or definitive solution, the concept of negative learning often proves more valuable. Guide the process towards a solution by systematically identifying and eliminating ineffective approaches and practices.

012
Class is a template for an object.
013
OOP is simple at beginning. FP is easy.

OOP becomes unnecessarily complex over time. FP remains easy.

014
TDD has no significant value.

TDD is a method of gradually incrementing software functionality toward a desired outcome. The incremental progress is driven by tests, followed by refactoring. TDD emphasizes minimal increments, which promise a self-emerging design. However, this approach can only lead to finding a local maximum—there is no guarantee of reaching the optimal solution. There is no evidence of any observable benefits from practicing TDD. Furthermore, software engineers should already write tests and perform refactoring as part of their daily practice, without needing a formal methodology to enforce these habits.

015
Don't be blinded by skills.

Skilled engineers pride themselves on being problem solvers—give them any tool or methodology, and they’ll find a way to make it work. Skilled engineers dive deep into patterns, constantly learn, and tirelessly improve until they deliver something useful. Their drive to find solutions may overshadow the need to step back and critically evaluate the technologies they’re embracing. We need to pause our problem-solving instincts and ask ourselves: why do we do things the way we do?

016
Avoid primitive obsession

Primitive obsession occurs when primitive data types are used to represent domain concepts. It’s one of the most pervasive and risky code smells because it’s both common and easy to miss—often leading to subtle bugs that only surface at runtime.

Every piece of domain information requires its own dedicated domain type.

017
It's not art.

Software development, programming, coding—whatever you call it—is not art. It’s a craft, a skill honed through practice, and while it can sometimes exude elegance or stir emotions, its essence lies elsewhere. Engineering intersects with science and design, standing in stark contrast to art.

018
SOLID exists purely to make a catchy acronym.
019
Be truthful.

Do not say what you believe to be false. Do not say that for which you lack adequate evidence.

This is about being authentic and transparent. To trust in the communication, each participant needs a sense of the other’s identity and motivations.

020
Verbs for changes, nouns for pures/immutables.

Use action verbs for functions that change the state: reserveBook(). Use nouns for pure or immutable functions: recentBooks().

021
Classes are an inefficient mid-sized abstraction.

A class represents an abstraction that resides in the intermediate scale between functions and modules. This positioning leads to challenges in its effective use for software modeling. Classes are generally too limited in scope to encapsulate complex behaviors, which typically span multiple interacting states. Simultaneously, they are too rigid and cumbersome to support the lightweight composability inherent to functions. Classes are most useful as data containers rather than tools for modeling behavior.

022
I don't know.

A common myth in software engineering is that experience equates to always having immediate answers. This belief is neither accurate nor realistic. Experience does not eliminate uncertainty; rather, it shortens the path to solutions.

Learn. Rethink. Refactor. Repeat until you have enough answers. Then act.

023
SRP is vague, avoid it.

The Single Responsibility Principle claims that a class should have only one reason to change. However, it conflates vague concepts like “responsibility” and “reason for change,” creating more confusion than clarity. Rooted in misinterpretations of David Parnas’ ideas on modularization and system decomposition, SRP oversimplifies complex principles and misguides developers with its binary application, offering an unhelpful abstraction that sacrifices precision.

024
Boring beats clever.

Use boring technologies, with which you are intimately familiar, understanding their nuanced details. This frees your cognitive capacities to concentrate on solving core problems and developing robust domain models. If you wish to expand your skillset, consider selectively adopting a single new technology and integrating it purposefully into a project.

025
The language one uses influences the way one thinks about reality.

The Sapir–Whorf hypothesis (aka the linguistic relativity hypothesis) refers to the proposal that the particular language one speaks influences the way one thinks about reality. The same applies to computer languages. The language is not a simple tool for communication.

026
Solutions are good or bad.

Solutions are not correct or incorrect. Instead, they are good or bad. More precisely, they are either good enough or they are not.

027
Software development is more craft than science.

Given that software development is more craft than science, adopting new or holding to old approaches can be as much about socialization as measurement. With this comes a tendency to dogmatism about methodologies, and to claim improved results (timeliness, quality) without proof or measurement. The promised benefits of any methodology should be met with skepticism and, whenever possible, empirically verified.

🏡     |     oblac?     |     rss     |     kontakt     |     ☕
Sadržaj je dostupan pod CC-BY-4.0 licencom.