82 comments

[ 4.5 ms ] story [ 148 ms ] thread
/shrug

Compound booleans are super useful for cleaning up huge chunks of imperative code. I do it all the time, and it makes the function far easier to read (although the tradeoff is in debugging).

What do you mean by "compound booleans"?
(comment deleted)
> I just think there aren’t enough people calling out his weaker advice.

Weird. My impression was that hating on uncle bob had made for low hanging blog fruit for a good 5 years already.

Some of CC is alright (I have a feeling he popularised negative gates and early returns?), some of it didn't age as well. As is often the case it's hard to see it in the context of its time.

Ooof, Uncle Bob. It's important to understand that he's a consultant with a heavy emphasis on pedagogy, and he has been since around 1990. As far as I can tell from his blog and public internet presence, he's never maintained a piece of software for the long term. His incentives are all about getting people to pay for training, and to that end he's succeeded beautifully. Object oriented programming caused massive confusion about writing maintainable software, and it's both easier and more profitable to tell people that they need to OOP better than to use more appropriate paradigms in the design of their code.
> it's both easier and more profitable to tell people that they need to OOP better than to use more appropriate paradigms in the design of their code.

I don't think this is quite on target. Sure, he needs to go where the people are in order to communicate to the most people, but that doesn't mean he doesn't speak out against fads if it's appropriate.

This talk was really formative for me: https://www.youtube.com/watch?v=Nsjsiz2A9mg If I remember correctly, he rails against just sticking the database in the middle of your system and calling that your architecture.

If I'm remembering https://www.youtube.com/watch?v=QHnLmvDxGTY correctly, he says OOP is no big deal, it's just encapsulation-polymorphism-inheritance, and then goes on to say things like C had better encapsulation than OO languages (you can keep your .c file to yourself, and just give someone the .h to program against.). And c-style bytes with stdin/stdout was designed with perfect forward-compatibility - programs like your terminal don't need to be recompiled even though they interact with your program (which was written after).

And for the last 5+ years he's been pushing FP pretty hard, especially closure. I don't see him as pushing OO on people at the expense of anything else.

I've made it a bit of a project lately to understand Uncle Bob and his effect on our profession, so I took your recommendation seriously and watched the first video you linked. I'll start with the bit I agree with. Uncle Bob is right that you want to be able to test your core logic without depending on a database and a webserver. I also found his statement at the beginning that your folder organization should reflect the purpose of the application appealing, but I'm not sure it's actionable. He also spends some time kicking RUP while it's down, before turning around and saying "actually, those guys had a point." A strange rhetorical tactic, and probably some ancient score-settling was involved, but it's a fair point. I can't fault him for saying "Ruby is about to get really big" in 2013, but he timed that trend just about exactly wrong.

Where I disagree with Uncle Bob is in his proposed solution. Define a plugin interface for every side-effecting part of your code, and pass in safe plugins when you want to test it. He's explicit about his preferences: the core of your application should be pure objects, exposing only methods and no data. And since you want those to be encapsulated, you can't let them leak out, you have to pass the actions in, so that you can do them in your core. This is a bonkers, topsy-turvy world where you can't just have a pure data processing routine. If you could, you would just write it, test it, use it in your side-effecting procedures, and go about your day. No plugin architecture required. Infinitely more testable, because your data processing routines don't have side effects. Uncle Bob's design forces you to deal with what happens when your side-effects go wrong somewhere in your core logic. This kind of thing is the reason we have a "law of leaky abstractions." Side effects can't be encapsulated. We try anyway, and they leak across the barrier.

I also went and looked at some of his blog posts about functional programming, and as far as I can tell, he's mostly repackaged his traditional advice for Clojure. I think a much better source there would be Eric Normand.

> Define a plugin interface for every side-effecting part of your code, and pass in safe plugins when you want to test it.

I think we're on the same page so far

> the core of your application should be pure objects, exposing only methods and no data.

