Keep functions simple


Link to this posting

Postby Ursego » 21 Feb 2013, 15:40

"Complexity kills. It sucks the life out of developers, it makes products difficult to plan, build, and test."
Ray Ozzie, CTO, Microsoft Corporation

Divide your program into short methods that perform one identifiable task ("separation of concerns"). The main function should call well-named sub-functions which call sub-sub-functions (and so on, and so on...), so a developer can easily "travel" up and down between different levels of abstraction through hierarchies of any depth, each time concentrating only on one level.

That will result in simple overall code even if the whole system is extremely complex, and dramatically facilitate understanding, debugging and enhancement of serious enterprise applications.

A big program should always be written in small pieces, so do that not only if the code will be re-used (i.e. called from more than one place - see Refactor to prevent duplication), but even if it will be called only once.

It's not a problem to have many functions - you can always bring into focus only one of them. When you are concentrated on one particular method, trying to understand what it does and how it works, the question "how many methods do serve this flow - 1, 10 or 100?" doesn't bother you. To get less-or-more familiar with a particular flow, it is usually enough to have a look on its root-level ("main") function. You will dig into the sub-functions only if that is really absolutely necessary. But, most time, it will not be needed. So, why to see all the time something, which is not needed most time, especially if it prevents you from seeing the big picture???

The more lines found in a method, the harder it's to figure out what the method does!

It's very difficult to investigate the logic of ONE intricate toilet-paper-long method. To understand what is fucking going on here the functionality, developers will be forced to read a terrible mix of high-level and low-level code (i.e. code which belongs to different abstraction levels). Kent Beck wrote in his book "Smalltalk Best Practice Patterns":
Keep all of the operations in a method at the same level of abstraction. This will naturally result in programs with many small methods, each a few lines long.

Another quote, found in the Internet:
I prefer to try to keep everything in a function on the same level of abstraction and as short as possibly. Programming is all about managing complexity and short one purpose functions make it easier to do that.


From the book "PL/SQL Best Practices" (true for any programming language, not only for PL/SQL):

Limit execution section sizes to a single page using modularization.

Sure, you're laughing out loud. You write code for the real world. It's really complicated. Fifty or sixty lines? You're lucky if your programs are less than 500 lines! Well, it's not a matter of complexity; it's more an issue of how you handle that complexity.

If your executable sections go on for hundreds of lines, with a loop starting on page 2 and ending on page 6 and so on, you will have a hard time "grasping the whole" and following the logic of the program.

An alternative is to use step-wise refinement (a.k.a. "top down decomposition"): don't dive into all the details immediately. Instead, start with a general description(written in actual code, mind you) of what your program is supposed to do. Then implement all subprogram calls in that description following the same method.

The result is that at any given level of refinement, you can take in and easily comprehend the full underlying logic at that level. This technique is also referred to as "divide and conquer."

Benefits

You can implement complicated functionality with a minimum number of bugs by using step-wise refinement. Local modules and packaged programs play a major role in keeping each executable section small.

A developer can understand and maintain a program with confidence if he can read and grasp the logic of the code.

ONE IDENTIFIABLE TASK

When we say, that each method should perform one identifiable task, let's concentrate on the word "one". If you need to perform 3 different actions in a row, create 3 separate methods, and call them in sequence. This way, you prepare a ready solution if tomorrow you or another developer will need to perform only one of these actions. The smaller lego bricks are, the more flexibility we have. Opositely, if you write one method which does more than one thing, then, in the future, a siamese twin separation surgery will be required - a fragment of your all-in-one method will need to be refactored to a brand new method, which will also require an additional regression test to make sure we have not broken something existing.

From the book "97 Things Every Programmer Should Know":
Next time you are tempted to lump a few things together into one API method, remember that the English language does not have one word for MakeUpYourRoomBeQuietAndDoYourHomeWork, even though it would be really convenient for such a frequently requested operation.


Bjarne Stroustrup, inventor of C++, wrote:

