zakirullin / cognitive-load

🧠 Cognitive Load is what matters
Creative Commons Attribution 4.0 International
3.24k stars 65 forks source link

[Comments] OMG YES #8

Open JohnnyWalkerDigital opened 1 year ago

JohnnyWalkerDigital commented 1 year ago

This isn't an "issue" but I just wanted to write a note of support 🎉

A lot of the useful tips and tools mentioned in this document are worth learning and using. OOP, Design Patterns, DDD, small method lengths (side note: never heard anyone say "small classes" -- that one makes no sense to me) are all extremely helpful in the pursuit of easily maintainable/extendable/scalable code... IF they're applied correctly.

I guess the issue is that you might need to apply them incorrectly a few times to learn their benefits.

But as I see it, a general rule of thumb is: Is the thing I'm trying to apply to my code making my FUTURE life harder or easier? When I return to this codebase in six months and have completely forgotten why I made the decisions I did, is the thing I'm trying to apply now actually going to make it easier for me to get back into it?

Or: Is it OK to leave this method long, breaking that recommendation, as long as the method is well-structured, has clearly understandable variables and logic, and has comments to explain what's going on?

You just want to make your future life as easy as possible for yourself (which also makes it easier for your follow team mates).

Rather than trying to be clever, using obscure functions and trying to reduce the number of lines of code, we should be focussing on reducing the number of lines you need to scroll up in order to understand what's going on. That's a much better goal than playing code golf with yourself/your team.

Impress me with how BASIC you've made everything (and, when applied correctly, the recommendations mentioned in this article with HELP with that).

zakirullin commented 1 year ago

@JohnnyWalkerDigital btw can you advice something to replace "small classes" with? I am not a native speaker unfortunately, and some wording maybe kinda incorrect

pixelbrackets commented 1 year ago

:100: :clap: My favorite quote regarding cognitive load in code development is

Code is a way you treat your coworkers. https://twitter.com/mfeathers/status/1031176879577780224

YOU might be able understand a challenging code base, and remember links, abbreviations and “historical reasons”. Your colleagues don't. Neither will you, in a year from now or after a job change.

Writing code and designing software architecture that meets complex requirements, but is still readable and easy to understand, is something that will ultimately help both your colleagues and yourself.

JohnnyWalkerDigital commented 1 year ago

I've just never heard the advice "classes should be small" before. It makes no sense to me taken literally, so perhaps I'm misunderstanding it. Classes should have a single responsibility, and maybe that's what it's referring to?

Even if "classes should be small" is referring to ensuring that your single responsibility class doesn't sprawl (which makes some sort of sense -- I'd still not use the word "small" personally, because I think it puts the emphasis on the wrong place, which would really be about "responsibilities" growing), it wouldn't sit in your article where it does, as there's never a situation where this would be bad...

If there's one thing to improve your code/reduce cognitive load, adhering to SRP would be it. It's all about keeping things simple and reducing interdependencies... both of which are in line with your article.

Personally I'd remove that reference altogether. And also I'm not really sure of a context where classes and methods could be used interchangeably... but maybe that's me?

zakirullin commented 1 year ago

@JohnnyWalkerDigital > I've just never heard the advice "classes should be small" before. That I borrowed from Clean Code, If I am not mistaken

Chapter 10: Classes Classes should be small. Classes should be responsible for only one thing and should have only one reason to change (Single Responsibility Principle).

The second point makes sense of course, but again, people tend to emphasize too much on the 1st point

pixelbrackets commented 1 year ago

Proper discussion with answers which exactly show the point: some developers suggest to keep »classes as small as possible« to have glorious »clean code«: https://softwareengineering.stackexchange.com/questions/125357/keep-my-classes-and-methods-as-small-as-possible

JohnnyWalkerDigital commented 1 year ago