I'll put my thumb on the scales here and say that classes should be all-behaviour or all-data. In that context, he's talking about structuring the behaviour classes in your application. Take the following (I don't recommend it because it violates DIP, but that's not the point I want to make yet):

  UserService -> UserStore { User getUser(uid) {..} }
Since getUser is exposed as a method, you could swap out UserStore for PostgresUserDatabase or anything else that exposes getUser(). But if you reached directly into UserStore's HashMap (exposed data), then you're stuck with HashMap storage and can't replace it with a database. As for User itself (all-data) I don't think it's a big deal if you read its data directly (e.g. user.name.firstName). Some folks insist on a getter there, but I say if you're doing the architecture well, it should be no problem to change it to a getter later on if that's what you actually need. I think that is all that is meant by "exposing only methods and no data".

> you have to pass the actions in

I think I get where you're going with this, but first

> This is a bonkers, topsy-turvy world

Topsy-turvy synonyms: reversed ... upside-down ... inverted? Yes, inverted! I, for one, vote that we rename DIP to BTTDWP, the "Bonkers topsy-turvy dependency world principle."

I suspect that Uncle Bob used a particular convention for his arrows and didn't define them in this talk, which is why you find it bonkers. The arrows are dependencies in the "who-knows-about-whom" sense, not the "who-invokes-whom" sense. The call-chain still goes in the expected ('forward') direction, but it's the knows-about arrows that are flipped in BTTDWP.

Here's an (incorrect) 3-part system, prefixed with (W)eb, (B)usiness and (D)atabase, for a typical getUser() flow.

  WUserController -> BUserService -> DUserStore
There's no risk here of BUserService accidentally coupling to Web stuff (unless BUserService decides to return a WUser instead of a BUser.) But there is every risk that BUserService will treat DUserStore like a concrete database. BUserService will start doing things like opening Hikari connection pools and beginning/ending SQL transactions, and then you'll never be able to test it in isolation.

All BTTDWP means here is to flip the 'knows-about' arrows so that the concrete, outside bits (which Uncle Bob keeps calling plugins) know about the B classes, instead of the other way around:

  WUserController -> BUserService <- DUserStore
The call chain still flows in the original direction! This is done with interfaces in mainstream OO:

  WUserController -> BUserService -> IUserStore
                                  <- DUserStore
I think that's how I'd push back on "you have to pass the actions in".

> If you could, you would just write {pure data processing routine}, test it, use it in your side-effecting procedures, and go about your day.

Yes this is right. Your side-effecting procedures are typically the Database or other plugins, and your data processing is typically your business logic. Calling into the business logic from the plugin means that BTTDWP is already satisfied and you don't have to reverse any arrows.

> And since you want those to be encapsulated, you can't let them leak out, you have to pass the actions in, so that you can do them in your core.

I don't think leakage is a problem in the above W->B<-D setup. If you're standing in W, and you call B for somethi...

I really appreciate your thoughtful response! I'm going to have to take some time to meditate on it. I think I see where you're coming from, and where my opinions may differ. Cheers!
Exactly. I like to get advice from people like Rob Pike, Matteo Collina, and John Carmack. People who build and maintain big, real, and important software projects.
> take his principles with a grain of salt.

This is healthy.

Applies to all advices, wisdoms and mottos.

Some years into my career, I struggled with colleagues writing sloppy code. I felt that they didn’t listen to me, when I gave them unsolicited advice. The optional code review mechanisms we set up were only eagerly used by people who cared about readability already.

So I started to refer to Clean Code when I needed an authority argument: if it’s in a book, you need a stronger argument against it, and unless you have a sound argument, your code is unclean.

I believe Clean Code mostly gets referenced by people who look for someone to agree with them on anything specific. The weaker opinions and examples become “junk DNA” for a selective quoter.

Back then, I often didn’t find a section where Clean Code agreed (or disagreed) with me. Mainly because I got influenced by functional programming, which the book does not address.

