Brian Will
Object-oriented programming harms software development because imbuing inert data with agency creates convoluted class hierarchies that obscure straightforward procedural transformations.
AI-assisted summary of Object-Oriented Programming is Embarrassing: 4 Short Examples — original source. Check the original for full context.
Source published:
Key ideas
- Brian Will challenges mainstream object-oriented programming by rewriting four conference talk examples under 200 lines—by Sandi Metz, Derek Banas, and Robert C. Martin—into procedural code within their original languages to prove procedural design is clearer and simpler. (0:01)
- Will argues that OOP's fundamental flaw is imbuing inert data with intent and agency while turning actions into artificial nouns, dragging developers into abstract Platonic essentialism rather than practical engineering. (2:44)
- The critique questions the OOP doctrine of replacing branching logic with polymorphism, noting that dynamic languages lack compile-time enforcement and real-world branching conditions are rarely parallel across system phases. (17:45)
- Will rejects the idea that codebases should be broken into opaque object webs so developers do not have to understand the whole system, warning that giving up on understanding how software works inevitably leads to worse engineering. (26:48)
Detailed notes
## Background Published by Brian Will on YouTube on March 4, 2016, this video serves as a follow-up to his earlier presentation, 'Object-Oriented Programming is Bad.' According to the page description and introduction, Will sets out to demonstrate the practical shortcomings of object-oriented design by refactoring four real-world code examples from industry conference talks into simple procedural equivalents.
## TL;DR Brian Will argues that object-oriented programming (OOP) introduces needless complexity, overengineering, and philosophical confusion into software development. To substantiate this claim, he examines four real-world code samples under 200 lines—drawn from well-known conference talks by Sandi Metz, Derek Banas, and Robert C. Martin ('Uncle Bob')—and rewrites each in its original language using straightforward procedural paradigms. He demonstrates how replacing artificial class hierarchies, polymorphic dispatch, and single-use wrappers with plain data structures and direct functions drastically improves clarity, maintainability, and cognitive load. Ultimately, Will rejects the OOP mindset that atomizes programs into obscure webs of objects, concluding that clean software engineering requires letting data remain inert and maintaining a thorough understanding of execution flow.
## Atmosphere & style The presentation carries a calm, unapologetic, and iconoclastic tone, driven by Will's measured yet scathing critique of mainstream software dogmas. Rather than engaging in hyperbole or theatrics, Will methodically walks through actual code on screen, delivering deadpan takedowns of venerated industry figures and beloved abstractions like UML and polymorphic branching. Watching the video feels like sitting in an informal, candid code review with an experienced engineer who has grown thoroughly exhausted by industry pretension, dismantling complex object hierarchies with understated common sense.
## Bulletin - [00:01](https://www.youtube.com/watch?v=IRTfhkiAqPw&t=1s) Brian Will challenges mainstream object-oriented programming by rewriting four conference talk examples under 200 lines—by Sandi Metz, Derek Banas, and Robert C. Martin—into procedural code within their original languages to prove procedural design is clearer and simpler. - [02:44](https://www.youtube.com/watch?v=IRTfhkiAqPw&t=164s) Will argues that OOP's fundamental flaw is imbuing inert data with intent and agency while turning actions into artificial nouns, dragging developers into abstract Platonic essentialism rather than practical engineering. - [17:45](https://www.youtube.com/watch?v=IRTfhkiAqPw&t=1065s) The critique questions the OOP doctrine of replacing branching logic with polymorphism, noting that dynamic languages lack compile-time enforcement and real-world branching conditions are rarely parallel across system phases. - [26:48](https://www.youtube.com/watch?v=IRTfhkiAqPw&t=1608s) Will rejects the idea that codebases should be broken into opaque object webs so developers do not have to understand the whole system, warning that giving up on understanding how software works inevitably leads to worse engineering.
## Key takeaways - [00:01](https://www.youtube.com/watch?v=IRTfhkiAqPw&t=1s) Will stands by his stance that object-oriented programming has harmed the software industry and analyzes unfiltered examples from OOP conference talks to demonstrate its flaws. - [02:44](https://www.youtube.com/watch?v=IRTfhkiAqPw&t=164s) The core critique rests on the maxim 'let data be data': passive data should remain inert records, while operations should remain simple procedural functions rather than encapsulated actors. - [05:01](https://www.youtube.com/watch?v=IRTfhkiAqPw&t=301s) The Single Responsibility Principle's rule of having 'only one reason for change' is criticized as vague and nebulous when applied to practical class design. - [05:01](https://www.youtube.com/watch?v=IRTfhkiAqPw&t=301s) Creating stateless classes merely to namespace logic produces throwaway instances that add architectural ceremony without delivering genuine object encapsulation. - [07:21](https://www.youtube.com/watch?v=IRTfhkiAqPw&t=441s) Decomposing single procedures into specialized classes with dynamic metaprogramming or internal dependency instantiation overengineers tasks that plain functions and hash maps solve cleanly. - [09:00](https://www.youtube.com/watch?v=IRTfhkiAqPw&t=540s) Inlining small, single-use helper functions reduces total function count and lowers overall cognitive load across codebases. - [11:30](https://www.youtube.com/watch?v=IRTfhkiAqPw&t=690s) Derek Banas's coin-flip simulation demonstrates how formal UML diagrams and multi-class breakdowns drastically overcomplicate trivial procedural routines. - [11:30](https://www.youtube.com/watch?v=IRTfhkiAqPw&t=690s) Visual programming and UML fail to scale because complex entity interactions inevitably turn diagrams into unreadable tangles, whereas text-based code handles scale naturally. - [14:49](https://www.youtube.com/watch?v=IRTfhkiAqPw&t=889s) In the Gilded Rose kata, replacing conditional logic with abstract base classes, subclasses, class lookup hashes, and factory methods introduces excessive overhead compared to a basic switch statement. - [17:45](https://www.youtube.com/watch?v=IRTfhkiAqPw&t=1065s) The claim that polymorphism safely replaces parallel switch statements fails in dynamic languages like Ruby, which provide no compile-time enforcement for missing method overrides. - [17:45](https://www.youtube.com/watch?v=IRTfhkiAqPw&t=1065s) Real-world program conditions are rarely neatly parallel, making polymorphic dispatch an unnatural fit for divergent branching criteria across different application stages. - [19:39](https://www.youtube.com/watch?v=IRTfhkiAqPw&t=1179s) Robert C. Martin's argument parsing design unnecessarily fragments simple string token processing across an abstract class and three specialized, recursively structured marshaler subclasses. - [22:29](https://www.youtube.com/watch?v=IRTfhkiAqPw&t=1349s) A procedural alternative using a single class and three typed hash maps accomplishes argument parsing with greater clarity, direct data inspection, and fewer moving parts. - [24:57](https://www.youtube.com/watch?v=IRTfhkiAqPw&t=1497s) Thinking in terms of synthetic object personas obscures the inherent simplicity of everyday data transformation algorithms. - [26:48](https://www.youtube.com/watch?v=IRTfhkiAqPw&t=1608s) Will rejects the software philosophy that developers do not need to understand their full codebase, asserting that atomizing systems into disconnected objects degrades software quality.
## Detailed walkthrough ### [00:01](https://www.youtube.com/watch?v=IRTfhkiAqPw&t=1s) Following Up on “Object-Oriented Programming is Bad”
Brian Will explains that after posting an earlier video titled *Object-Oriented Programming is Bad*, where he outlined theoretical arguments against OOP and argued that developers should avoid it, some viewers complained that the title was clickbait. Will rejects that criticism entirely, standing by the framing because he believes OOP has genuinely harmed software engineering:
> “I chose the title because I think it's really true; I have nothing nice to say about object-oriented programming, I don't like it, I think it's really been a disaster for the industry.”
To ground his critique in practice, he is upping the ante with this follow-up, *Object-Oriented Programming is Embarrassing*. In this presentation, he analyzes four short, real-world code examples under 200 lines—two written in Java and two in Ruby. For every case, he rewrites the code into simple procedural code within the exact same programming language to prove that a procedural design produces a vastly clearer and superior outcome without unfair language-level bias.
Will emphasizes that he did not cherry-pick bad code. Instead, he took the very first examples he encountered on YouTube and Vimeo from speakers giving conference talks specifically explaining the virtues of object-oriented design and how it supposedly improves codebase maintainability.
Acknowledging that critics will argue that OOP’s benefits only manifest in massive software projects to protect against spaghetti code, Will states that the ultimate test would be taking a 500,000-line codebase and rewriting it between procedural and OOP paradigms. While that scale is practically impossible for one person, he invites viewers to send him complete, idiomatic OOP examples—either full programs or submodules of 1,000 to 2,000 lines, 10,000 lines, or even up to 50,000 lines. He expresses a preference for widely understood languages like JavaScript, Python, Java, or Ruby (with JavaScript being ideal for broad accessibility), provided the code embodies genuine, architectural OOP rather than merely perfunctory class syntax.
- **Examples examined:** 4 short programs under 200 lines (two Java, two Ruby). - **Core methodological rule:** Rewrites remain in the original source language to maintain a fair comparison. - **Source of examples:** Unfiltered first hits from online tech conference talks demonstrating OO design. - **Future testing target:** Open call for real-world modules between 1,000 and 10,000+ lines.
---
[02:44](https://www.youtube.com/watch?v=IRTfhkiAqPw&t=164s) The Flawed Philosophy: Letting Data Be Data
Before jumping into the code rewrites, Will recaps his broader critique with a core maxim: “let data be data.” Object-oriented programming forces developers into a conceptual error by insisting that passive data should be modeled as an entity endowed with purpose, duties, and the power to interact with the universe:
> “We basically have to imbue our data with intent and agency, and that's just a fundamental mistake; data does not do things, data is just inert, and that's the way it should be, and you're just adding confusion if you try and conceptualize your data as some kind of actor.”
The inverse holds true for behavior: actions should simply be actions. OOP requires programmers to “nounify” verbs, packaging basic operations into rigid data structures. Will argues this unnatural fusion of action and state forces software developers to abandon practical engineering in favor of analytical philosophy and Platonic essentialism. When building a hypothetical `Chair` class, one is forced to contemplate the true essence and boundaries of “chairness.” If a developer draws those boundaries incorrectly, OOP theorists argue that design principles were violated, making the resulting spaghetti code the developer's fault rather than a flaw in OOP itself. This creates a circular, unfalsifiable justification where failure to achieve clean code merely results in gurus prescribing ten more rules and design patterns.
```mermaid flowchart TD accTitle: The Conceptual Trap of Object-Oriented Modeling accDescr: Diagram contrasting natural programming separation of data and behavior against the conflated philosophical overhead of OOP.
subgraph Natural["Natural Procedural Paradigm"] D["Inert Data<br/>(Plain values, hashes, records)"] F["Pure Actions<br/>(Verbs, procedures, routines)"] F -->|"operates on"| D end
subgraph OO_Trap["Object-Oriented Conflation"] OOP["Platonic Entity / Class"] OOP --- Intent["Imbued with Agency & Intent"] OOP --- Noun["Verbs turned into Nouns"] OOP -->|"misdrawn boundaries lead to"| Bug["Spaghetti Code & Guru Dogma"] end ```
---
[05:01](https://www.youtube.com/watch?v=IRTfhkiAqPw&t=301s) Example 1: Sandi Metz on the Single Responsibility Principle
Will examines his first code sample, taken from Sandi Metz’s talk *SOLID Object-Oriented Design*. The core subject of Metz’s presentation is a concise Ruby class responsible for handling patents: it downloads a remote patent file, parses its contents, and updates the patent data inside a database. Metz uses this sample to demonstrate a violation of the Single Responsibility Principle (SRP)—the rule that every module, class, or method should perform exactly one task.
Will points out that defining what constitutes “one thing” remains deeply contentious. Robert C. Martin (“Uncle Bob”), who popularized the SOLID acronym, defined the Single Responsibility Principle by stating:
> “His definition is that a single class, a single unit, should have only one reason for change.”
Even after lengthy explanations, Will views the "one reason for change" metric as hopelessly vague and nebulous.
Looking at Metz's starting Ruby class, Will observes that it centers around a single `run` method that orchestrates calls to three private helper methods: `download_file`, `parse`, and `update_patents`. Metz objects to this initial implementation because downloading, parsing, and persisting data are three separate responsibilities crammed into a single unit.
Will actually concedes that grouping these disparate tasks together in one unit feels awkward, but highlights a deeper absurdity: the class contains zero fields or internal state. Because a class is supposed to define a data type, an instance of this patent processor is nothing more than an artificial namespace. To run the logic, the caller must instantiate an ephemeral object with no long-term identity or state, call `.run`, and instantly discard it.
- **Source talk:** *SOLID Object-Oriented Design* by Sandi Metz. - **Domain task:** Fetch a patent file over FTP, parse it, and commit updates to a database. - **Initial class structure:** A single `run` method orchestrating `download_file`, `parse`, and `update_patents`. - **Primary structural flaw:** Zero instance variables or persistent state, functioning merely as an instantiated wrapper around a namespace.
---
[07:21](https://www.youtube.com/watch?v=IRTfhkiAqPw&t=441s) Deconstructing Metz's Multi-Class Solution
Metz’s remedy for the multi-responsibility class is canonical OOP: decomposing the single class into multiple specialized classes. Her first extraction is a dedicated `Config` class designed to abstract and centralize configuration values parsed from a YAML file. Its `initialize` constructor accepts an options parameter, reads the corresponding YAML file, and runs a metaprogramming routine (`define_methods_for_environment`) that dynamically generates methods on the config instance matching every configuration key.
Will argues that this abstraction is an overengineered solution to a trivial problem. Instead of instantiating an object filled with dynamically generated getter methods just to read settings, a simple standalone function could take the options, parse the YAML file, and return a plain native hash map.
Metz next isolates network concerns into an `FTPDownloader` class. Will points out the exact same architectural absurdity: the class requires an `initialize` constructor to accept and assign a `config` property, yet nobody interacting with the FTP downloader will ever access or manipulate this internal configuration reference again. It exists purely as an artificial object wrapper around a single execution step.
[09:00](https://www.youtube.com/watch?v=IRTfhkiAqPw&t=540s) Refactoring Sandi Metz’s Patent Job to Plain Procedural Code
Continuing his critique of Sandi Metz's refactoring, Brian Will points out the absurdity of instantiating an entire class just to invoke a single method like `download_file`. In her revised `PatentJob` class, Metz removes the downloader method and instead creates a config object directly within the constructor, passing it along to instantiate a downloader object that gets stored in an instance variable for later use by the `run` method. Will finds this structure bizarre from an object-oriented design standpoint, noting that she avoids constructor injection: “As much as I don't like object-oriented programming, if you are going to do it, I think for a very good reason you do use dependency injection because that—well, that's a whole tangent, but basically it solves the problem of having to wire together all your objects, or it's a much more sane way of doing it.” Having a constructor instantiate its own dependencies internally defeats the standard benefits claimed by OOP practitioners.
However, Will argues that debating the nuances of object wiring misses the larger point entirely. The task should not involve classes at all, but rather straight procedural code using basic functions—what Ruby terms Kernel methods. Instead of an entire `Config` class, a simple `get_config` function could read the YAML file and return a plain hash map of values, ending the story without requiring constructors or throwaway instances. Will takes the simplification a step further by arguing that small helper functions called in only one place should simply be inlined into the main `process_patent` routine until future complexity genuinely demands extraction.
```mermaid flowchart TD accTitle: Procedural Alternative for Patent Processing accDescr: Pipeline showing configuration parsing into a hash map and sequential patent processing. subgraph ConfigStage["Configuration"] A["YAML file"] --> B["get_config()"] B --> C["Config Hash"] end subgraph ProcessingStage["Processing Pipeline"] C --> D["process_patent()"] D --> E["Download patent file"] E --> F["Process patent data"] end ```
- When using plain functions, developers do not need to ponder the significance of classes, speculate about what their fields represent, or trace single-use objects that exist only to invoke one method. - Inlining single-use helper functions reduces cognitive load; while shrinking five functions to three seems minor in a toy example, reducing a codebase from 100 functions to 60 makes a system substantially easier to navigate.
---
[11:30](https://www.youtube.com/watch?v=IRTfhkiAqPw&t=690s) Derek Banas, UML Modeling, and the Over-Engineered Coin Flip
Will turns to his second example: an object-oriented design tutorial series by Derek Banas. While acknowledging that Banas produces quality educational content in his more recent programming videos, Will characterizes his 11-part series on object-oriented design as disastrous due to the flawed nature of the paradigm itself. In the second video of the series, Banas demonstrates how to build a coin-flipping simulation. The program is not an elaborate interactive game; it simply takes two named players, randomly determines a winner, randomly selects heads or tails, prints a message like "so-and-so won with a coin flip of heads," and prompts the user in a loop if they would like to flip again.
To build this trivial script, Banas applies full formal modeling using UML (Unified Modeling Language). He constructs use case diagrams, an object model, a sequence diagram to map out time progression and messages between instances, and a class diagram dividing the problem into `Player`, `Coin`, and `CoinGame` classes, plus a fourth class solely to house the Java `main` method. Will demonstrates that the entire application could instead be written as a procedural loop inside `main`—optionally delegating to a single `play_game` function—that takes two strings, picks random values, and prints the result.
```mermaid flowchart LR accTitle: Derek Banas UML Complexity vs Procedural Simplicity accDescr: Comparison between Banas's multi-class UML architecture and Brian Will's minimal procedural flow. subgraph OOPApproach["Banas's OOP Architecture"] direction TB CG["CoinFlippingGame (main)"] --> GM["CoinGame"] GM --> P["Player"] GM --> C["Coin"] end subgraph ProcApproach["Brian Will's Procedural Alternative"] direction TB M["main() loop"] --> PG["play_game(player1, player2)"] PG --> R["Random winner + flip & print"] end ```
Beyond the code itself, Will mounts an attack on UML and visual programming systems in general. Diagramming tools are notoriously frustrating, but the deeper conceptual flaw is that visual representations break down as soon as a program achieves any realistic level of complexity. When multiple entities interact, diagrams devolve into an unreadable tangle of boxes and crossed lines. Written code with named functions and textual references handles scale far better than visual diagrams, which Will identifies as the primary reason visual programming environments have never succeeded broadly:
> “The lesson here is that UML is just garbage, don't ever use it, it's crazy.”
---
[14:49](https://www.youtube.com/watch?v=IRTfhkiAqPw&t=889s) Sandi Metz’s Gilded Rose: Polymorphism vs. a Simple Switch
The third example revisits Sandi Metz, specifically her presentation titled "All the Little Things," which tackles the well-known Gilded Rose refactoring kata containing World of Warcraft-themed item names. In the talk, Metz displays a block of nested, convoluted conditional logic that updates item quality and sell-by dates over time, framing polymorphism as the elegant solution that swoops in to rescue the codebase from messy branching logic.
Metz refactors the code by introducing an enclosing `GildedRose` class containing an abstract `Item` base class with an empty `tick` method, accompanied by three specialized subclasses: `Normal`, `Brie`, and `Backstage`. She then constructs a lookup hash map that associates specific strings (such as `"Aged Brie"` or `"Backstage passes to a TAFKAL80ETC concert"`) with their respective class types. Finally, she implements a class-level factory method (`self.for`) that accepts the item name, quality, and remaining days, looks up the corresponding subclass in the hash, instantiates it, and invokes its overridden `tick` method to execute the update.
Will argues that while Metz’s solution is clever and showcases dynamic Ruby features, it introduces unnecessary architectural overhead. The flaw in the original code was not that it used conditionals, but that it failed to identify that the business logic boiled down to four mutually exclusive categories. Once those discrete cases are identified, a simple `case`/`switch` statement or an `if-elsif` ladder expresses the logic cleanly without inventing an entire hierarchy of classes:
> “The way she presents it in the talk is that polymorphism swoops in and saves the day, it makes the code much cleaner, but there's no need for it—you can just write a damn switch.”
- Metz's polymorphic architecture requires an abstract base class (`Item`), three subclasses (`Normal`, `Brie`, `Backstage`), a class-to-string registry hash, and a static factory method (`self.for`). - Will contends that creating new data types and inheritance hierarchies to replace straightforward conditional branches is "perverse" when a flat procedural switch statement achieves the same clarity with far fewer concepts.
[17:45](https://www.youtube.com/watch?v=IRTfhkiAqPw&t=1065s) The Fallacy of Replacing Branching with Polymorphism
Transitioning from an earlier refactoring example, Brian Will explains that the procedural version could be made even cleaner. In a strictly procedural design, one does not retain instance variables inside methods of a class; instead, functions like `tick` would simply take arguments such as a name and a data object, operating directly upon its members. He addresses the widely circulated object-oriented doctrine that polymorphism can or should eliminate all branching logic. The common justification is that codebases frequently repeat parallel switch statements across multiple files; by modeling these cases polymorphically, statically typed languages like Java will enforce at compile time that every method in an interface or abstract class is implemented. This supposedly prevents developers from updating half of the switch statements when introducing a new case while forgetting the other half.
```mermaid flowchart TD accTitle: Comparison of Polymorphic Branching vs Procedural Switches accDescr: Contrasts how polymorphic classes handle dispatch across types versus how procedural code handles diverging logic.
subgraph PolymorphicApproach["Polymorphic Class Hierarchy"] Base["Base Type / Interface"] --> TypeA["Subclass A (implements method)"] Base --> TypeB["Subclass B (implements method)"] Base --> TypeC["Subclass C (implements method)"] end
subgraph ProceduralApproach["Procedural Data Inspection"] Input["Raw Input Data"] --> Switch1{"Context 1: Switch on Case"} Switch1 --> Action1["Execute Logic"] Input --> Switch2{"Context 2: Divergent Conditions"} Switch2 --> Action2["Execute Separate Logic"] end ```
Brian points out that this safety argument immediately collapses in dynamic languages such as Ruby, where subclassing an abstract class without overriding a method triggers zero language warnings. Even setting aside language typing, he fundamentally disagrees with the philosophy itself. Real-world branching logic is rarely identical across different program phases; it is typically semi-parallel, where logic switches over one set of factors in one part of the codebase, but requires completely different criteria elsewhere. Forcing code into artificial class hierarchies under the guise of eliminating switches introduces unnecessary mental friction.
> “Regardless, I still think polymorphic branching is a perverse idea mostly because I think these neatly parallel switches are actually quite rare.” — Brian Will
- In a pure procedural model, free functions accept data structures as parameters rather than executing implicitly against encapsulated instance state. - In statically typed languages like Java, unimplemented interface methods trigger compile-time errors, but dynamic languages offer no such safety nets. - Reconceptualizing regular branching logic as dispatch over types creates high conceptual overhead with negligible long-term payoff.
---
[19:39](https://www.youtube.com/watch?v=IRTfhkiAqPw&t=1179s) Uncle Bob’s Command-Line Argument Parser
Brian introduces the final example of the presentation: a command-line argument parser demonstrated in a talk by Robert C. Martin (known as Uncle Bob), whom Brian calls “probably the preeminent object-oriented Guru.” Martin’s program parses command-line arguments into a queryable data structure so a caller can look up the value of flags such as `-b` or `-c`. The arguments are defined via a comma-delimited schema string that uses special character annotations to denote data types:
| Type Indicator | Meaning | Example Schema Token | Description | |:--- |:--- |:--- |:--- | | *(None)* | Boolean flag | `f` | Defaults to `false` if flag is omitted | | `#` | Integer flag | `j#` | Followed by an integer parameter on CLI | | `*` | String flag | `w*` | Followed by a string parameter on CLI |
For example, a schema string defining `j#`, `f`, and `w*` expects an integer flag `j`, a boolean flag `f`, and a string flag `w`. Given command-line input like `-j 35 -w blahblahblah`, the resulting data structure assigns `35` to flag `j`, `false` to flag `f` (because `-f` was omitted), and `"blahblahblah"` to flag `w`. Martin originally showed an unrefined version before presenting his cleaned-up solution. Brian skips the "bad" version entirely, remarking that it was neither worse nor better than Martin’s ostensibly superior refactored code.
In Martin's refactored architecture, an abstract `ArgumentMarshaler` class exposes two abstract methods: `set` and `get`. Brian expresses bewilderment as to why Martin chose an abstract class over a standard Java interface, given that the class contains no fields or concrete implementation. This abstract base is extended into three concrete subclasses: `BooleanArgumentMarshaler`, `StringArgumentMarshaler`, and `IntegerArgumentMarshaler`. The bodies of these classes are nearly identical, differing only in the primitive or reference type they manipulate.
```mermaid flowchart TD accTitle: Uncle Bob's Marshaler Architecture accDescr: Hierarchy of ArgumentMarshaler and its concrete subtypes feeding into the Args coordinator class.
subgraph MarshalerHierarchy["Marshaler Classes"] AM["abstract ArgumentMarshaler\n+ set()\n+ get()"] AM --> BAM["BooleanArgumentMarshaler"] AM --> SAM["StringArgumentMarshaler"] AM --> IAM["IntegerArgumentMarshaler"] end
subgraph Execution["Execution Flow"] Main["Main / Client Code"] -->|"instantiates with schema & args"| ArgsClass["Args Coordinator Class"] ArgsClass --> BAM ArgsClass --> SAM ArgsClass --> IAM Main -->|"getBoolean(), getInt(), getString()"| ArgsClass Main -->|"invokes on success"| Exec["executeApplication()"] end ```
These subclasses are then coordinated by an `Args` class containing `main`. In `main`, the consumer instantiates `Args` with the schema string and CLI inputs, receives an `Args` object, and queries it through dedicated methods like `getBoolean('l')`, `getInt(...)`, or `getString(...)`. The values are subsequently passed to a stubbed `executeApplication` function. Because the constructor and getters are declared to throw `ArgsException`, the setup must be wrapped in a broad `try`/`catch` block.
---
[22:29](https://www.youtube.com/watch?v=IRTfhkiAqPw&t=1349s) Brian Will’s Single-Class Procedural Solution
Brian contrasts Uncle Bob’s multi-class hierarchy with his own concise alternative: a single class named `Args`. Rather than generating a family of marshaler classes, Brian's `Args` class manages state using three simple hash maps that map individual characters to values: - `HashMap<Character, Integer>` for integers - `HashMap<Character, String>` for strings - `HashMap<Character, Boolean>` for booleans
The constructor processes the input in two brief loops. First, it parses the schema string by splitting on commas, reading each token's accompanying character symbol, and inserting the flag character into the corresponding map alongside sensible defaults. Boolean entries default to `false` (since an omitted boolean flag represents falsehood), whereas integer and string entries default to `null` (since missing values have no implicit value).
```mermaid flowchart LR accTitle: Procedural Parsing Pipeline accDescr: Two-stage procedural parsing from raw schema and argument strings into three typed hash maps.
subgraph Stage1["1. Parse Schema"] Schema["Schema String\n(e.g., 'j#,f,w*')"] --> SplitSchema["Split by Comma"] SplitSchema --> PopulateMaps["Populate HashMaps with Defaults:\n- Booleans -> false\n- Integers -> null\n- Strings -> null"] end
subgraph Stage2["2. Parse CLI Tokens"] Tokens["CLI Arguments\n(e.g., '-j', '35', '-w', 'val')"] --> FilterHyphens{"Starts with '-'?"} FilterHyphens -- No --> Ignore["Ignore Token"] FilterHyphens -- Yes --> MatchMap{"Check Flag in Maps"} MatchMap --> AssignVal["Parse & Store Next Value"] MatchMap -- Unknown Flag --> Err["Throw Exception"] end ```
Second, the constructor loops over the supplied command-line arguments. Non-flag arguments lacking a leading hyphen are skipped. When a hyphenated flag is found, the constructor inspects the character against the three maps, reads the value that follows, converts it, and updates the map entry. If an unexpected character not declared in the schema is encountered, it drops into an `else` branch and throws an exception.
Brian provides equivalent `getBool`, `getString`, and `getInt` accessors to match Martin's API, but notes that in his own code, he would prefer making the hash map fields `public` and querying them directly. He also favors explicit null-checking over throwing exceptions:
> “And I prefer explicit error handling over exceptions, so the resulting code will look something like this: we get `args.ints.get`, but that might be null, and if so then that's an error, otherwise if it's not null then we can get its value.” — Brian Will
Brian acknowledges that checking for null values directly in Java can look verbose and clumsy, but explains that this clunkiness is the fault of Java's awkward divide between primitive types and boxed object types rather than an issue with procedural design.
---
[24:57](https://www.youtube.com/watch?v=IRTfhkiAqPw&t=1497s) Complexity, Recursion, and OO Mental Models
Brian directly contrasts the architectures of both solutions. His procedural approach consists of a single `Args` data type containing a constructor, public fields or straightforward getters, and `main`. A developer inspecting the codebase knows instantly where execution begins and how data flows. Martin's solution, by contrast, explodes the problem across five separate classes, including an abstract class that Brian argues is completely unnecessary.
Most baffling to Brian is the internal design of Uncle Bob's marshaler classes: they are implemented recursively. For instance, `BooleanArgumentMarshaler` contains a field for its boolean value, but also contains an internal map associating characters with instances of `ArgumentMarshaler`. Brian observes that in practice, this internal map only ever points back to another `BooleanArgumentMarshaler`. He cannot discern any practical reason for making the marshalers recursive, describing the design as profoundly bizarre.
Brian diagnoses the root cause of this design as a cognitive failure brought on by object-oriented ideology. When programmers model everyday algorithms by assigning agency to passive data and turning actions into classes, they lose touch with the baseline simplicity of what they are building.
> “I think what happened is that because he was programming in an object-oriented style, he just didn't have the clarity to see how simple what he was doing really was and so he couldn't then devise a sensibly simple solution.” — Brian Will
When an engineer inspects Uncle Bob’s code, they must puzzle through why five separate types exist, decipher why the data structures are recursive, and trace execution jumping across numerous fragmented, tiny methods. Procedural logic may retain a switch statement or an explicit loop, but Martin’s object-oriented decomposition merely scatters that logic to the winds. Brian concludes that thinking purely in terms of data structures and data transformations produces far clearer, more maintainable code than inventing synthetic "doer" types.
[26:48](https://www.youtube.com/watch?v=IRTfhkiAqPw&t=1608s) The Illusion of Decomposed Objects and the Necessity of Understanding Code
Reflecting on the command-line argument parsing example, Brian Will reiterates that the core task was remarkably straightforward: taking an incoming array of strings and converting it into a structured format of data—specifically using three simple hash maps—that is convenient to query and work with downstream. Stripping away the layers of object-oriented ceremony yields immediate clarity, cutting through unnecessary abstractions to focus strictly on data transformation. Brian argues: “By avoiding all of his object-oriented nonsense we can have that clarity.”
```mermaid flowchart LR accTitle: Procedural Data Transformation vs Object-Oriented Decomposition accDescr: Simple procedural pipeline transforming raw arguments into queryable hash maps versus fragmented object graphs.
subgraph Procedural ["Procedural Approach"] direction LR A["Array of Strings"] --> B["Parser Function"] B --> C["Three Hash Maps"] end
subgraph OOP ["Object-Oriented Approach"] direction LR D["System of Small Objects"] -.->|"Fragmented Interactions"| E["Opaque Call Graph"] end ```
This contrast prompts Will to recall a talk by prominent software author and speaker Sandi Metz, who openly conceded that object-oriented code is generally less comprehensible than equivalent procedural code. Metz defended object-oriented programming by arguing that developers are not actually supposed to understand the entirety of a codebase; instead, if a system is properly broken down into a web of small, isolated objects, developers can supposedly implement changes simply by introducing new objects without having to master all the complex connections and interactions beneath the surface.
Brian remains deeply skeptical that object-oriented code ever truly delivers on that promise, rejecting the notion as a fundamentally flawed engineering philosophy. In his view, attempting to build systems while deliberately avoiding comprehension of how the pieces fit together is disastrous. Developers must understand their code, their tools, and the underlying platform environment. Delivering his concluding verdict on the mindset, Brian warns: “When we give up on understanding what we're actually doing when we write code it just leads to bad software.”
- **The Pragmatic Objective**: Command-line parsing only requires taking raw input strings and reshaping them into structured, queryable data structures such as hash maps. - **Sandi Metz's Concession**: Object-oriented code is inherently harder to comprehend in its entirety than procedural code, but OOP proponents argue total system comprehension should be unnecessary if responsibilities are atomized into small objects. - **The Core Counterargument**: Abdicating the responsibility to thoroughly understand code, tools, and runtime platforms inevitably degrades software quality rather than improving maintainability.
## Notable quotes > [00:01](https://www.youtube.com/watch?v=IRTfhkiAqPw&t=1s) I have nothing nice to say about object-oriented programming, I don't like it, I think it's really been a disaster for the industry. — Brian Will
> [26:48](https://www.youtube.com/watch?v=IRTfhkiAqPw&t=1608s) When we give up on understanding what we're actually doing when we write code it just leads to bad software. — Brian Will
> [11:30](https://www.youtube.com/watch?v=IRTfhkiAqPw&t=690s) UML is just garbage, don't ever use it, it's crazy. — Brian Will
## Mentioned Brian Will, Sandi Metz, Robert C. Martin, Derek Banas, SOLID Object-Oriented Design, All the Little Things, Gilded Rose, Java, Ruby, JavaScript, Python, YAML, UML, YouTube, Vimeo
## Video description A follow up to https://www.youtube.com/watch?v=QM1iUe6IofM
[Source](https://www.youtube.com/watch?v=IRTfhkiAqPw)