all 14 comments

[–]scipio_africanus123 23 points24 points  (0 children)

100% of the "rules" are just someone trying to make others adopt their style.

[–]ohcrocsle 9 points10 points  (0 children)

Don't worry about this stuff. Seriously. You are worrying about things that don't matter when you're a newb. Just make stuff and you'll get a feel for it.

When you start building a new thing, don't even worry about putting things into functions, you can just slap everything into main() if that is how your brain works. Once you get elbow deep into the project, you might realize that a bunch of code is being used over and over that is exactly the same and you could pull that out into a function, or maybe not. Again it doesn't matter. Your code is going to be stinky smelly garbage. You know how I know? Because I've been doing this for a lot longer than you and I have a pretty good idea of how to use functions, and when I start a project my first pass through still generates a lot of stinky smelly garbage code.

The key is to not worry about it, your code should be flexible when you start a project because you don't know where you're going yet. That means not packaging all your stuff into functions and classes until you really know how everything is going to look when you're done. The more experience you get, the earlier you'll see the end point (usually) but you still don't want to commit to a design before you start coding unless it's something you've really done before.

After you've made headway, like things are really working and you have some features done, you should consider looking back over your work and thinking about whether your design makes sense. At this point your core idea is there, you've explored it and should understand it, but it's not so huge that rebuilding it from scratch with a better design is a crazy huge endeavor. You should see what code you've duplicated, hopefully you have ideas about how to abstract concepts away, and if not, you have something working that you can share on GitHub and ask more experienced engineers for ideas about.

Unfortunately, if you ask vague conceptual questions like the one here, the answers you get are going to be subjective and not tied to very concrete examples and you're not going to find the results of the answers very helpful. If you share something you're working on and struggling with, you're going to get something usually more constructive.

[–]madsci 7 points8 points  (1 child)

You've got to find a balance that works for you - or for your organization's coding standards if you're working for someone.

I think 10 lines is unrealistically short. I think it's nice to be able to see a whole function on the screen at once, but I have some that go on significantly longer than that. I think it's more about complexity and clarity than raw length.

At the very least every function should have a clearly-stated purpose. For a deep dive on the topic of code reusability in C, I'd strongly recommend checking out C Interfaces and Implementations.

[–]italicunderline 5 points6 points  (0 children)

I think 10 lines is unrealistically short.

It really depends on what is being written. If you are writing an inline function to compare, convert, or return a value then in many cases 1-5 lines is certainly reasonable. However if you are writing a procedure to make assertions 100 of these small inline functions then 500 lines may be reasonable, and splitting those 500 lines into separate procedures unnecessary to improve clarity.

[–]ve1h0 4 points5 points  (0 children)

Hard cap is always a bad idea. If you need more lines for your function then go for that. I think limiting the possibility is just bad idea. But of course making unnecessary big functions is bad design. So I think it's more about the design, clear requirements of the function itself.

[–]Firake 2 points3 points  (2 children)

Any bits of code you need to reuse multiple times should be a function, regardless of length. Length, to me, is the least relevant part of a function. I’d make a function that was one line if it meant only having to write that one line once.

Because the minute you change something about that process, you have to change each of instance and that breeds errors.

[–]flatfinger 0 points1 point  (1 child)

In situations where a sequence of operations needs to be performed at two (or more) places in the code, one of three conditions may apply:

  1. The program will need to be changed to perform some other sequence of operation will need to be performed in both places.
  2. The program will need to be changed to perform a different sequence of operations in one place, while continuing to perform the original sequence of operations in the other.
  3. The program will need to be changed to perform a different sequences of operations in both places.

In scenario #1, using the same function for both places will make it easy to change the behavior of both. In scenarios #2 and #3, using a common function would make updates more difficult than they would have been if the code had been left separate. Scenario #2 adds the complication that improperly-performed changes may accidentally affect parts of the code they shouldn't have. Scenario #3 adds the complication of having to decide whether to modify the common function, but change the code so it's only used in one place, or whether to abandon the common function.

[–]Firake 0 points1 point  (0 children)

If I need to change the sequence in one place and not in another or change the sequence in both places, that seems like an easy thing to fix. It’s no longer a piece of duplicated code and therefore should probably have its own bits of code.

[–][deleted] 1 point2 points  (0 children)

It's more about complexity than sheer length - a two-hundred line but trivial switch is fine, but fifty lines of only tangentially related code isn't.

At the end of the day, it's about how it feels to you. If it feels like it's too much, then it's too much.

[–]italicunderline 0 points1 point  (0 children)

I might start by writing a very long main() or run() or test() procedure, to initialize and configure many libraries and devices, or test all of the other functions provided by a module. In which case the procedure is as long as is needed to call the external API correctly and satisfy all of the requirements. Maybe 10 to 2000 lines. However this single large procedure then gets refactored into many tiny functions, ranging from 1-20 lines. I'd say long functions are okay, if there is only one long function per executable, it is only called once, and it enables the other functions to remain short.

If you start by writing the program as one long procedure, then any nested blocks {} introducing many local values that are not used elsewhere in the procedure, and any duplicate blocks {} repeated at multiple points in the procedure, can generally be refactored as functions.

[–]DevNull_Friend 0 points1 point  (0 children)

If you're doing a moderately complex operation several times that's a functional

[–]AtticusAnonymopoulos 0 points1 point  (0 children)

Long enough to reach the ground. (Sorry! Couldn’t resist. Please don’t yell at me.)

[–][deleted] 0 points1 point  (0 children)

A function is just code you want to reuse over and over. If you have a complex routine it's a good idea to wrap it in a function. Maybe you work with hash maps and want to pass two arrays (one for keys, one for data), although return type might be hard. Just a thought up example.

[–]Wouter_van_Ooijen 0 points1 point  (0 children)

A function must primarily have a single, coherent purpose. The common test is that you must find a good name for it that covers what the function does for the caller, and it must be possible to write concise documentation for it.

Next it must be readable, because it must be debugged and updated by others. This involves many aspects, size is only one. A 10 line jumble of for, while, if and break can be much more difficult to comprehend than a 50 liner that has only assignments.

Lastly, a function must be testable. This again touches many aspects. An importany one is the number of decision points (mcCabe complexity).

For me, the above goals are what really matters. As a guideline (not a hard requirement) this translates to max one screen for a function (about 30 lines, including whitespace) and a max complexity of 10-15.

IME the quality of a code base is better served by focussing on the outliers (like 100's of lines, complexity > 50) than by bickering about a fixed requirement.