Once I decided to read the whole book, because I wanted to collect the good parts. I found myself only disagreeing in a small number of sections. But at the same time, I was completely unable to use his examples for anything.

I think this book serves a purpose. But I don’t think it addresses enough of what I have learned in the last ten years.

A simple example is naming things: the book does a fine job at giving good and bad examples. But I find that once you find the right vocabulary from your problem domain, and you want to choose among words that are all appropriate, thoughts like “is this at the right level of abstraction for this area of the code?” and “will this name also be meaningful in 6 months?” don’t get addressed. It’s the scenario where all your choices are okay, which name is better?

The book played a big role on my early career before I gained an authority and an experience of my own.

Haha, the infamous Clean Code book - "how to make your code unreadable in these few easy recursive steps". The steps are roughly to extract everything into as small as possible functions that happily mutate lots of state while having "expressive" (read: overly verbose and long) names but no parameters. Make a function "shouldNotBeCompacted" that returns the inverse of "shouldBeCompacted" because of a pathological hate of negatives? Okally-Dokally, or cue Robert C. Martin: "worthy of a professional."
Clean code was the first book I found when looking for books about writing clean code, go figure, and back then I found it very helpful. It's easy to criticize now that I'm so far removed from then (in both time and skill). Before this point I really had never even thought about the code's design, only its functionality. I wouldn't recommend it now, but this book played its role in my life
I can't believe this "refactoring" is actually presented as good—creating unnecessary abstractions for a simple function.

If anything, I would go in the opposite direction. There's no need to use parameterized variables at all. Just write out the message 3 times:

  private String generateGuessSentence(char candidate, int count) {
      if (count === 0) {
          return "There are no " + candidate + "s";
      } else if (count === 1) {
          return "There is 1 " + candidate;
      } else {
          return "There are " + count + " " + candidate + "s";
      }
  }
Maybe this isn't "clean" (I'd claim it is) but it's certainly the easiest to understand.
Yes! I really like "99 Bottles of OOP" by Sandi Metz. I feel like it's a version of "Clean Code" that my brain actually aligns with.

In a chapter, talking about different ways you could write a class that generates verses for the song 99 bottles of beer, we end up at:

https://sandimetz.com/99bottles-sample-ruby#_shameless_green

    The Shameless Green solution is disturbing because, although the code is easy to understand, it makes no provision for change. In this particular case, the song is so unlikely to change that betting that the code is "good enough" should pay off. However, if you pretend that this problem is a proxy for a real, production application, the proper course of action is not so clear.

    When you DRY out duplication or create a method to name a bit of code, you add levels of indirection that make it more abstract. In theory these abstractions make code easier to understand and change, but in practice they often achieve the opposite. One of the biggest challenges of design is knowing when to stop, and deciding well requires making judgments about code.
This book is beyond parody. I send it to people I hate and want to sabotage, along with clean code.
...ok? Noted, you do not like the book I like.
Which means that in the future not only can you two ignore each other's book recommendations, you can also note each other's book disrecommendations to find stuff you'd actually enjoy! It's a win-win!
So you’re actually sabotaging the people who have to maintain their code, not them.
I'd think your code is clean. The only change I might make is to change the if statements into a single switch with `case 0`, `case 1`, and `default`. But even without this, it's already much better than what Uncle Bob comes up with.
I'd remove the else if blocks since we're returning.

    private String generateGuessSentence(char candidate, int count) {
      if (count === 0) return "There are no " + candidate + "s";
      if (count === 1) return "There is 1 " + candidate;

      return "There are " + count + " " + candidate + "s";
    }
I feel like single-line conditionals are harder to read in bigger projects. I remove the brackets in single-statement conditionals and loops, but I still use another line.
Braces _always_.

It just needs one tired / inexperienced / somethingelse coder to "quickly add" an extra call to the topmost if and then you'll have interesting issues.