@pixelbrackets Yes indeed. That illustrates the point very well and I agree with the answers that say "small classes" is a useful and helpful intention... expressed poorly. I cracked open my copy of Clean Code to take a look. As you might expect, Uncle Bob fills the confusion gap very succinctly:

The first rule of classes is that they should be small. The second rule of classes is that they should be smaller than that.

With functions we measured size by counting physical lines. With classes we use a different measure. We count responsibilities.

As with all programming best practices, people who struggle with them usually do so because they've misinterpreted them.

Edit: Hmm, re-reading "Too many small methods, classes or modules" (had to skim read before). Not sure I agree with that entire section to be honest. It sounds like someone taking small classes to an extreme. The idea that one class should have many responsibilities, and that long complex methods are preferable to shorter ones, flies in the face of my experience.

Sounds like I should read "A Philosophy of Software Design" :)

zakirullin commented 1 year ago

We can continue discussion if you don't mind :)

The idea that one class should have many responsibilities, and that long complex methods are preferable to shorter ones, flies in the face of my experience.

That's not really what I had in mind. Maybe some article modifications are needed 🤔

By no means this is a call to write god objects with many responsibilities

JohnnyWalkerDigital commented 1 year ago

Ok, I've just learned more about John Ousterhout's work. I think I understand why I was having so much difficulty Too many small methods, classes or modules. However, I also agree with Robert Martin's arguments that classes should have single responsibilities...!

There must be a middle-path.

Let's see if I can communicate why, or even if what I'm about to say makes any sense...!

As you know, Ousterhout is concerned with the cost of complexity, and he talks about how Deep Modules are preferable to Shallow Ones. He says that a simple interface with complex functionality is more desirable than vise versa... and it's hard to disagree!

For him it's about the cost of complexity vs the reward.

Screenshot 2023-05-25 at 21 43 56

So, if we have to pay 1 mental unit (representing cognitive load) to use a module's interface, then it would be great to get 10+ mental unit's worth of functionality in return! Similarly, if an interface is extremely complicated, but offers little in functionality, then we might pay 10MU to get our heads around the interface in return for 1MU functionality. Not good.

But if we had to pick one out of the two, I'd say that the most important is the interface. Yes, taken literally then both are equally important, but in practice the more complicated the interface, the less likely a net positive outcome will be.

We don't ever want to be in a situation where we're trying to add functionality to justify a complicated interface in order to balance Ousterhout's equation(!) Plus our brains don't appreciate the reward element: A complex interface is a complex interface.

Therefore, we should all focus our programmer mental energy on writing simpler interfaces that hide the most amount of complexity it can.

Which brings us to Bob Martin...

I think when Martin wrote, "the first rule of classes is that they should be small. The second rule of classes is that they should be smaller than that" he wasn't talking about the functionality of the classes. He was talking about the interfaces.

That is to say: He was trying to simplify a class to the point where the complexity cost for using it was reduced as much as possible. As he wrote in Clean Code:

The name of a class should describe what responsibilities it fulfills. In fact, naming is probably the first way of helping determine class size. If we cannot derive a concise name for a class, then it’s likely too large. The more ambiguous the class name, the more likely it has too many responsibilities.

And:

Every sizable system will contain a large amount of logic and complexity. The primary goal in managing such complexity is to organize it so that a developer knows where to look to find things and need only understand the directly affected complexity at any given time. In contrast, a system with larger, multipurpose classes always hampers us by insisting we wade through lots of things we don’t need to know right now.

In other words, just like Ousterhout, his concern is with reducing the cognitive load on the programmer.

As he states:

[Assuming the functionality is the same]...a system with many small classes has no more moving parts than a system with a few large classes. There is just as much to learn in the system with a few large classes. [Note that he's talking specifically about the interfaces -- fewer classes means fewer, but more complex, interfaces.] So the question is: Do you want your tools organized into toolboxes with many small drawers each containing well-defined and well-labeled components? Or do you want a few drawers that you just toss everything into?

I would say that Martin is arguing for the same thing as Ousterhout.

Unfortunately this has been misconstrued into abstracting things to an absurd level: "If it can be abstracted into its own class, it should be". I wasn't even aware that people had come to this conclusion from reading Martin's words.

I believe that both Ousterhout and Martin are equally concerned with simplifying interfaces in order to reduce programmer cognitive load, but Martin's goal was lost in translation. People have been blindly applying Martin's words without thinking about the supposed benefit.

Ousterhout reframing this same concern another way helps highlight this danger: you can go too far and abstract things to where your interface costs more than the functionality you get back.

I've never been taught to abstract to such a destructive granular level -- but now I'm aware of it, I don't think your examples really explain the problem clearly enough. (Sorry!) If it did, I would have wholeheartedly agreed with the point you were making.

I know you'd like to me to try and offer suggestions for a clearer way to explain what Ousterhout calls "classitis" (ie. an abundance of shallow classes). I will mull it over!

What do you think? 🙂 🤔

PS - I love John Ousterhout now! Thanks for making me aware of his work.

TiffQ commented 1 year ago

I'm a product owner not a developer and even for me this hit the nail on the head. I work on projects where my dev team is constantly changing so overly complex structures make the learning curve for each new team member that much more difficult and the time to usefulness as a team member that much longer. I am going to share this with the senior mgrs in the hopes of some change of practices. I found this via TDLR newsletter. Thank you for taking the time to articulate this.

zakirullin commented 1 year ago

Together with @nkopylov we've just added a conclusion, hope you like it:

The intricate and multifaceted nature of cognitive load within the realm of comprehension and problem-solving necessitates a diligent and strategic approach in order to navigate the complexities and optimize mental capacity allocation.

Do you feel it? The above statement is difficult to understand. We have just created an unnecessary cognitive load in your head. Do not do this to your colleagues.

Do not code in gibberish.

TiffQ commented 1 year ago

I read that, thought that soberly academic and complex. Then realized that was a demo. I like it!

On Fri, May 26, 2023, 15:10 Artem Zakirullin @.***> wrote:

We've just added a conclusion, hope you'll like it:

The intricate and multifaceted nature of cognitive load within the realm of comprehension and problem-solving necessitates a diligent and strategic approach in order to navigate the complexities and optimize mental capacity allocation.

With the above statement we have just created an unnecessary cognitive load in your head. Do not do this to your colleagues.

Do not code in gibberish.

— Reply to this email directly, view it on GitHub https://github.com/zakirullin/cognitive-load/issues/8#issuecomment-1564453343, or unsubscribe https://github.com/notifications/unsubscribe-auth/BADUJD2SEWI2VRQJHMWELH3XIC2T5ANCNFSM6AAAAAAYOWBDSQ . You are receiving this because you commented.Message ID: @.***>

zakirullin commented 1 year ago

@pixelbrackets I believe that with the conclusion above we conveyed the main idea of your quote as well :)

đź’Ż đź‘Ź My favorite quote regarding cognitive load in code development is Code is a way you treat your coworkers.

zakirullin commented 1 year ago

@JohnnyWalkerDigital Thanks for such a throughout analysis! Really, you have a very strong grip of the topic. Sorry it took long to respond.

We definitely need to refine this section, because more and more people are asking questions about this exact section.

I think when Martin wrote, "the first rule of classes is that they should be small. The second rule of classes is that they should be smaller than that" he wasn't talking about the functionality of the classes. He was talking about the interfaces.

I think he wasn't talking about interfaces. I believe he was talking about the implementation. He was talking the exact same thing about small methods. This topic was greatly covered here.

I know you'd like to me to try and offer suggestions for a clearer way to explain what Ousterhout calls "classitis" (ie. an abundance of shallow classes). I will mull it over!

That would be so nice, please proceed!

zakirullin commented 1 year ago

For starters I have provided deep module example from John Ousterhout's book