I like my code to be elegant and efficient. The logic should be straightforward to make it hard for bugs to hide, the dependencies minimal to ease maintenance... so as not to tempt people to make the code messy with unprincipled optimizations. Clean code does one thing well.


Bjarne closes with the assertion that clean code does one thing well. It is no accident that there are so many principles of software design that can be boiled down to this simple admonition. Writer after writer has tried to communicate this thought. Bad code tries to do too much, it has muddled intent and ambiguity of purpose. Clean code is focused. Each function, each class, each module exposes a single-minded attitude that remains entirely undistracted, and unpolluted, by the surrounding details.

From the book "Clean Code":

Do One Thing

The following advice has appeared in one form or another for 30 years or more.

FUNCTIONS SHOULD DO ONE THING. THEY SHOULD DO IT WELL. THEY SHOULD DO IT ONLY.

The problem with this statement is that it is hard to know what "one thing" is.

If a function does only those steps that are one level below the stated name of the function, then the function is doing one thing. After all, the reason we write functions is to decompose a larger concept (in other words, the name of the function) into a set of steps at the next level of abstraction.

So, another way to know that a function is doing more than "one thing" is if you can extract another function from it with a name that is not merely a restatement of its implementation.

One Level of Abstraction per Function

In order to make sure our functions are doing "one thing", we need to make sure that the statements within our function are all at the same level of abstraction. Mixing levels of abstraction within a function is always confusing. Readers may not be able to tell whether a particular expression is an essential concept or a detail. Worse, once details are mixed with essential concepts, more and more details tend to accrete within the function.

Reading Code from Top to Bottom: The Stepdown Rule

We want the code to read like a top-down narrative. We want every function to be followed by those at the next level of abstraction so that we can read the program, descending one level of abstraction at a time as we read down the list of functions. I call this The Stepdown Rule.

For database developers:
Encapsulate each SQL statement in a dedicated function/proc
User avatar
Ursego
Site Admin
 
Posts: 131
Joined: 19 Feb 2013, 20:33

Link to this posting

Postby Ursego » 27 Jun 2019, 10:54

From the book "97 Things Every Programmer Should Know":

Beauty Is in Simplicity

There is one quote , from Plato, that I think is particularly good for all software developers to know and keep close to their hearts:

Beauty of style and harmony and grace and good rhythm depends on simplicity.


In one sentence, this sums up the values that we as software developers should aspire to.

There are a number of things we strive for in our code:
• Readability
• Maintainability
• Speed of development
• The elusive quality of beauty

Plato is telling us that the enabling factor for all of these qualities is simplicity.

What is beautiful code? This is potentially a very subjective question. Perception of beauty depends heavily on individual background, just as much of our perception of anything depends on our background. People educated in the arts have a different perception of (or at least approach to) beauty than people educated in the sciences. Arts majors tend to approach beauty in software by comparing software to works of art, while science majors tend to talk about symmetry and the golden ratio, trying to reduce things to formulate. In my experience, simplicity is the foundation of most of the arguments from both sides.

I have found that code that resonates with me, and that I consider beautiful, has a number of properties in common. Chief among these is simplicity. I find that no matter how complex the total application or system is, the individual parts have to be kept simple: simple objects with a single responsibility containing similarly simple, focused methods with descriptive names. Some people think the idea of having short methods of 5–10 lines of code is extreme, and some languages make it very hard to do, but I think that such brevity is a desirable goal nonetheless.

The bottom line is that beautiful code is simple code. Each individual part is kept simple with simple responsibilities and simple relationships with the other parts of the system. This is the way we can keep our systems maintainable over time, with clean, simple, testable code, ensuring a high speed of development throughout the lifetime of the system.

Beauty is born of and found in simplicity.
User avatar
Ursego
Site Admin
 
Posts: 131
Joined: 19 Feb 2013, 20:33

Link to this posting

Postby Ursego » 07 Jul 2019, 09:45