I'll take the "ugly" braces every day compared to having risky structures like that in the code at all.

Thorough testing should catch all those "interestig" issues.

But easier maintainability I also favor braces everywhere.

or pattern match it:

   defmodule Guess do
      def guess_message(candidate, _count=0) do
        "There are no #{candidate}s"
      end

      def guess_message(candidate, _count=1) do
        "There is 1 #{candidate}"
      end

      def guess_message(candidate, count) do
        "There are #{count} #{candidate}s"
      end
    end

    return "There are no " + candidate + "s";
I always hated overloading + for both addition and concatenation. Hence D uses ~ for concatenation.
Why? It seems natural and I can't think of a case where it could be confusing off the top of my head (for strongly typed languages).
+ should be commutative.
I disagree - I think the intuition that mathematical addition being commutative means that the + operator must be commutative is not pragmatic. Whether + must be commutative is less relevant than whether + must be mathematical. I.e. I see no problem - theoretically, practically, or semantically - with using + to combine things in programming, mathematically or otherwise. As long as it's in a strong language that doesn't support nonsense. E.g. JavaScript might not deserve operator overloading (I imagine - I rarely use JS).
I love this. The cognitive of understanding what this code does is super low, it solves the problem, presents no glaring performance issues, and isn’t trying to be cute.
This is much better. Love it when code follows the KISS principle without unnecessary abstractions or optimizations when its not needed.
A few decades ago I refactored a different way:

   // this may be a mistake!  English language is unorthogonal in the extreme!
   const char * Add_es( int count ) { return 1 == count ?  "" : "es"; }
   const char * Add_s(  int count ) { return 1 == count ?  "" : "s" ; }
(some English words pluralize with "es" suffix, most with "s"). It has proven surprisingly useful:

   Msg( "%d file%s updated when you switched back", updates, Add_s( updates ) );
Only difference vs example is mine prints "0" vs "no".
I wanted to write a sane version of this function, and it looks exactly like this.
My preference would be not to refactor it at all. It works, you can figure out what it does, and the tests pass. Forget about it. Whoever wrote it, wrote it to the best of their ability to implement the requirements and style that existed at the time.

The requirements changed and it now has to handle a new case? Now you can refactor it. What refactoring to choose depends on the new case you have to support - it's going to look a lot different if you need to accept a new "language" parameter than if you need to special-case another number.

Agreed that I wouldn’t refactor this for no reason.

That being said, the first time I had to edit this (even just do change the base string) I’d make the change.

Sadly this only works for English. Of course, given the functional requirements, it's probably fine, but this kind of code exists IRL and it can be annoying to refactor to support more languages.

For example, many languages have grammatical gender and so may also change how the word looks like. Bonus points, when the word body changes. In this example it wouldn't change radically, but the word for a file in German is "die Datei" and the plural is "die Dateien" (although apparently "das File" can be used.)

Okay, this isn't that big of a deal, one might say. You just pack the word's singular and plural with the possible gender, and you're set. But no, because this isn't even the worst part! The simplest complicated case might be that you have a singular, dual, and plural. In some sense this makes sense, since "two" is pretty special in terms of amount of stuff.

But then you get stufi like the following, as documented in [0]:

> Three forms, special cases for numbers ending in 1 and 2, 3, 4, except those ending in 1[1-4] > > [...] > > Languages with this property include: > > Slavic family > > Russian, Ukrainian, Belarusian, Serbian, Croatian

Or the six form pluralisation found in Arabic.

So yeah, this sort of "complicated" pluralisation isn't just an issue if you're trying to support some comparatively very niche languages, but this is a thing one needs to think about when supporting many major world languages.

And of course, how you inflect the noun you're pluralising depends on the context of the sentence and the relevant grammar. For example, the form might change if you're at a place that expects a noun in its nominative versus accusative form. And of course, there's the order of words, and so muth more, that could be mentioned.

