<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Christie Cosky</title>
    <description>The latest articles on Forem by Christie Cosky (@christiecosky).</description>
    <link>https://forem.com/christiecosky</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3800609%2F6fb89a16-0094-40ec-8ace-f7a267f83f78.jpg</url>
      <title>Forem: Christie Cosky</title>
      <link>https://forem.com/christiecosky</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/christiecosky"/>
    <language>en</language>
    <item>
      <title>Why AI Makes Readability More Important, Not Less</title>
      <dc:creator>Christie Cosky</dc:creator>
      <pubDate>Tue, 19 May 2026 16:00:00 +0000</pubDate>
      <link>https://forem.com/christiecosky/why-ai-makes-readability-more-important-not-less-1lfi</link>
      <guid>https://forem.com/christiecosky/why-ai-makes-readability-more-important-not-less-1lfi</guid>
      <description>&lt;p&gt;Software development has always had an asymmetry: code can be written faster than it can be understood.&lt;/p&gt;

&lt;p&gt;Code is written once but &lt;a href="https://dev.to/christiecosky/readability-is-a-performance-constraint-15fa"&gt;read repeatedly&lt;/a&gt; during reviews, debugging sessions, refactors, and production incidents. The real constraint has always been understanding code rather than typing it.&lt;/p&gt;

&lt;p&gt;AI dramatically widens that asymmetry. Tools like GitHub Copilot and Claude Code can generate thousands of lines of code in minutes, but the speed at which humans understand code hasn't changed.&lt;/p&gt;

&lt;p&gt;Before AI, the speed of writing code naturally constrained how quickly systems could grow. AI removes much of that friction. But human comprehension speed hasn't changed. Systems can now grow faster than teams can understand them — or keep them coherent.&lt;/p&gt;

&lt;p&gt;The asymmetry isn't new, but the magnitude is.&lt;/p&gt;

&lt;h2&gt;
  
  
  Systems Can Now Grow Faster Than Teams Can Understand
&lt;/h2&gt;

&lt;p&gt;Discussions of AI-generated code often focus on correctness or "AI slop." That risk is real. But a larger risk is structural: how quickly disorder can accumulate as systems grow faster than humans can maintain them.&lt;/p&gt;

&lt;p&gt;AI removes much of the friction from writing code. AI can generate large portions of features from a handful of prompts. But every line of code still has to be read, reviewed, debugged, refactored, and extended. Even correct code increases cognitive load, because it still increases the amount of code humans must understand.&lt;/p&gt;

&lt;p&gt;A messy system with 20k lines of code is annoying. A messy system with 500k AI-generated lines of code becomes extremely difficult to understand.&lt;/p&gt;

&lt;p&gt;AI increases code volume while increasing the speed at which structural entropy, like small inconsistencies and duplicated code blocks, can accumulate.&lt;/p&gt;

&lt;p&gt;As that entropy grows, so does the system's cognitive surface area: the amount of structure developers must mentally explore to understand the code.&lt;/p&gt;

&lt;p&gt;As systems grow faster, the team's mental model can struggle to keep up.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Bottleneck Shifts
&lt;/h2&gt;

&lt;p&gt;When developers switch to using AI tools, they spend less time manually writing code and more time analyzing AI-generated code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;reviewing it&lt;/li&gt;
&lt;li&gt;validating that it actually behaves correctly&lt;/li&gt;
&lt;li&gt;integrating it correctly into the system&lt;/li&gt;
&lt;li&gt;refactoring it into something readable&lt;/li&gt;
&lt;li&gt;aligning it with existing patterns in the codebase&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The bottleneck shifts from typing code to understanding, reviewing, and shaping the generated code to fit the system.&lt;/p&gt;

&lt;p&gt;As code volume increases, code review becomes more cognitively demanding. Large AI-generated changes increase review fatigue, making it harder for both the author and the reviewer to fully understand and validate every change.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI Introduces Structural Drift
&lt;/h2&gt;

&lt;p&gt;AI generates code from the local context it sees rather than a full understanding of the system's architecture. As a result, it does not reliably maintain global structural consistency across the system. Small inconsistencies can accumulate across many changes, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;slightly different abstractions&lt;/li&gt;
&lt;li&gt;duplicate logic&lt;/li&gt;
&lt;li&gt;inconsistent naming&lt;/li&gt;
&lt;li&gt;subtle architectural drift&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Individually, these differences are small. But across hundreds of changes they compound.&lt;/p&gt;

&lt;p&gt;Over time, these inconsistencies increase the cognitive entropy — the amount of disorder a reader must make sense of — of the system. The structure becomes harder to recognize. The patterns become less predictable. When patterns become unpredictable, understanding slows down.&lt;/p&gt;

&lt;h2&gt;
  
  
  Humans Maintain Coherence
&lt;/h2&gt;

&lt;p&gt;In addition to validating correctness, developers must ensure AI-generated code fits the system's existing structure and conventions. This maintains system coherence. By coherence, I mean:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The structure of the new code makes sense&lt;/li&gt;
&lt;li&gt;The patterns remain consistent internally and across the system&lt;/li&gt;
&lt;li&gt;The boundaries of methods, classes, and packages remain clear&lt;/li&gt;
&lt;li&gt;Responsibilities remain understandable&lt;/li&gt;
&lt;li&gt;Complexity remains manageable&lt;/li&gt;
&lt;li&gt;Naming remains accurate and meaningful&lt;/li&gt;
&lt;li&gt;Similar problems are solved in similar ways&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Readable code is required to make that possible.&lt;/p&gt;

&lt;p&gt;AI does not automatically maintain system coherence. As codebases grow faster through AI code generation, a larger part of the developer's job becomes counteracting structural drift. Without that effort, the system becomes harder to understand over time, even if every individual piece works.&lt;/p&gt;

&lt;p&gt;Readable structure allows coherence to survive as the system grows.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI Can Also Help Maintain Readability
&lt;/h2&gt;

&lt;p&gt;AI can also help with the work of maintaining coherence. With enough context and a human in the loop to validate their proposals, frontier models are surprisingly good at:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;structural refactors&lt;/li&gt;
&lt;li&gt;naming improvements&lt;/li&gt;
&lt;li&gt;package reorganization&lt;/li&gt;
&lt;li&gt;large mechanical changes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I've had good results asking Claude Code to analyze an architectural layer of a codebase and propose an updated package and class structure based on the business functionality it detects — &lt;a href="https://dev.to/christiecosky/mystery-meat-vs-breadcrumb-systems-4jie"&gt;adding breadcrumbs for me&lt;/a&gt;. (More on using AI to improve a hard-to-navigate codebase in a future article.) It saves time by handling the analysis and tedious cut-and-paste, and the return on investment multiplies every time someone works in that part of the system and benefits from the improved navigability.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Constraint Hasn't Changed
&lt;/h2&gt;

