There is a particular brand of misfortune that only a certain kind of person truly knows — the kind who stumbles upon an archived repository promising to hold the implementation of some rare, elegant algorithm, only to discover, many hours and a great deal of quiet despair later, that the promises were hollow. I have lived that misfortune firsthand. And having once walked the halls of pure science before wandering into programming, I arrived with no inherited instinct for the labyrinthine beauty of a large, poorly-documented codebase. What I found instead was a masterclass in how complexity compounds itself in silence.
The first few imports seem innocent enough. Then the rabbit hole opens. One module calls another, which calls three more, each of which quietly assumes the existence of some version of some package that may or may not still exist, may or may not have changed its API somewhere between then and now. You follow the trail. You use the AI tools — and they are genuinely good tools, to be fair — but even they eventually shrug, because the fault is not in any single line; it is woven into the very fabric of how the thing was assembled. Long hours pass. The kind that blur into one another. When things finally click, there is, I will admit, an almost unreasonable joy to it. But more often than not, the clicking never comes.
When all else fails, you download a local copy of the module, hunt for definitions, trace variable names, follow classes — only to discover a function that was never quite finished.
And here is where I must confess something: I am part of the problem. I have done what the industry encouraged me to do. I broke code into functions. I abstracted away complexity. I drew neat little boundaries around messy logic and called it good architecture. I followed the gospel of modularity with something close to genuine belief. The structure looked right. The folder hierarchy was pleasing. The function names were descriptive. And yet, somewhere in that cathedral of good intentions, the actual thought — the raw, living algorithm — was buried so deep beneath layers of abstraction that not even its author could have excavated it cleanly on short notice.
The Good Scientist, the Bad Programmer
I have met brilliant researchers who would hear all of this and offer a calm, entirely reasonable defence: "Ends justify the means. I do not wish to spend weeks optimising code that merely needs to run once." And honestly? There is something honest in that. Science moves fast and its practitioners have neither the mandate nor, often, the time to be craftsmen of code. That might make you a great scientist. It does, however, make for a rather dangerous programmer — not because the code is wrong, but because it becomes, in time, genuinely unknowable.
Does maintaining structure and standards truly lead to greater productivity for the community over the long run? More often than not, yes — but we cannot measure the damage done by the codebases that slowly erode trust, waste time, and silently exclude the very people they were meant to serve.
Consider the newcomer. They approach a repository with good faith and genuine need. They read what passes for documentation. They import, they implement, they run — and then something breaks. Not dramatically. Not with a clear error message pointing to a culprit. It breaks in that quiet, ambiguous way that makes you wonder if the problem is the code, or your understanding of it, or the version of Python you are running, or some combination of all three and something else entirely. They descend. They open files. They click through to function definitions. They begin to build, in their head, a chart of all the variable names and method calls and class relationships that must somehow cohere into a working whole.
That chart becomes too large. The human brain, magnificent as it is, has a working memory. And when the map of dependencies exceeds it, something gives way — not the code, but the person trying to understand it.
The Page Worth of Algorithm
Here is a thing I have noticed: if you sit a thoughtful programmer down and ask them to simply describe what all these scattered blocks of code actually do — to strip away the architecture and name the logic — the answer almost always fits on a page. A single page. Sometimes less. The algorithm itself is rarely the problem. It is the scaffolding built around it that becomes the fog.
When the map of dependencies exceeds what a mind can hold at once, it is not the code that breaks — it is the person trying to understand it.
When pydantic arrived, I was genuinely delighted. The type safety, the clarity of structure, the way it imposed a kind of discipline on the chaos of Python's dynamic nature — it felt like a civilising influence. And it still is, in the right hands. But the right hands is the operative phrase. In an era of AI-generated code proliferating through repositories faster than anyone can audit it, pydantic schemas become yet another layer between you and the truth of what a system does. Unless you built it yourself, unless you watched it grow from nothing, understanding the full shape of the thing becomes an act of archaeology rather than engineering.
The Right Tool in the Wrong Hands
This is not an argument against structure. Structure matters enormously. The problem is that structure, unmoored from understanding, becomes a different kind of trap — not the chaos of unorganised code, but the false reassurance of organised opacity. Things look right. The folders are labelled. The types are checked. And yet the actual behaviour of the system remains, to anyone outside its original authors, essentially unknowable without significant investment of time that most real-world situations simply do not afford.
Time. That is the resource that cuts everything. In the rush to implement, to ship, to close the ticket, to meet the deadline, we do not ask why something works. We ask only whether it does. And that is a rational response to an irrational amount of pressure. But it creates a kind of technical debt that is not measured in lines of code or test coverage — it is measured in the accumulated confusion of every person who ever had to inherit something they did not write.
Bad practices spread like a slow pandemic. They do not announce themselves. They are adopted because they look like good practices, because they were used by someone respected, because they appear in popular repositories, because the AI suggestion looked clean. And then, quietly, they are everywhere.
The Cheat Sheet Manifesto
So here is what I wish for, not as policy or mandate, but as a quiet, earnest hope: that every repository, every little piece of work released into the world, might come with its bones visible. Not necessarily the full architecture document, not the exhaustive wiki — though those are lovely when they exist — but simply a bare-bones explanation. A page. A plain-language description of what the algorithm does, what assumptions it makes, what inputs it expects and what outputs it produces. Written as though you were handing your working notes to a curious friend.
A cheat sheet. That is all. Something that says: here is the shape of the idea before we dressed it in classes and wrapped it in interfaces. Here is what I was actually trying to do. That document — humble, informal, perhaps a little rough around the edges — might do more to preserve the usefulness of a codebase than any amount of linting or type annotation.
Releasing your bare-bones logic to the world is simply handing your practice notes across — a simple cheat sheet that might finally super-glue the gloriously flawed house of cards we call programming.
Programming is, at its core, a community endeavour. The code you write today will be read by someone else — possibly someone with less context, less time, less familiarity with the particular idioms you favour. The care you put into making it legible, not just syntactically correct but genuinely comprehensible, is a gift to that person. It is also, in a more pragmatic sense, insurance against the moment when that person is you, returning to your own work six months later and finding it as foreign as someone else's.
Even with tighter deadlines, increasing pressure, and every incentive pushing towards speed over clarity, the work of evangelising good documentation, releasing standardised notes alongside code, offering that one-page description of the logic — it is worth it. Not in some distant, theoretical future. Now. Every time.
I do not expect perfection. I am not asking for it. I am asking only for honesty — the intellectual honesty to admit that the structure we build around an idea is not the same as the idea itself, and that both deserve to be preserved. The algorithm and the architecture. The thought and the scaffolding. Let both be visible, and we might just build something that lasts.
Six Patterns That Break Codebases
The Import Rabbit Hole
You import one thing. It imports five. Those import twelve. No one knows where the logic actually lives.
# main.py from core.engine import Processor # core/engine.py imports from core/base.py # core/base.py imports from utils/transforms.py # utils/transforms.py imports from utils/validators.py # utils/validators.py imports from config/schema.py # config/schema.py imports from config/defaults.py # ... 6 files deep before any actual logic p = Processor() result = p.run(data) # Which version of numpy does this need? # Why does it fail silently on Python 3.11? # Nobody knows. The author archived this in 2021.
# processor.py — everything in one readable file # Dependencies: numpy>=1.23, python>=3.9 # Algorithm: sliding-window cosine similarity import numpy as np # only real dependency def run(data: list[float], window: int = 5) -> list[float]: """ Compute rolling cosine similarity. data : 1-D list of floats window : frame size (default 5) returns: similarity scores, length = len(data)-window """ arr = np.array(data) return [ np.dot(arr[i:i+window], arr[i+1:i+window+1]) / (np.linalg.norm(arr[i:i+window]) * np.linalg.norm(arr[i+1:i+window+1]) + 1e-9) for i in range(len(arr) - window) ]
The Partially Implemented Function
The method exists. It has a docstring. It raises NotImplementedError at runtime — buried six calls deep.
class SignalProcessor: def normalise(self, signal): """Normalise input signal to [-1, 1].""" # TODO: handle edge case where signal is flat mn, mx = min(signal), max(signal) return [(x - mn) / (mx - mn) for x in signal] def denoise(self, signal, method='wavelet'): """Remove noise using specified method.""" if method == 'wavelet': raise NotImplementedError( "wavelet denoising not yet implemented" ) elif method == 'moving_avg': raise NotImplementedError( "moving average not yet implemented" ) # only 'none' actually works return signal def run_pipeline(self, data): # calls normalise → denoise → ... crashes at denoise return self.denoise(self.normalise(data))
def normalise(signal: list[float]) -> list[float]: """Scale signal to [0, 1]. Returns unchanged if flat.""" mn, mx = min(signal), max(signal) if mx == mn: return [0.0] * len(signal) # edge case handled return [(x - mn) / (mx - mn) for x in signal] def moving_avg_denoise(signal: list[float], k: int = 3) -> list[float]: """ Simple moving-average denoiser (only method available). k : window half-width. Larger k = more smoothing. Note: wavelet denoising is not implemented. """ result = [] for i in range(len(signal)): lo, hi = max(0, i-k), min(len(signal), i+k+1) result.append(sum(signal[lo:hi]) / (hi - lo)) return result # ── PIPELINE ────────────────────────────────── # normalise → moving_avg_denoise → downstream # Both steps are complete and tested.
Over-Abstraction
A simple algorithm wrapped in so many layers of "good architecture" that the actual logic is invisible.
class AbstractBaseTransformer(ABC): @abstractmethod def transform(self, payload: TransformPayload) -> TransformResult: ... class TransformPayload(BaseModel): data: list[float] config: TransformConfig class TransformConfig(BaseModel): window_size: int = 5 strategy: StrategyEnum = StrategyEnum.DEFAULT class DefaultTransformer(AbstractBaseTransformer): def transform(self, payload: TransformPayload) -> TransformResult: handler = TransformHandlerFactory.get(payload.config.strategy) return handler.execute(payload) # ... 8 more files to find where addition actually happens # The algorithm: a[i] = a[i] + a[i-1] (prefix sum)
# Algorithm: prefix sum # Given [1, 2, 3, 4] → returns [1, 3, 6, 10] # Each output[i] = sum of all inputs up to index i def prefix_sum(data: list[float]) -> list[float]: result, total = [], 0.0 for x in data: total += x result.append(total) return result # That's it. That's the whole thing. # Wrap it in a class only if you have 3+ related # functions that genuinely share state.
Documentation — The Cheat Sheet
What a README looks like when it's written to impress vs. written to actually help.
## SignalKit A robust, extensible, production-grade signal processing toolkit built with modern Python best practices, full type safety via Pydantic v2, and a plugin-based architecture for maximum flexibility. ### Features - Modular transformer pipeline - Abstract factory pattern - Pydantic v2 schema validation - Async-ready architecture - 94% test coverage ### Installation pip install signalkit ### Usage # (see examples/ directory) ← examples/ directory is empty
## SignalKit — What This Does Two functions for 1-D signal cleaning: 1. normalise(signal) → scales values to [0, 1] 2. moving_avg_denoise(signal, k=3) → smooths noise ## The Algorithm (plain English) Normalise: shift the minimum to 0, stretch the range to 1. Denoise: replace each point with the average of its k neighbours. Pipeline: normalise first, then denoise. Order matters. ## Requirements Python 3.9+ No external dependencies. ## Quickstart from signalkit import normalise, moving_avg_denoise clean = moving_avg_denoise(normalise([3,1,4,1,5,9])) ## Known Gaps - Wavelet denoising: NOT implemented (stub exists, raises error) - No GPU support
AI-Generated Slop vs. Deliberate Clarity
The same task — validate and parse a config file — expressed two very different ways.
from pydantic import BaseModel, Field, validator from typing import Optional, Union, Literal, Annotated from enum import Enum class ModeEnum(str, Enum): fast = "fast"; safe = "safe"; balanced = "balanced" class RetryPolicy(BaseModel): max_retries: Annotated[int, Field(ge=0, le=10)] = 3 backoff_factor: float = 1.5 retry_on: list[int] = [500, 502, 503] class ConnectionConfig(BaseModel): host: str port: Annotated[int, Field(gt=0, lt=65536)] timeout: Optional[float] = 30.0 retry: RetryPolicy = Field(default_factory=RetryPolicy) class AppConfig(BaseModel): mode: ModeEnum = ModeEnum.balanced connection: ConnectionConfig debug: bool = False tags: Optional[list[str]] = None @validator('tags') def tags_must_be_lowercase(cls, v): if v: return [t.lower() for t in v] return v # ... 40 more lines of nested validators
# Config loader — reads a JSON/dict and returns # a plain object with sane defaults. # No external dependencies. def load_config(raw: dict) -> dict: """ Expected keys (all optional): host str default "localhost" port int default 8080 (must be 1–65535) timeout float default 30.0 mode str one of fast|safe|balanced, default balanced debug bool default False """ port = int(raw.get("port", 8080)) if not (1 <= port <= 65535): raise ValueError(f"port {port} out of range") mode = raw.get("mode", "balanced") if mode not in {"fast", "safe", "balanced"}: raise ValueError(f"unknown mode: {mode}") return { "host": raw.get("host", "localhost"), "port": port, "mode": mode, "timeout": float(raw.get("timeout", 30.0)), "debug": bool(raw.get("debug", False)), }
The One-Page Algorithm
A real algorithm — k-means clustering — as it lives in a production repo vs. as a bare-bones cheat sheet.
# 11 files. Here is a partial map of what imports what: # # kmeans/ # __init__.py → exports KMeansClusterer # clusterer.py → class KMeansClusterer # initialiser.py → class KPlusPlusInit, RandomInit # distance.py → class EuclideanMetric, CosineMetric # centroid.py → class CentroidUpdater # convergence.py → class ConvergenceChecker # schemas.py → ClusterResult, FitPayload # exceptions.py → KMeansConvergenceError # utils/ # array_ops.py → _normalise, _safe_divide # logging.py → structured_logger # seed.py → seeded_rng # # Total: ~650 lines to express a 20-line algorithm. # clusterer.py calls 6 other files before doing any math.
import random, math """ K-MEANS CLUSTERING — bare bones ================================ 1. Pick k random points as starting centroids. 2. Assign every point to its nearest centroid. 3. Move each centroid to the mean of its assigned points. 4. Repeat 2-3 until assignments stop changing (or max_iter). """ def kmeans(points, k, max_iter=100, seed=42): random.seed(seed) centroids = random.sample(points, k) # step 1 for _ in range(max_iter): # step 2 — assign clusters = [[] for _ in range(k)] for p in points: nearest = min(range(k), key=lambda i: dist(p, centroids[i])) clusters[nearest].append(p) # step 3 — update centroids new_c = [mean(c) if c else centroids[i] for i, c in enumerate(clusters)] if new_c == centroids: break # step 4 centroids = new_c return clusters, centroids def dist(a, b): return math.sqrt(sum((x-y)**2 for x,y in zip(a,b))) def mean(pts): return [sum(x[i] for x in pts)/len(pts) for i in range(len(pts[0]))]
The production repo is not wrong. For a library used at scale, those abstractions earn their weight: swappable distance metrics, structured logging, typed results. That is genuinely useful. BUT — The cheat sheet should ship alongside it. 30 lines. The plain-English steps above the code. Zero imports beyond the standard library. Any reader — regardless of Python fluency — can verify: "Yes, this is k-means. I understand what it does." That confidence is what lets someone trust the larger repo, extend it, debug it, and adapt it to their needs without six hours of archaeology. The cheat sheet is not a replacement. It is an anchor.