So yeah, natural language is difficult. Not quite Turing complete, but good localisation takes a lot of effort.

[0]: https://www.gnu.org/software/gettext/manual/html_node/Plural...

This is so much easier to read for me personally. It immediately tells me what the test cases are. With the author's version it's not that obvious what all the possible combinations of verbIs, countStr and sIfPlural are. By eliminating redundancies the author introduces a new one, the count == 1 case is handled twice, once for verbIs and than again for sIfPlural.
There are some issues with the code he's criticizing (createPluralDependentMessageParts is satire), but his infatuation with ternary operators makes his code a mess
> I’ll be discussing the very first example of refactoring in his book from “Chapter 2: Meaningful Names”.

I prefer the 'after' (not that I don't have complaints about it).

When I read 'before', I can't see any structure. I scan from top-to-bottom, get half-way down, think "wait, what?" and my eyes jump back up without me even consciously deciding to.

> The first thing you’ll notice is that Martin has taken a single, mostly PURE function (shout out to all the functional bros), and made a class out of it.

That doesn't make a good purity argument. 'Before' has a print. There is no way to test it apart from reading stdout (or god forbid, having your test harness somehow intercept/mock the print call). `After` has no such IO, it returns the String itself to make unit testing possible.

I despise mutability, but neither before nor after does any re-assignment, (only assignment.) So really, those fields should be marked 'final' and the 'private void' methods should be constructors or something... I'm not really sure about the 2008 version of whatever language he's using.

No, that refactored version is indeed supposed to be used like

    print(new GuessStatisticsMessage().make(candidate, count));
There are cases where using a class as a bag of pseudo-global variables that get mutated during a long web of internal method calls — a recursive-descent parser for a non-trival language is a prime example, — is fine but this, I'd argue, is not one of those cases. You need to (very straightforwardly) map two input values to three output values and return them glued together — one function with a switch in it is quite enough for that.

P.S. And what the hell does "thereAreNoLetters" even mean? Oh, right, it makes sense since this is a letter-guessing game, and this class is used to tell you that no, there are no letters like the one you tried to guess. Bleh.

> There is no way to test it apart from reading stdout (or god forbid, having your test harness somehow intercept/mock the print call).

It's trivial to change if you care about that part. DI - pass in a ConsoleWriter. It also solves the test without mocking.

The post is right in that the example code is terrible, Uncle Bob's changes make it much worse, but also the suggested fix is also bad. I think this is better, but the whole thing is silly. Obviously you'd want to write a comment for the function that explains its weird behaviour.

  String getCandidateCountString(char candidate, int count) {
    if (count == 0 ) {
      return String.format("There are no %ss", candidate);
    } else if (count == 1) {
      return String.format("There is 1 %ss", candidate);
    } else {
      return String.format("There are %s %ss", count, candidate);
    }
  }
There are 3 as. lol
This is pretty much what I've come to over the years. It kinda feels like the "IQ Bell Curve" meme:

Beginner: I'll just create an if statement with 3 clauses.

Middle: No! that's clearly a ton of duplication - it's bad code!

Expert: I'll just create an if statement with 3 clauses.

And I can see how people get to the middle position. People often tell beginners to eschew duplication. They learn to see any trio of similar-looking lines as a code smell and jump to refactor. It takes a few years of doing that and getting burnt by it before you can really differentiate "this is duplicative and would benefit from refactoring" from "this only looks duplicative, and is actually cleanest with the repetition in place."

I wouldn't say his suggested fix is bad - it's certainly better than the original. But I agree your code is even better. Except it has a minor bug - "There is 1 candidates"
:) yes. I saw that error and wondered whether someone would point it out. I think the author fails to acknowledge that the use of variables like "verb" just make the code much harder to understand than a string that reads mostly like English.

  question = "Is";
  object = "this";
  adjective = "easy";
  String.format("%s %s %s");
vs

  String.format("Is this easy");