&lt;p&gt;AI dramatically reduces the cost of producing code. But the fundamental constraint in software development has never been typing. It has always been understanding. Human comprehension speed does not scale with code generation speed. &lt;/p&gt;

&lt;p&gt;Whether a system remains understandable depends on its coherence: the consistency of its structure, boundaries, and patterns. Readable structure is what preserves that coherence as a system evolves.&lt;/p&gt;

&lt;p&gt;Some companies are experimenting with AI-driven development loops where agents generate specifications, write implementations, run tests, and even review their own changes. In those environments, humans may not read every line of code. But even highly automated systems still rely on humans when something goes wrong. Production incidents, unexpected behavior, and architectural changes require developers to understand what the system is doing and why.&lt;/p&gt;

&lt;p&gt;Whether code is written manually, with AI assistance, or through AI-driven development loops, understandable systems are still necessary for the humans maintaining them.&lt;/p&gt;

&lt;p&gt;But what actually determines how difficult a system is to understand?&lt;/p&gt;

&lt;p&gt;In my next article, I'll go into more depth about cognitive surface area: the property of a system that determines how much of it developers must mentally explore to understand or change behavior.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;AI was my editor, but these ideas are my own.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;This article is part of a broader series exploring how code structure, navigability, and cohesion align with cognitive limits.&lt;/p&gt;

&lt;p&gt;If you're interested in the deeper dive, the full series is here:&lt;br&gt;
&lt;a href="https://christiecosky.com/series/designing-systems-for-human-brains/" rel="noopener noreferrer"&gt;Designing Systems for Human Brains&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>readability</category>
      <category>maintainability</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Why the Single Responsibility Principle Protects Working Memory</title>
      <dc:creator>Christie Cosky</dc:creator>
      <pubDate>Tue, 12 May 2026 16:00:00 +0000</pubDate>
      <link>https://forem.com/christiecosky/why-the-single-responsibility-principle-protects-working-memory-3ioj</link>
      <guid>https://forem.com/christiecosky/why-the-single-responsibility-principle-protects-working-memory-3ioj</guid>
      <description>&lt;p&gt;In my previous article, Mystery Meat vs. Breadcrumb Systems, I argued that organizing code by feature reduces unnecessary exploration. Feature-based packages make it easier to navigate to the right place in the system.&lt;/p&gt;

&lt;p&gt;But navigation alone doesn't produce comprehension. You can open &lt;code&gt;service.pricing&lt;/code&gt; immediately and still have no idea what you're looking at. Finding the file only solves the location problem. Understanding it requires something else.&lt;/p&gt;

&lt;p&gt;The Single Responsibility Principle is usually framed as a design rule: "a class should have one reason to change." But that framing doesn't explain why classes that follow it are often easier to understand.&lt;/p&gt;

&lt;p&gt;The SRP protects working memory by limiting the number of responsibilities within a boundary. Usually the SRP applies to the boundary of a class, but the same constraint can be applied to packages and methods.&lt;/p&gt;

&lt;p&gt;When a boundary contains one responsibility, understanding is easier. When it mixes responsibilities, reading turns into code simulation. Simulation is time consuming and adds cognitive overhead.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Cost of Cognitive Thrash
&lt;/h2&gt;

&lt;p&gt;Our working memory is limited. We can only hold a small number of conceptual frames in our heads at once.&lt;/p&gt;

&lt;p&gt;By "conceptual frame," I mean the mental model — the mode of reasoning — the reader uses to interpret what the code is doing. Validation logic is read differently than persistence logic. Calculation logic is read differently than logging.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;stable conceptual frame&lt;/strong&gt; is one in which the reader can predict the type of reasoning required without repeatedly shifting mental models.&lt;/p&gt;

&lt;p&gt;Each responsibility activates a conceptual frame. When a boundary contains multiple responsibilities, the reader must repeatedly switch frames. That context switching isn't free. The reader has to set aside one mental model, load another, and preserve the relationships between them.&lt;/p&gt;

&lt;p&gt;That repeated switching creates cognitive thrash.&lt;/p&gt;

