Hacker News new | ask | show | jobs
by kangar00 3683 days ago
> If you cannot figure out in one minute what a C++ file is doing, assume the code is incorrect.

This statement at first resonated with me, and then I thought about it: this doesn't reduce the complexity of the overall application or service, it just means that one file is simple. You could have 10,000 files instead of 1 much shorter one; is that any more simple?

3 comments

Yes. If each file makes sense in isolation then the whole will as well. Just splitting code into lots of files won't necessarily produce files that you can figure out in 1 minute though (you have to define the boundaries between files such that they make sense).
I disagree. A complicated function may be made of a bunch of statements where each statement makes sense easily. The entire function may still be complicated. The same argument can be extended for files and projects. Even if each file is simple, if the code in those files interact with each other in a complicated manner, the project becomes complicated. This can happen despite having neat boundaries between files. Nothing stops a new programmer from writing new simple files that interact with the existing files in a complicated manner. Simplicity of source code in individual files or functions is just one of the factors behind a simple project. Simplicity of design has to go hand in hand with it.

On the other hand, a couple of files may be very complicated but the entire project could still be simple if those complicated files hide the complexity behind neatly exposed functions, and the remainder of the project does not make use of those functions in a complicated manner.

> A complicated function may be made of a bunch of statements where each statement makes sense easily. The entire function may still be complicated.

Statement yes, but I avoid them where possible - the complexity comes from their interactions because their interactions are unmanaged, implicit and arbitrary. If you make each function an expression made up of expressions and functions, then I think it becomes true that if each expression makes sense easily then the whole will also make sense easily.

As far as the complexity of programs are concerned, there is a similarity between statements at one level of abstraction and functions at a higher level. I have seen many cases where small functions have been assembled into complicated programs. These programs often have a proliferation of 'helper' classes and functions, where you have to trace through long series of calls to get to where the work is done. They often seem to come from a poor design that has been repeatedly patched instead of fixed, or from programmers who write functions because they think they will be part of the solution, but not backing out and replacing them them when they find a complication they had not anticipated.

Using small functions is a necessary, but not sufficient, condition for making understandable code.

I think what you're describing is a case where you can't understand what those helpers do, and therefore can't understand what the function that calls them does. I maintain that if each individual function makes sense then the whole will too.
This holds if the small functions are built around a coherent top-down design, respecting each other's invariants. Once the project is too large to fit in one's head, it is no longer sufficient for each function to be 'correct' in a local sense.
Sensible, understandable functions can be assembled into complicated, incomprehensible programs in exactly the same way that the sensible, understandable operators of a programming language can.
In my opinion this only shift the problem from coding clean code to handling hundreds or thousands of simple and small source files. This will make much more complicated to handle a large project because everything is scattered in such an extend that a developer spend more time searching thru the include list of files than understanding what the code is actually doing. Implementing complex functionality is going to be a nightmare and the end, everything is going to be merged in a single translation unit anyway. But this is just my personal opinion..
> In my opinion this only shift the problem from coding clean code to handling hundreds or thousands of simple and small source files. This will make much more complicated to handle a large project because everything is scattered in such an extend that a developer spend more time searching thru the include list of files than understanding what the code is actually doing.

I've worked on a number of very large codebases and that simply isn't my experience. If code is easy to understand it tends to make good use of the domain language and therefore also be easy to search.

> Implementing complex functionality is going to be a nightmare

The opposite, in my experience. The only maintainable way to implement complex functionality is to break it into small pieces.

> everything is going to be merged in a single translation unit anyway.

That's the compiler's business. I don't care one way or the other about its implementation details.

> That's the compiler's business. I don't care one way or the other about its implementation details.

Actually, you do- for at least several reasons.

1. If the runtime or compiler were to have problems with interdependencies.

2. If the compiled code that will actually be executed or the application or service itself across cores, processors, VMs, geography at runtime takes longer to run because of its compiler implementation, that might make it more expensive or too slow for your needs or to compete.

3. There may be a security flaw in the compiler, e.g. https://www.cvedetails.com/vulnerability-list/vendor_id-72/p...

4. The compiler may have a bug or problem prohibiting you from finishing your code in a timely manner, e.g. https://gcc.gnu.org/onlinedocs/gcc-4.0.4/gcc/Cross_002dCompi... or http://www.securitycurrent.com/en/writers/paul-robertson/mot...

5. The compiler may lack other required functionality or features.

lmm's statement was clearly intended to be taken in the context of the statement he was replying to. While your points are valid in general, the fact that the functions will generally be composed into a single translation unit is not an argument against the benefits of making them small.
I apologize. I assumed he was generalizing.
Felt like this after doing lisp/python coming from Java. In the end it's about being sensible on reducing a system size, no matter where the 'unit' is.
Depends how well the project is structured. Imagine that you're writing a function that adds some values to a hashmap. Would you rather have the the logic, the hashing, and the datastructure details in that function? Getting a shorter function and reduced complexity in that function is great, even if it doesn't affect the complexity of the whole project.

If the modules are well-designed, you can ignore how the hashmap works and the details of hashing itself. You'll get at least 4 extra files, but yes, it's very likely worth it.