The second is much easier to understand than the first, so one should be careful when pulling out too many variables.

Seems like someone should write a book called "Clean Code Done Right" that has actual good examples of code. Maybe Rich Hickey? The high level advice is good, code should be written as simply as it can be. Functions should have a small clear mission and it should be easy to argue that the function is correct locally. That is to say: a functions correctness shouldn't depend in a complicated way on other functions, just a reasonable expectation on the behavior of the functions it calls, and a reasonable simple description of its mission. Easy to state this objective often difficult to achieve in practice.

Maybe a book showing elegantly coded solutions to actually difficult programming problems would be good, e.g. a system with maybe the complexity of high performance sharded fault tolerant Redis. There would be a lot of interesting topics there.

This is the way. Far easier to understand (and maintain!) than any of the other examples. Temporary variables are abstractions, must use them with care.
> Look, I don’t have anything personal against him

I could probably point you to some things he has said that might change that...

Someone is criticizing a tech book written 16 years ago? *insert Jackie Chan meme here*
Uncle Bob books were constantly suggested as good go-to programming books here on HN for years, though in the past ~4 years they tides have finally turned against him. And thankfully, Martin Fowler too.
This is addressed in the second to last paragraph of the blog post: The 'last straw' that made the author write that post was Robert Martin doubling down on his advice, even today.
It's not a "tech" book, as if it were a 2008 book on how to use a specific version of Access. It's on programming practices. Most good programming books from 20 or 30 years ago are still relevant today. Instead of acting smug with your jackie chan meme(?) you could just ask next time. I hate these one line "zingers" people drop like this is twitter.
The man has moved on to functional programming since then. People are allowed to change.

I have 211+ programming books in my library going back to 1998. I don't consider any books from 16 years ago to be valuable or relevant today.

Then those books aren't any providing any deep knowledge, just superficial trivia about whatever is fashionable. some people do that as their job, though, so do what you want.
Me: "Uncle Bob has some very intelligent thoughts about coding"

Internet: "But some of his minor ideas are wrong."

Me: "So you think it's wise to ignore his brilliant ideas because of a few nits?"

Internet: "He once invaded Poland."

Me: ???

(though part of me wonders if the "Uncle Bob hate" is actually a clever PT Barnum style marketing campaign for Clean Code)

I'll still take clean code over what some people try to ship any day of the week.
Isn't their example even simpler? In JS/TS, for example:

```ts

  const pluralizePhrase = (noun: string, count: number) =>
    count == 1 ? `There is one ${noun}` : `There are ${count || "no"} ${noun}s`;
```
(comment deleted)
There's talk here about the over-abstraction of what should be a simple problem.

Usually when this happens, people are dismissing a toy example, (and they can make the toy even simpler.) I think that is the case here.

String-concatenation in a word game is not worth this abstraction overhead. But I think it's supposed to teach people not to be afraid of usingLongFunctionNamesToTellAStory().

It would probably work better on code like:

  ..
  // Otherwise we gotta make a sibling now
  if ( newNode (&siblingId, a) == NEWNODE_FAIL ) {
    return ADD_FAIL;
  }
  Node *sibling = getNode (a, siblingId);
        
  // And a new place for it to point
  NodeIdx destId = 0U;
  if ( newNode (&destId, a) == NEWNODE_FAIL ) {
    return ADD_FAIL;
  }
  ..
could take on more of a style like:

  if (needSiblingNode()) {
    return ADD_FAIL;
  }
  ..
  pointToNewAddress()
That's also kind of a bad example, because it's using a language with dogshit error handling. In a language like Rust, you'd just do e.g. `let sibling = new_node(...)?;`, and you wouldn't need the extra functions. I'd dislike having to go off and find both `needSiblingNode` and `pointToNewAddress` just to figure out what a few lines of code mean.
My take (using switch expressions)

    private String generateGuessSentence(char candidate, int count) {
        return switch(count){
            case 0 -> "There are no " + candidate +"s";
            case 1 -> "There is one " + candidate;
            default -> "There are " + count + " " + candidate + "s";
        };
    }