&lt;p&gt;Cognitive load theory separates intrinsic load (the complexity of the problem) from extraneous load (the cost of how it's presented). The SRP doesn't reduce intrinsic complexity; pricing rules are still pricing rules. But it reduces extraneous load by limiting unnecessary frame switching inside a boundary.&lt;/p&gt;

&lt;p&gt;Code that follows the SRP reduces context switching by stabilizing the frame contained within each boundary.&lt;/p&gt;

&lt;h2&gt;
  
  
  SRP Across Every Boundary
&lt;/h2&gt;

&lt;p&gt;The SRP can apply to more than just the boundary created by a class definition. Any structural boundary in the system can contain (or mix) conceptual frames.&lt;/p&gt;

&lt;p&gt;At each level, the question is the same:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Does this boundary contain one stable conceptual frame, or does it mix several?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  System Level: Packages
&lt;/h3&gt;

&lt;p&gt;At the package level, we can group features together. Feature-based packages increase cohesion and enable navigation. They reduce the search space and make functionality visible in the structure of the system.&lt;/p&gt;

&lt;p&gt;When packages are organized primarily by framework layer or persistence entity, features get fragmented. Instead of navigating to a visible feature boundary, readers must search and infer relationships across layers.&lt;/p&gt;

&lt;p&gt;At the system level, this determines whether you can find functionality without relying on memory — yours, a teammate's, or the original author's (who left three years ago).&lt;/p&gt;

&lt;p&gt;A cohesive package structure stabilizes feature-level frames; a fragmented structure forces reconstruction and search.&lt;/p&gt;

&lt;h3&gt;
  
  
  Class Level: Objects
&lt;/h3&gt;

&lt;p&gt;At the class level, SRP is commonly defined as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A class should only have one reason to change.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Cognitively, that means a class should represent one coherent responsibility which activates one stable conceptual frame.&lt;/p&gt;

&lt;p&gt;A class that validates, calculates, coordinates, persists, and logs mixes frames. Readers can't rely on the class name or structure to predict the pattern of logic inside. They must simulate execution, trace control flow, and hold multiple responsibilities in working memory.&lt;/p&gt;

&lt;p&gt;When simulation replaces pattern recognition, reading slows down. Every interaction requires reconstruction.&lt;/p&gt;

&lt;p&gt;Classes with a single responsibility contain one frame inside the boundary, so the reader knows what kind of logic to expect, as well as what does &lt;em&gt;not&lt;/em&gt; belong there.&lt;/p&gt;

&lt;p&gt;Good names reinforce that boundary. Class names with cohesive boundaries usually answer one or more of these questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Purpose:&lt;/strong&gt; Why does this class exist? (&lt;code&gt;InvoiceReconciliation*&lt;/code&gt;, &lt;code&gt;OrderFulfillment*&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Role:&lt;/strong&gt; What kind of logic is this? (&lt;code&gt;*Calculator&lt;/code&gt;, &lt;code&gt;*Validator&lt;/code&gt;, &lt;code&gt;*Orchestrator&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Constraint:&lt;/strong&gt; What variant is this? (&lt;code&gt;*Cached*&lt;/code&gt;, &lt;code&gt;*External*&lt;/code&gt;, &lt;code&gt;*ReadOnly*&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mechanism:&lt;/strong&gt; How is it implemented? (&lt;code&gt;*Jdbc*&lt;/code&gt;, &lt;code&gt;*Stripe*&lt;/code&gt;, &lt;code&gt;*Rest*&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The role suffix often signals the one reason the class has to change:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;OrderTotalCalculator&lt;/code&gt; changes if calculations change.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;CheckoutOrchestrator&lt;/code&gt; changes if sequencing changes.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;RefundValidator&lt;/code&gt; changes if validation rules change.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These repeatable naming patterns enable pattern recognition. Pattern recognition works best when frames are stable.&lt;/p&gt;

&lt;p&gt;Names don't create cohesion, but they do constrain what belongs inside the boundary. If a name can't meaningfully exclude logic, the class doesn't represent a single stable frame.&lt;/p&gt;

&lt;p&gt;This is why suffixes like &lt;code&gt;*Service&lt;/code&gt;, &lt;code&gt;*Helper&lt;/code&gt; or &lt;code&gt;*Manager&lt;/code&gt; tend to become junk drawers. They describe broad roles, not stable conceptual frames. Almost anything can fit inside them — and once it does, frame containment erodes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Method Level: Units of Thought
&lt;/h3&gt;

&lt;p&gt;The SRP can apply at the method level too.&lt;/p&gt;

&lt;p&gt;For most of my career, I used helper methods to prevent duplicate code or isolate especially complicated logic.&lt;/p&gt;

&lt;p&gt;Recently, I began using helper methods as &lt;strong&gt;labels for units of thought&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Instead of writing a dense public method with comments preceding each section, I now extract helper methods as labeled steps:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;validateRequest&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;calculateTotals&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;applyDiscounts&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;persistOrder&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The public method becomes a narrative. Each call marks a structural boundary around one stable conceptual frame.&lt;/p&gt;

&lt;p&gt;Instead of holding the entire implementation in working memory, the reader processes one frame at a time.&lt;/p&gt;

&lt;p&gt;I don't do this with every public method; just the ones that benefit from it.&lt;/p&gt;

&lt;p&gt;A simple test:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Does the public method read like a clear sequence of steps after extraction?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If not, the extraction hasn't improved frame containment. Keep the logic inline.&lt;/p&gt;

&lt;h3&gt;
  
  
  When Extraction Backfires
&lt;/h3&gt;

&lt;p&gt;Extraction reduces density but increases indirection.&lt;/p&gt;

&lt;p&gt;A boundary only helps if it contains a stable frame. When extraction forces the reader to jump across multiple files or through layers of trivial delegation, it replaces conceptual compression with navigational friction.&lt;/p&gt;

&lt;p&gt;We want to increase cohesion, not decrease it.&lt;/p&gt;

&lt;p&gt;Warning signs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Methods extracted that don't represent a meaningful step.&lt;/li&gt;
&lt;li&gt;One-line delegations that merely rename trivial logic.&lt;/li&gt;
&lt;li&gt;Control flow that requires navigating through several classes to understand a simple process.&lt;/li&gt;
&lt;li&gt;"Micro-objects" that fragment one coherent idea across multiple files.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If extraction increases frame switching instead of reducing it, the boundary is no longer stabilizing the frame.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Happens When Boundaries Degrade
&lt;/h2&gt;

&lt;p&gt;SRP violations don't just make a single class harder to read; over time, degraded boundaries affect the entire system.&lt;/p&gt;

&lt;h3&gt;
  
  
  Increased Mental Simulation
&lt;/h3&gt;

&lt;p&gt;When a boundary contains multiple responsibilities, skimming stops being safe. Readers can't rely on the name or the structure of the class to predict what belongs inside or what pattern of logic they'll find. They must trace execution step-by-step, juggling multiple conceptual frames in working memory.&lt;/p&gt;

&lt;p&gt;When frames become unstable, recognition becomes unreliable and readers fall back to simulation. Recognition is fast. Simulation is slow. Over time, slow becomes normal.&lt;/p&gt;

&lt;h3&gt;
  
  
  Decreased Code Review Efficacy
&lt;/h3&gt;

&lt;p&gt;At a small scale, one unstable boundary is an annoyance. At a large scale, unstable boundaries create systemic fatigue.&lt;/p&gt;

&lt;p&gt;Reviewers spend more time reconstructing mental models before they can even evaluate correctness. Frame instability forces deeper inspection on every change, and eventually reviewers adapt by conserving effort. They skim when it isn't safe to skim.&lt;/p&gt;

&lt;p&gt;Bugs hide in the noise because unstable boundaries made full understanding too expensive.&lt;/p&gt;

&lt;p&gt;Cognitive thrash spreads beyond the original class and into the review process itself.&lt;/p&gt;

&lt;h3&gt;
  
  
  God Class Gravity
&lt;/h3&gt;

&lt;p&gt;Classes with loosely named boundaries (&lt;code&gt;OrderService&lt;/code&gt;, &lt;code&gt;UserService&lt;/code&gt;) attract unrelated behavior. Because the name doesn't constrain a single responsibility, almost anything fits. The class stops representing a stable conceptual frame and becomes an expanding collection of unrelated behaviors.&lt;/p&gt;

&lt;p&gt;Developers hesitate to introduce new cohesive classes because doing so feels like violating the team's conventions. Entropy wins.&lt;/p&gt;

&lt;p&gt;Boundaries between functionality blur because the "entity service" contains all logic for all features that touch that entity.&lt;/p&gt;

&lt;p&gt;Unit tests grow harder to write, read, and understand. They stop reinforcing a coherent frame and instead mirror the fragmentation of the production code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Knowledge Concentration
&lt;/h3&gt;

&lt;p&gt;When responsibility isn't visible in structure, knowledge concentrates in people instead of boundaries.&lt;/p&gt;

&lt;p&gt;Ownership narrows to the developer who wrote the feature. Others avoid touching or refactoring that code because it feels risky. The bus factor increases.&lt;/p&gt;

&lt;p&gt;Knowledge centralizes, and unreadable structure increases risk concentration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Systemic Entropy
&lt;/h3&gt;

&lt;p&gt;As unstable boundaries multiply, feature boundaries degrade.&lt;/p&gt;

&lt;p&gt;Packages become dumping grounds for everything in that architectural layer. Navigability decreases as the system grows. New functionality gets fragmented to fit the existing structure.&lt;/p&gt;

&lt;p&gt;The cost compounds, and even experienced developers struggle to onboard or safely extend the system because it's difficult to build a reliable mental model of where functionality lives.&lt;/p&gt;

&lt;p&gt;Stable class-level boundaries reinforce package-level boundaries. When cohesion degrades at the class level, navigability degrades globally.&lt;/p&gt;

&lt;p&gt;Systemic drag follows.&lt;/p&gt;

&lt;p&gt;In a read-heavy discipline like software development, drag is lost performance. Readability now limits performance.&lt;/p&gt;

&lt;h2&gt;
  
  
  SRP Reinterpreted
&lt;/h2&gt;

&lt;p&gt;SRP is often defined as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A class should have one reason to change.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Cognitively, the rule could be defined as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A boundary should contain one stable conceptual frame.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Software development is constrained by how quickly a team can find, understand, and safely change code. Cohesive boundaries protect comprehension at every level: package, class, and method.&lt;/p&gt;

&lt;p&gt;The SRP reduces frame switching inside a boundary. That reduction compounds.&lt;/p&gt;

&lt;p&gt;SRP aligns with constraints imposed by human cognition rather than being merely a stylistic preference. And individual cognition does not scale with system size.&lt;/p&gt;

&lt;h2&gt;
  
  
  When Write Velocity Outpaces Cognition
&lt;/h2&gt;

&lt;p&gt;The number of boundaries grows as AI quickly generates code. The only way that growth remains sustainable is if those boundaries preserve frame stability instead of fragmenting it.&lt;/p&gt;

&lt;p&gt;As code volume grows, developers spend more time reviewing and validating than writing. Effective code review relies heavily on pattern recognition. Pattern recognition depends on frame stability.&lt;/p&gt;

&lt;p&gt;If boundaries between frames are unstable, AI amplifies the problem, and it spreads faster than ever.&lt;/p&gt;

&lt;p&gt;When cohesion degrades, reading becomes simulation, simulation becomes cognitive thrash, thrash becomes fatigue, and fatigue becomes risk.&lt;/p&gt;

&lt;p&gt;Applying the SRP across packages, classes and methods can help protect one resource that doesn't scale: human cognition.&lt;/p&gt;

&lt;p&gt;Next time, I'll explore why AI makes readability more important, not less.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;AI was my editor, but these ideas are my own.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;This article is part of a broader series exploring how code structure, navigability, and cohesion align with cognitive limits.&lt;/p&gt;

&lt;p&gt;If you're interested in the deeper dive, the full series is here:&lt;br&gt;
&lt;a href="https://christiecosky.com/series/designing-systems-for-human-brains/" rel="noopener noreferrer"&gt;Designing Systems for Human Brains&lt;/a&gt;&lt;/p&gt;

</description>
      <category>readability</category>
      <category>maintainability</category>
      <category>softwareengineering</category>
      <category>cleancode</category>
    </item>
    <item>
      <title>Mystery Meat vs. Breadcrumb Systems</title>
      <dc:creator>Christie Cosky</dc:creator>
      <pubDate>Tue, 31 Mar 2026 15:00:00 +0000</pubDate>
      <link>https://forem.com/christiecosky/mystery-meat-vs-breadcrumb-systems-4jie</link>
      <guid>https://forem.com/christiecosky/mystery-meat-vs-breadcrumb-systems-4jie</guid>
      <description>&lt;p&gt;It's your first week at a new job. You've checked out the repository and are scanning the package structure. The codebase is organized by &lt;code&gt;*Service&lt;/code&gt;, &lt;code&gt;*Controller&lt;/code&gt;, and &lt;code&gt;*Repository&lt;/code&gt;. Within each layer, files are grouped by persistence entity: &lt;code&gt;Order*&lt;/code&gt;, &lt;code&gt;User*&lt;/code&gt;, &lt;code&gt;Product*&lt;/code&gt;, and so on.&lt;/p&gt;

&lt;p&gt;You've been asked to fix a bug in the pricing logic. Must be in &lt;code&gt;OrderService&lt;/code&gt;, right?&lt;/p&gt;

&lt;p&gt;You open the file. It's 4,000 lines long. Some methods are hundreds of lines.&lt;/p&gt;

&lt;p&gt;You search for &lt;code&gt;pric*&lt;/code&gt;. Not found.&lt;/p&gt;

&lt;p&gt;You search the entire codebase. The word appears in seven files, scattered across variable names, constants, and magic strings. You open the first result and start reading. This doesn't seem right. You move to the next file.&lt;/p&gt;

&lt;p&gt;You can feel the cognitive load accumulating.&lt;/p&gt;

&lt;p&gt;Some systems make the correct starting point obvious. I'll call these &lt;em&gt;breadcrumb systems&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Others require exploration. I'll call these &lt;em&gt;mystery meat systems&lt;/em&gt; — borrowing the term from "mystery meat navigation," where the destination isn't visible until you hover over it. Unfortunately, mystery meat systems aren't unusual. They're common in codebases organized primarily around framework layers and persistence entities.&lt;/p&gt;

&lt;p&gt;Most real systems fall somewhere in between. &lt;/p&gt;

&lt;h2&gt;
  
  
  Readability vs. Navigability
&lt;/h2&gt;

&lt;p&gt;Designing for readability isn't just about how a file reads. It's also about how easily the system can be navigated. Navigation determines whether understanding starts immediately, or only after a search. A system can be perfectly readable and still force exploration.&lt;/p&gt;

&lt;p&gt;Readability answers: &lt;em&gt;"Can I understand this code once I'm in it?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Navigability answers: &lt;em&gt;"Can I find the right code in the first place?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Structure determines whether capability can be found by following visible boundaries or by searching and guessing.&lt;/p&gt;

&lt;p&gt;Exploration isn't inherently bad; it's how we learn new systems, and it's inevitable in legacy code. The problem is when exploration becomes the default mode of interaction: when every task begins with searching instead of navigating. When that happens, cognitive cost accumulates and people slow down. Navigability reduces &lt;em&gt;forced&lt;/em&gt; exploration; it doesn't eliminate learning.&lt;/p&gt;

&lt;p&gt;That distinction sounds subtle, but it changes the experience of working in the system. Designing code for human brains means designing how people find things, not just how they read them.&lt;/p&gt;

&lt;p&gt;When the starting point isn't obvious, you're already using working memory before you even reach the right file. You're guessing where the logic might live, opening files and reading methods that turn out to be irrelevant. The effort adds up. Finding the code becomes part of the work.&lt;/p&gt;

&lt;h2&gt;
  
  
  When Structure Makes Navigation Obvious
&lt;/h2&gt;

&lt;p&gt;So what does navigability look like in practice?&lt;/p&gt;

&lt;p&gt;A system is navigable when a new developer can answer these questions without asking a teammate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Where does this capability live?&lt;/li&gt;
&lt;li&gt;What other parts of the system participate in it?&lt;/li&gt;
&lt;li&gt;What ties those parts together?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If those answers are visible in structure and vocabulary, navigation doesn't depend on tribal knowledge. This means when I'm fixing a pricing bug, I should have a reasonable starting point without asking a teammate or running a full-text search.&lt;/p&gt;

&lt;p&gt;If we want navigation instead of guesswork, capability can't live only in someone's head. It has to be visible in the structure of the codebase.&lt;/p&gt;

&lt;p&gt;At the structural level, visibility shows up in two places:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Capability boundaries: packages or modules that reflect what the system does, not just how it runs.&lt;/li&gt;
&lt;li&gt;Shared vocabulary:  constants, enums, and other stable identifiers that tie related pieces together across classes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When both are present, readers can navigate the codebase without guesswork.&lt;/p&gt;

&lt;p&gt;When those signals are missing, mystery meat systems emerge.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mystery Meat Systems
&lt;/h2&gt;

&lt;p&gt;I've noticed three anti-patterns that create mystery meat systems. Every project I've worked on in my career has included at least two of the three.&lt;/p&gt;

&lt;h3&gt;
  
  
  Anti-Pattern 1: Organization By Framework Layer
&lt;/h3&gt;

&lt;p&gt;Most modern web frameworks encourage a layered structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;controller/
service/
repository/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This separation is useful. It clarifies technical boundaries and keeps HTTP, application logic, and persistence concerns separate.&lt;/p&gt;

&lt;p&gt;But those layers describe &lt;em&gt;how the application runs&lt;/em&gt;, not &lt;em&gt;what the application does&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;They answer questions like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is this handling a request?&lt;/li&gt;
&lt;li&gt;Is this performing application logic?&lt;/li&gt;
&lt;li&gt;Is this reading from the database?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;They don't answer questions like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What feature does this represent?&lt;/li&gt;
&lt;li&gt;Where does the pricing logic live?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Framework layers are technical scaffolding: necessary, but not helpful in showing where a feature lives.&lt;/p&gt;

&lt;p&gt;This isn't an argument against layering or a specific architectural philosophy. It's about navigability: whether the structure makes the right starting point obvious.&lt;/p&gt;

&lt;h3&gt;
  
  
  Anti-Pattern 2: Organization By Persistence Entity
&lt;/h3&gt;

&lt;p&gt;In every project I've worked on, the team took layering a step further and organized within each layer by persistence entity:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;UserController
UserService
UserRepository

OrderController
OrderService
OrderRepository
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That worked for small CRUD systems, but as behavior grew, the business concepts got fragmented across the stack, and the codebase got harder and harder to navigate. Technical layers and persistence entities had become the navigation model, and over time, that model stopped working.&lt;/p&gt;

&lt;p&gt;CRUD is a persistence concern, not a feature.&lt;/p&gt;

&lt;p&gt;Grouping by entity answers the question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Which table does this logic touch?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Grouping by feature answers a different question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What feature does this code implement?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Most real features don't belong to a single table.&lt;/p&gt;

&lt;p&gt;Pricing, for example, may span products, discounts, contracts, and tax rules — none of which cleanly belong to a single persistence entity. Refunds may touch orders, payments, customers, and accounting entries.&lt;/p&gt;

&lt;p&gt;When everything is centered around a persistence entity, functionality tied to a single feature gets fragmented across controllers, services, and repositories. No single place represents the whole idea.&lt;/p&gt;

&lt;p&gt;Centering logic on persistence entities forces exploration. You search and guess instead of navigating to a visible boundary.&lt;/p&gt;

&lt;p&gt;In addition, classes named primarily after persistence entities and architectural roles (like &lt;code&gt;OrderService&lt;/code&gt; or &lt;code&gt;UserController&lt;/code&gt;) tend to accumulate unrelated behavior. Because the name encodes a persistence entity and architectural role rather than a feature, it doesn't constrain what belongs inside. The boundary stops signaling what belongs inside it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Anti-Pattern 3: Missing Shared Vocabulary
&lt;/h3&gt;

&lt;p&gt;Feature boundaries are visible in language too.&lt;/p&gt;

&lt;p&gt;In mystery meat systems, important identifiers aren't encoded consistently. Instead, they appear as scattered hard-coded values embedded directly in conditional statements across multiple files.&lt;/p&gt;

&lt;p&gt;When vocabulary isn't shared, related behavior becomes difficult to trace. Searching for functionality becomes a game of "guess and check." You look for one value, then another. You read the surrounding code to figure out if you're in the right place.&lt;/p&gt;

&lt;p&gt;Without stable identifiers, you don't have a reliable way to track down specific concepts or functionality. This forces exploration instead of navigation: searching and guessing instead of following visible boundaries.&lt;/p&gt;

&lt;p&gt;A codebase without shared vocabulary can't reliably reveal where a feature lives because the language that ties the related pieces together doesn't exist.&lt;/p&gt;

&lt;p&gt;The alternative is to make functionality visible, both structurally and linguistically.&lt;/p&gt;

&lt;h2&gt;
  
  
  Breadcrumb Systems
&lt;/h2&gt;

&lt;p&gt;In contrast, breadcrumb systems give you ways to navigate the system and find functionality without relying on another human. These systems enable navigation by organizing around features and shared vocabulary.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pattern 1: Packages as Feature Boundaries
&lt;/h3&gt;

&lt;p&gt;Once you organize around features instead of architectural layers or tables, packages become clear mental maps.&lt;/p&gt;

&lt;p&gt;A package should answer:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What feature am I in?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You can still keep technical layers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;controller/
service/
repository/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But organize &lt;em&gt;within&lt;/em&gt; them by feature.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;controller/ 
  pricing/ 
    PricingController 

service/ 
  pricing/ 
    PriceCalculator 
    DiscountApplier 
    TaxCalculator 

repository/ 
  pricing/ 
    DiscountRepository 
    TaxRuleRepository 
    PriceListRepository
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now "pricing" is visible across the stack. The feature becomes the breadcrumb you follow through the layers.&lt;/p&gt;

&lt;p&gt;If you're trying to fix a pricing bug, you don't scan every controller or every service. You go directly to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;controller.pricing&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;service.pricing&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;repository.pricing&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In a breadcrumb system, you wouldn't start with &lt;code&gt;OrderService&lt;/code&gt;. You'd start with &lt;code&gt;pricing&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That's one way you get the benefits of proximity. Related behavior lives near related behavior.&lt;/p&gt;

&lt;p&gt;Search can find text. Structure reveals relationships. The starting point becomes obvious, and that alone changes how the system feels to work in.&lt;/p&gt;

&lt;p&gt;When packages reflect features, the system supports navigation instead of requiring exploration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pattern 2: Shared Vocabulary
&lt;/h3&gt;

&lt;p&gt;Features aren't only visible in folders. They're also visible in language.&lt;/p&gt;

&lt;p&gt;In breadcrumb systems, important identifiers are defined once and referenced consistently. Enums and well-scoped constants create stable identifiers for behavior.&lt;/p&gt;

&lt;p&gt;That stability does more than improve readability. It creates structural benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Traceability: related behavior can be located reliably across files.&lt;/li&gt;
&lt;li&gt;Consistency: the same concept is referenced the same way everywhere.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When a concept has a single name and a clear home, you can navigate to its behavior instead of hunting for it.&lt;/p&gt;

&lt;p&gt;Shared vocabulary turns language into infrastructure. It gives the system reliable reference points for its features.&lt;/p&gt;

&lt;p&gt;When identifiers aren't stable or specific, you lose the ability to trace functionality.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where This Goes Wrong
&lt;/h2&gt;

&lt;p&gt;Like any pattern, these can be misapplied.&lt;/p&gt;

&lt;p&gt;Structure improves navigation, but only when it reduces exploration instead of adding to it. We don't want more structure; we want clearer boundaries.&lt;/p&gt;

&lt;p&gt;Every structural improvement carries risk.&lt;/p&gt;

&lt;h3&gt;
  
  
  When Packages Become Taxonomy
&lt;/h3&gt;

&lt;p&gt;Packages should support navigation.&lt;/p&gt;

&lt;p&gt;Large systems will have lots of packages. The number isn't the problem. Unclear grouping that makes navigation harder instead of easier is.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Warning signs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deep nesting without clear feature boundaries&lt;/li&gt;
&lt;li&gt;Folders organized by abstract categories instead of features&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Good structure narrows the search space by giving you a reliable breadcrumb. Poor structure forces you to explore. If structure increases search effort instead of narrowing it, it's working against you.&lt;/p&gt;

&lt;h3&gt;
  
  
  When Shared Vocabulary Fails
&lt;/h3&gt;

&lt;p&gt;Introducing enums and constants isn't enough.  They can be misused, too.  Common failure modes include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All identifiers are centralized in one location without regard for feature boundaries.&lt;/li&gt;
&lt;li&gt;Constants are so abstract they don't convey any feature meaning (&lt;code&gt;TRUE&lt;/code&gt;, &lt;code&gt;ZERO&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Constants are reused across unrelated functionality.&lt;/li&gt;
&lt;li&gt;Prefixes imply cohesion when they're actually unrelated.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In these cases, language creates another layer of indirection instead of reducing exploration. The system looks structured, but it isn't easier to navigate.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Common Thread
&lt;/h3&gt;

&lt;p&gt;Across all of these patterns, the failure is the same: instead of improving navigation, the structure increases indirection.&lt;/p&gt;

&lt;p&gt;The goal isn't to add more folders or identifiers. The goal is to make features visible without requiring someone to trace execution across or search the entire system.&lt;/p&gt;

&lt;p&gt;Good structure narrows the search space and supports navigation. Poor structure expands the search space and pushes readers into guesswork.&lt;/p&gt;

&lt;h2&gt;
  
  
  Breadcrumb Systems Can Emerge Gradually
&lt;/h2&gt;

&lt;p&gt;Most systems don't start out with breadcrumbs.&lt;/p&gt;

&lt;p&gt;On Taz, our system wasn't always organized around feature boundaries. For years, it followed the standard Java/Spring layering pattern — controllers, services, repositories — grouped by persistence entity. That's a common default, and it worked well enough early on.&lt;/p&gt;

&lt;p&gt;But as the system grew, navigation became harder. Behavior related to a single feature was scattered across layers and files. Finding the right starting point required searching and guesswork.&lt;/p&gt;

&lt;p&gt;About ten years in, we started introducing a new package structure organized around feature boundaries. The legacy packages remained layered by entity, but new features were built using feature boundaries. And as we refactored old code, we moved it into the new structure.&lt;/p&gt;

&lt;p&gt;The system didn't flip overnight. For a while, it was part mystery meat, part breadcrumb.&lt;/p&gt;

&lt;p&gt;But over time, the navigable surface area grew. Exploration shrank. The default starting point became clearer. Instead of accumulating entropy, the system gradually became easier to understand.&lt;/p&gt;

&lt;p&gt;You don't need perfect architecture to improve navigability. Even inside a legacy system, you can begin introducing visible feature boundaries and shared vocabulary. Each new boundary reduces search space for the next person who has to read the code.&lt;/p&gt;

&lt;p&gt;Navigability compounds over time. Small structural improvements, repeated consistently, shift how the entire system feels to work in.&lt;/p&gt;

&lt;p&gt;Even if you're currently in a mystery meat system, you can start leaving breadcrumbs now.&lt;/p&gt;

&lt;h2&gt;
  
  
  What About Microservices?
&lt;/h2&gt;

&lt;p&gt;The same principle applies even when the system is distributed.&lt;/p&gt;

&lt;p&gt;In distributed systems, cross-service navigation is inherently harder. Some exploration is unavoidable. But inside each boundary, navigability is still possible.&lt;/p&gt;

&lt;p&gt;You may not control the entire ecosystem. You may not be able to reorganize every microservice. But you can make the ones you touch internally navigable, with visible feature boundaries and shared vocabulary.&lt;/p&gt;

&lt;p&gt;Global coherence may be too lofty a goal, but local coherence will help shrink the search space for the next reader.&lt;/p&gt;

&lt;h2&gt;
  
  
  Navigation Is The First Step
&lt;/h2&gt;

&lt;p&gt;Organizing around feature boundaries makes it possible to find the right part of the system. But finding the right file doesn't mean the file itself is readable.&lt;/p&gt;

&lt;p&gt;Once you're inside a package, the question changes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Does this class make its purpose visible?&lt;/li&gt;
&lt;li&gt;Or do you still have to simulate execution to understand it?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Structure determines whether you can find the code. Cohesion determines whether you can understand it. Navigability gets you to the right room; cohesion determines whether the room makes sense once you're inside it.&lt;/p&gt;

&lt;p&gt;When finding the right code takes longer, every review, bug fix, and feature change slows down because the starting point is unclear.&lt;/p&gt;

&lt;p&gt;If software performance depends on how quickly a team can understand and safely change a system, then navigability becomes part of its performance. The faster the right starting point can be located, the less cognitive overhead a change requires.&lt;/p&gt;

&lt;p&gt;Navigation is step one. Cohesion is step two.&lt;/p&gt;

&lt;p&gt;That leads directly to the Single Responsibility Principle as a design tool for reducing mental load, which will be my next post.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;AI was my editor, but these ideas are my own.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;This article is part of a broader series exploring how code structure, navigability, and cohesion align with cognitive limits.&lt;/p&gt;

&lt;p&gt;If you're interested in the deeper dive, the full series is here:&lt;br&gt;
&lt;a href="https://christiecosky.com/series/designing-systems-for-human-brains/" rel="noopener noreferrer"&gt;Designing Systems for Human Brains&lt;/a&gt;&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>readability</category>
      <category>maintainability</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Readability Is a Performance Constraint</title>
      <dc:creator>Christie Cosky</dc:creator>
      <pubDate>Mon, 02 Mar 2026 00:43:37 +0000</pubDate>
      <link>https://forem.com/christiecosky/readability-is-a-performance-constraint-15fa</link>
      <guid>https://forem.com/christiecosky/readability-is-a-performance-constraint-15fa</guid>
      <description>&lt;p&gt;Most teams obsess over execution speed: build speed, runtime performance, delivery velocity.&lt;/p&gt;

&lt;p&gt;But there's another bottleneck we rarely measure: how long it takes a human to understand the code.&lt;/p&gt;

&lt;p&gt;Open a file during a production incident. The clock is ticking. You're scanning for the bug. If the structure is unclear, you waste time just figuring out what you're looking at.&lt;/p&gt;

&lt;p&gt;Readability is more than style. It's a performance constraint.&lt;/p&gt;

&lt;p&gt;Software development is a read-heavy discipline. Code is written once and read repeatedly — during code reviews, debugging sessions, refactors, feature enhancements, and production incidents, often under time pressure. When code is hard to read, understanding slows down. When understanding slows down, velocity drops and risk increases.&lt;/p&gt;

&lt;p&gt;To be clear, there are contexts where optimizing for speed is rational. But when the code is expected to change and grow over time, readability isn't optional overhead.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance Depends on Reading, Not Writing
&lt;/h2&gt;

&lt;p&gt;We write code once, but we read it for years.&lt;/p&gt;

&lt;p&gt;The majority of software development effort isn't typing new logic; it's reconstructing mental models while reading what already exists. If most engineering time is spent reading and modifying existing code, that's the hot path. Even small reductions in comprehension time compound across reviews, fixes, and feature changes.&lt;/p&gt;

&lt;p&gt;The bottleneck isn't typing speed; it's the speed of reading and understanding. In a read-heavy system, that makes readability a performance constraint, not a stylistic preference.&lt;/p&gt;

&lt;h2&gt;
  
  
  The First Bottleneck Is Perception
&lt;/h2&gt;

&lt;p&gt;We don't read code line-by-line like a compiler. Structure is processed before logic.&lt;/p&gt;

&lt;p&gt;When structure is unclear, working memory fills with extraneous cognitive load, leaving less capacity to reason about the code itself. It's like running too many programs at once: performance drops.&lt;/p&gt;

&lt;p&gt;Unreadable code slows understanding by consuming the cognitive resources required to make correct decisions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Optimizing for Writes Slows the System
&lt;/h2&gt;

&lt;p&gt;In cultures that prize speed above all else, teams often optimize for writes. Code optimized for writes often lacks clarity, and much of the context lives only in the author's head. In these environments, short-term urgency consistently outruns long-term maintainability.&lt;/p&gt;

&lt;p&gt;The cost of re-reading gets pushed downstream — to reviewers, on-call teammates, and future maintainers. They pay the cognitive tax. Over time, that compounds.&lt;/p&gt;

&lt;p&gt;Code optimized for reads may take longer to write, but it shortens time-to-understanding, reduces review friction, and lowers change risk.&lt;/p&gt;

&lt;p&gt;This isn't polishing; it's throughput protection.&lt;/p&gt;

&lt;p&gt;Readability is an up-front investment that shifts cost left. It increases writing time slightly in exchange for reducing every future interaction cost — review time, debugging time, onboarding time, and refactoring time. In systems that are read repeatedly, that trade pays for itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Unreadable Code Concentrates Knowledge — and Risk
&lt;/h2&gt;

&lt;p&gt;Most teams have at least one feature that "belongs" to a single developer. It's usually difficult to understand and completely avoided by the rest of the team. We joke that writing code nobody else understands is "job security." It stops being funny when that person leaves.&lt;/p&gt;

&lt;p&gt;Unreadable code concentrates knowledge. When understanding is expensive, fewer people are willing to pay the cost. Ownership narrows, incidents escalate to the one person who "knows how it works," and refactors are avoided because the risk feels too high.&lt;/p&gt;

&lt;p&gt;That's not an aesthetics problem. That's a risk concentration problem.&lt;/p&gt;

&lt;p&gt;Over time, this increases the "bus factor": the number of people who would need to be hit by a bus before the system becomes unmaintainable.&lt;/p&gt;

&lt;p&gt;Readable code lowers the energy required to understand and modify a feature. Knowledge spreads. Ownership widens. Operational risk decreases.&lt;/p&gt;

&lt;p&gt;Readability creates redundancy in human understanding — and redundancy is how systems stay resilient.&lt;/p&gt;

&lt;h2&gt;
  
  
  Structure Exposes Errors
&lt;/h2&gt;

&lt;p&gt;When logic follows consistent patterns (using perceptual principles like repetition, hierarchy, and alignment), inconsistencies become visible. When I can see the patterns in my code, problems sometimes jump out: "Duh, how did I miss that?" Well-structured code allows the pattern-matching brain to notice what doesn't match.&lt;/p&gt;

&lt;p&gt;In code reviews, predictable structure lets reviewers safely scan for inconsistencies instead of reconstructing the entire mental model from scratch. When structure is unclear, bugs hide in the noise and are discovered in QA or production later — when the context has faded and the cost of fixing them is higher.&lt;/p&gt;

&lt;p&gt;Readable code prevents mistakes and makes them visible while they're still cheap to fix.&lt;/p&gt;

&lt;h2&gt;
  
  
  Experience Shifts the Optimization Target
&lt;/h2&gt;

&lt;p&gt;Over time, many developers learn the same lesson: unreadable code is expensive.&lt;/p&gt;

&lt;p&gt;They inherit parts of the codebase that technically "work" but are impossible to understand. They review changes that take longer to understand than they did to write. They refactor code whose original intent is unclear.&lt;/p&gt;

&lt;p&gt;Experience changes the optimization target.&lt;/p&gt;

&lt;p&gt;Early in my career, I optimized for speed. I was praised for finishing tasks quickly, so that's what I focused on. When I look back at some of my early personal projects, I see god classes, massive methods, and long unbroken walls of code. It worked — but it was optimized for writes, not reads.&lt;/p&gt;

&lt;p&gt;Over time, my priorities changed from local velocity ("How fast can I finish?") to system velocity ("How easy can I make this to understand and change in the future?"). &lt;/p&gt;

&lt;p&gt;Many experienced developers make that shift — not because they became perfectionists, but because they've paid the cost of unreadable code.&lt;/p&gt;

&lt;p&gt;Some developers argue that working code can be cleaned up later. In practice, this rarely happens. Cleanup competes with new deadlines, and new deadlines almost always take precedence. (As I like to say, entropy always wins!)&lt;/p&gt;

&lt;h2&gt;
  
  
  Readability Is a Team Multiplier
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Code Reviews
&lt;/h3&gt;

&lt;p&gt;Readable code lowers the cost of understanding during review. When the structure and intent are clear, reviewers can focus on correctness instead of reconstructing the mental model from scratch. Reviews become faster and more effective.&lt;/p&gt;

&lt;p&gt;Unreadable code does the opposite. Large, dense changes increase review fatigue and time-to-understanding. When understanding is expensive, reviewers conserve their energy. They skim when it isn't safe to do so and approve changes they don't fully understand.&lt;/p&gt;

&lt;p&gt;Readable code increases the probability that reviews actually catch the right problems.&lt;/p&gt;

&lt;h3&gt;
  
  
  Coordination Cost
&lt;/h3&gt;

&lt;p&gt;When intent is visible in the code, fewer meetings are required to explain it. Developers don't need to ask for walkthroughs of control flow just to make a small change.&lt;/p&gt;

&lt;p&gt;When it's not, meetings and messages are required to answer questions — often multiple times to multiple developers over time. The knowledge lives in chat messages and in the original author's head.&lt;/p&gt;

&lt;p&gt;Readability reduces repeat explanations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Load Distribution
&lt;/h3&gt;

&lt;p&gt;In unreadable codebases, certain developers become the "translators" of the logic. They are pulled into meetings, pinged constantly, and have their time consumed by answering questions and validating changes. They become bottlenecks. &lt;/p&gt;

&lt;p&gt;Readable systems distribute cognitive load. Changes aren't dependent on a single person's availability. That's not just a bus factor issue; it's structural load-balancing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Onboarding Velocity
&lt;/h3&gt;

&lt;p&gt;In a readable system, a new developer can build a mental model by skimming structure and names. In an unreadable one, onboarding requires synchronous explanations and tribal knowledge. Understanding doesn't scale.&lt;/p&gt;

&lt;h3&gt;
  
  
  Psychological Safety
&lt;/h3&gt;

&lt;p&gt;When developers struggle to understand code, they often blame themselves. They question their competence, hesitate to ask questions, and avoid touching risky areas as much as they can. That erodes morale and confidence and can eventually affect retention. I have experienced this myself.&lt;/p&gt;

&lt;p&gt;Readable systems reinforce competence. Developers can understand, complete their work independently, and contribute without fear. And developers who aren't afraid to touch the code move faster, ask better questions, and take ownership more readily — which feeds directly back into throughput.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scale Requires Consistency
&lt;/h2&gt;

&lt;p&gt;As systems grow, inconsistency becomes more expensive.&lt;/p&gt;

&lt;p&gt;In a small codebase with a small team, structural differences are tolerable. Context lives in shared memory and conversation. But as the codebase grows, the team grows, and time passes, that tolerance disappears.&lt;/p&gt;

&lt;p&gt;Large codebases rely on shared patterns, not shared memory.&lt;/p&gt;

&lt;p&gt;When similar problems are solved in similar ways, developers learn how to read the system. Patterns become familiar, and structure becomes predictable. They know where to look and what shape new code should take. When they add their own changes, they reinforce those patterns instead of inventing new ones.&lt;/p&gt;

&lt;p&gt;Inconsistent systems are unfamiliar and unpredictable. Each file must be reinterpreted from scratch. Every change requires learning a new pattern. Developers hesitate to make changes or require validation from others that their changes are correct. Understanding doesn't accumulate; it resets. That re-learning cost grows with the size of the system.&lt;/p&gt;

&lt;p&gt;You can't scale shared understanding without structural consistency.&lt;/p&gt;

&lt;p&gt;Without it, every change requires deep context reconstruction. With it, understanding becomes incremental, and incremental understanding is what makes large systems sustainable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding Is the Performance Constraint
&lt;/h2&gt;

&lt;p&gt;Velocity is often measured in outputs: tickets closed, commits merged, lines of code written. Those metrics optimize for writes — short-term wins and visible activity.&lt;/p&gt;

&lt;p&gt;Comprehension speed is invisible.&lt;/p&gt;

&lt;p&gt;If we could measure read-optimized velocity, we'd track how quickly a human can understand and safely change the code.&lt;/p&gt;

&lt;p&gt;Code volume is increasing rapidly. I recently generated 11,000 lines of code (including comments and tests) in three days using AI tooling. It took me more than twice as long to review, restructure, and refactor it into something I actually understood.&lt;/p&gt;

&lt;p&gt;Writing has never been the primary constraint. Understanding has.&lt;/p&gt;

&lt;p&gt;AI removes friction from writing, but the underlying performance constraint hasn't changed. If organizations don't rebalance toward comprehension, the gap between write speed and understanding speed widens — and velocity eventually slows under its own weight.&lt;/p&gt;

&lt;p&gt;Experienced teams stay fast because of readable code, not in spite of taking the time to write it that way.&lt;/p&gt;

&lt;p&gt;If performance is constrained by understanding, then structure isn't cosmetic. It's throughput protection.&lt;/p&gt;

&lt;p&gt;AI is accelerating write velocity. But review, comprehension, and safe change still depend on human cognition — and cognition doesn't scale with code volume.&lt;/p&gt;

&lt;p&gt;That makes readability more important now than it was five years ago, not less.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;AI was my editor, but these ideas are my own.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;This article is part of a broader series exploring how code structure, navigability, and cohesion align with cognitive limits.&lt;/p&gt;

&lt;p&gt;If you're interested in the deeper dive, the full series is here:&lt;br&gt;
&lt;a href="https://christiecosky.com/series/designing-systems-for-human-brains/" rel="noopener noreferrer"&gt;Designing Systems for Human Brains&lt;/a&gt;&lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>codequality</category>
      <category>readability</category>
    </item>
  </channel>
</rss>