Complex Problems Require Simple Solutions

One of the coolest things I've heard in software was in a long series of lectures given at Google about the deep internals of the unix kernel. A longtime kernel hacker was explaining the locking strategy of kernel components and the witness tool that verifies it:

"We can't do anything complicated in the kernel. It just wouldn't work."

He meant that complex locking strategies would inevitably fail in an environment as complex as the kernel.

This struck me as crazy at first, since the kernel is one of the most complex pieces of software ever conceived. But I realized what he means is that given the necessary complexity of the kernel, there is no way it can afford unnecessary complexity. The only strategy that works over time is simplicity.

I'm not the smartest person programming. I stumble over highly stateful code. Math beyond simple arithmetic is pretty tough for me. But despite my mental shortcomings, I am actually a great programmer. When I look at code I write compared to average code in average projects, even code written by great engineers, I notice that my code is always simpler. My classes do less. My APIs are small and clear. I can't actually grok classes that have too many responsibilities, so my classes always do one thing. I remove and remove from my APIs until they fit neatly in my brain. I could explain any class constellation I've devised to any competent programmer and they would not be overwhelmed.

There's this prevailing idea that complex problems call for complex solutions, or, worse, ugly problems call for ugly code. :evil: I remember the C++ code I saw at Microsoft, full of preprocessor macros, bit twiddling, and hand-rolled data structures, in desperate need of layers of abstraction. Programmers would tell themselves they were solving hard problems, maybe the code should be hard to read.

The reality is just the opposite - the worse the problem, the more important it is to have simple solutions. Even if the nature of a problem is complex, there is a solution that divides the problem into subproblems of manageable complexity. There is an interface that can be put atop that complexity that lets the reader ignore that complexity in the average case.

Now, I've never worked on anything as complex as the unix kernel. But I have also never worked on a project that can afford unnecessary complexity. Competition says no such project could exist.

Source
User avatar
Ursego
Site Admin
 
Posts: 131
Joined: 19 Feb 2013, 20:33

Link to this posting

Postby Ursego » 07 Jul 2019, 10:06

WHAT SHOULD BE THE MAXIMUM FUNCTION LENGTH?

No philosophy question has a mathematically precise answer. Our question is not an exception, but some ideas (sometimes pretty different) in that concern do exist. The universal principle is: the shorter - the better. In ideal, a function should be no longer than one screen (excluding the header comments and the parameters validation section, which should be visually divided from the beginning of the "interesting part" by an empty line or comments). Two screens are still acceptable, but three screens already bring up the issue of incorrect functions organization - unless the function performs a long "black work" which cannot (or should not) be broken into pieces - for example, processing of a big number of fields, returned by an external service, when each field is processed in a few lines (but if all the fields are processed in a same or in a similar way, it makes sense to take that processing into a generic function, and call it for each field).

The next acceptable advice was found by me in a programming book:
functions should contain up to approximately 100 lines of code not including comments.


:!: Pay attention: the problem of too long functions usually goes together with the problem of extra indentation which is discussed here!

From the book "97 Things Every Programmer Should Know":
Make your functions short and focused on a single task. The old 24-line limit still applies. Although screen size and resolution have changed, nothing has changed in human cognition since the 1960s.


Steve McConnell writes in "Code Complete":
A large percentage of routines in object-oriented programs will be accessor routines, which will be very short. From time to time, a complex algorithm will lead to a longer routine, and in those circumstances, the routine should be allowed to grow organically up to 100-200 lines.

Let issues such as the routine's cohesion, depth of nesting, number of variables, number of decision points, number of comments needed to explain the routine, and other complexity-related considerations dictate the length of the routine rather than imposing a length restriction per se.

That said, if you want to write routines longer than about 200 lines, be careful. None of the studies that reported decreased cost, decreased error rates, or both with larger routines distinguished among sizes larger than 200 lines, and you're bound to run into an upper limit of understandability as you pass 200 lines of code.