And it would look even better with string templates...hopefully soon!
Hey, wait, you can use String format with positional parameters...

    private static String generateGuessSentence(char candidate, int count) {
        return String.format(switch(count){
            case 0 -> "There are no %1$ss";
            case 1 -> "There is one %1$s";
            default -> "There are %2$s %1$ss";
        }, candidate, count);
    }
Writing clean code is easy. If I write it, it's clean. If anyone else writes it, it's not. Time to start a blog.
Ousterhout's book "Philosophy of Software Design" discusses that a developer's goal is to reduce complexity. I also personally maintain this idea at work.

So if the OOP class is confusing, it has not met its goal. One could also argue that functions "do one thing" to reduce complexity, and if that one thing is to return a string, your function has succeeded.

Ousterhout also writes about "deep" functions versus "shallow" functions. "Deep" referring to a function that does a lot, without the caller doing a complex series of functions to get the end result. So subjectively, a single function with a lot of logic would be "more correct" than a class with many shallow methods.

That is my favorite software book. Deep implementations, but shallow interfaces is a good rule of thumb.
Semantic overload is a weird but effective attack on human thought.

Whenever I voice criticism of 'Clean Code', a lengthy, awkward and difficult conversation follow, where I must clarify that I don't dislike writing code that is 'clean', I dislike writing code according to the whims of some dude who wrote a bunch of random practices down in a book he titled 'Clean Code'.

It never goes over well, and my chances of success are inversely proportional to how technical the conversation partner is, and how much of an agenda has.

When a non-technical manager 'helpfully' suggest I adopt best practices from this book to improve code quality, a special kind of pain arises in my soul.

I know I'm not alone in this struggle, the ways of defeating this menace has escaped a generation of programmers.

Same goes for Agile.

Keep up the fight, brother
Can we finally admit that one person’s clean code is another person’s code smell?*

Uncle Bob has caused more wasted time and effort than any other “thought leader” except perhaps Martin Fowler.

His examples are absolutely terrible and really make me question how anyone can read more than a few chapters into this book and still think Uncle Bob is an expert.

It’s subjective. It’s easy to obfuscate code, really hard to make it “clean.”

Personal anecdote: our cto is hung up on making super tiny methods bc of this book. You can’t follow logic because you’re constantly hopping between tiny bits of code all over the project. It’s like trying to read a Wikipedia article that’s been condensed to one run on sentence with every word hyperlinked.

*Terms popularized by consultants selling books and consulting.

Every extremism is bad. Period. ;-)

Yet it is very conforting and comfortable if you are inside its echo chamber.

So all the output of those thought leaders are not to be applied to the letter, but should be part of a whole educational process that also evaluates all the limits of that thinking.

But that takes time to think. Which our modern society has almost outlawed, in its rush for "fastfood for thought" that enables selling more ads and hype

As a side note, if anyone is looking for clean well factored easy to understand code, the golang standard library is a great example.

I learned go this way. I don’t mean I learned the syntax I mean I learned how to structure the code. It’s just really well thought out and extremely pragmatic.

It’s a master class in “clean code”.

"He who lives by the rule book dies by the rule book." --A.J. Rimmer

We use Clean Code in one of our classes here at the university. What I tell my students is that they should take every piece of programming advice, and form their own rationalized opinion about that advice. And no matter what you think the right thing is, half the internet is probably going to disagree with you.

What's important, is that you assess the situation, draw upon your knowledge to code up the "best" solution, and have an opinionated rationale for why you did it that way.

Five years from now, you're going to look back at all your college code and be amazed at how crap it is. But what's important is that by having these opinionated rationales for the work you do, you are continuously learning how to make your code better.

And you're doing it in a flexible way. Sometimes rules should be broken for the greater good.