And now - ideas of different developers, found by me in the Internet:
When reading code for a single function, you should be able to remember (mostly) what it is doing from beginning to the end. If you get partway through a function and start thinking "what was this function supposed to be doing again?" then that's a sure sign that it's too long...

Usually if it can't fit on my screen, it's a candidate for refactoring. But, screensize does vary, so I usually look for under 25-30 lines.

IMO you should worry about keeping your methods short and having them do one "thing" equally. I have seen a lot of cases where a method does "one" thing that requires extremely verbose code - generating an XML document, for example, and it's not an excuse for letting the method grow extremely long.

...you should make functions as small as you can make them, as long as they remain discrete sensible operations in your domain. If you break a function ab() up into a() and b() and it would NEVER make sense to call a() without immediately calling b() afterwards, you haven't gained much.

Perhaps it's easier to test or refactor, you might argue, but if it really never makes sense to call a() without b(), then the more valuable test is a() followed by b().

Make them as simple and short as they can be, but no simpler!"

As a rule of thumb, I'd say that any method that does not fit on your screen is in dire need of refactoring (you should be able to grasp what a method is doing without having to scroll. Remember that you spend much more time reading code than writing it).

~20 lines is a reasonable maximum, though.

Aside from method length, you should watch out for cyclomatic complexity i.e. the number of different paths that can be followed inside the method. Reducing cyclomatic complexity is as important as reducing method length (because a high CC reveals a method that is difficult to test, debug and understand).

During my first years of study, we had to write methods/functions with no more than 25 lines (and 80 columns max for each line). Nowadays I'm free to code the way I want but I think being forced to code that way was a good thing ... By writing small methods/functions, you more easily divide your code into reusable components, it's easier to test, and it's easier to maintain.

I often end up writing C# methods with 10 - 30 lines. Sometimes I find longer methods suitable, when it’s easier to read/test/maintain.

My problem with big functions is that I want to hold everything that's happening in the code, in my head all at once. That's really the key. It also means that we're talking about a moving target.

Dense perl should appear in shorter chunks than padded VB. And, worse, at only 38, I'm finding that while my abilities have matured in nice ways, by core ability to grok a bunch of code is diminishing and thus the size of code blocks I want to handle is shrinking.

Because the goal is usability, the one screen rule really does make sense even though you can point to seeming flaws like varying screen resolutions. If you can see it all at once without paging around the editor, you are very much more likely to handle it all as a block.

What if you're working on a team? I suppose the best thing for the team would be to determine the lowest common denominator and target that size. If you have someone with a short attention-span or an IDE set up displaying around 20 lines, that's probably a good target. Another team might be good with 50.

And yeah, whatever your target is, sometimes you'll go over. 40 lines instead of 25 when the function can't really be broken up reasonably is fine here and there. You and your partners will deal with it. But the 3K line single-function VB6 modules that exist in some of my employer's legacy suites are insane!


From the book "Clean Code":

Functions should not be 100 lines long. Functions should hardly ever be 20 lines long.

How short should a function be? In 1999 I went to visit Kent Beck at his home in Oregon. We sat down and did some programming together. At one point he showed me a cute little Java/Swing program that he called Sparkle. It produced a visual effect on the screen very similar to the magic wand of the fairy godmother in the movie Cinderella. As you moved the mouse, the sparkles would drip from the cursor with a satisfying scintillation, falling to the bottom of the window through a simulated gravitational field. When Kent showed me the code, I was struck by how small all the functions were. I was used to functions in Swing programs that took up miles of vertical space. Every function in this program was just two, or three, or four lines long. Each was transparently obvious. Each told a story. And each led you to the next in a compelling order. That's how short your functions should be!
User avatar
Ursego
Site Admin
 
Posts: 131
Joined: 19 Feb 2013, 20:33




IF you want to ((lose weight) OR (have unbelievable (brain function AND mental clarity))) THEN click:




cron
free counters

eXTReMe Tracker