Earlier quoted context omitted.
Splitting things up into multiple independent translation units enables incremental compilation. One function per file is the most extreme version of this. For example: https://git.musl-libc.org/cgit/musl/tree/src/stdio
That seems like a problem for compiler optimizers to solve, not programmers.
An Experiment on Code Structure
11–20 of 62 posts
Re: An Experiment on Code Structure
#12According to GitHub, the totals are: backendA: 11 files, 1 directory, 799 lines (676 sloc), 23.56KB backendB: 23 files, 5 directories, 1578 lines (1306 sloc), 42.26KB It's approximately twice as big for the same functionality, and I had to spend a lot more time "digging" through the second one to get an overall idea of how everything works. Jumping around between lots of tiny files is a big waste of time and overhead…
Yeah I tend to like something like a semantic compression approach: I'll start in a single file, and then split it into separate files organized by domain as the length of the file starts to get unwieldy. And so on into more files and later subdirectories as the program grows.
In my opinion it's much better to let the "needs of the program" dictate code and filesystem structure rather than some academic ideas about how a program should be organized. As you say, when I've worked on projects which are very strict about adopting a particular structure, a lot of time ends up being wasted figuring out how to map my intent to that structure rather than just writing the damn code.
Re: An Experiment on Code Structure
#13According to GitHub, the totals are: backendA: 11 files, 1 directory, 799 lines (676 sloc), 23.56KB backendB: 23 files, 5 directories, 1578 lines (1306 sloc), 42.26KB It's approximately twice as big for the same functionality, and I had to spend a lot more time "digging" through the second one to get an overall idea of how everything works. Jumping around between lots of tiny files is a big waste of time and overhead…
Digging through files manually (I.e. Using a mouse) is painful, but your IDE is your friend. It takes me less than 3 seconds to search and open any file of the codebase I currently work in (it has a bit more than 2k files). And having a sane hierarchy means I type the folder / file name as I remember it, and filter the search results on-demand.
Re: An Experiment on Code Structure
#14Re: An Experiment on Code Structure
#15Earlier quoted context omitted.
I’ve come to the conclusion that half or more of the rules we have about “clean” are about avoiding merge conflicts. Few things have been consistently disappointing to me as the inability of coworkers and myself to reason about merges correctly. There are three hard things in software and merges are #3. If anyone ever figures out how to make merges Just Work, then I expect a lot of pressure toward decomposition over…
Can you elaborate a little please? It's unclear to me if you are taking about merging data, code changes, or something else
Re: An Experiment on Code Structure
#16According to GitHub, the totals are: backendA: 11 files, 1 directory, 799 lines (676 sloc), 23.56KB backendB: 23 files, 5 directories, 1578 lines (1306 sloc), 42.26KB It's approximately twice as big for the same functionality, and I had to spend a lot more time "digging" through the second one to get an overall idea of how everything works. Jumping around between lots of tiny files is a big waste of time and overhead…
I suspect it is the same kind of thinking that says all functions should be very small (without reference to whether each function provides a single meaningful behaviour). Locally, this keeps things relatively simple, but it ignores the global issue that now there are potentially many more connections to follow around and everything becomes less cohesive. As far as I’m aware, such research as we have available on this still tends to show worse results (in particular, higher bug frequencies) in very short and very long functions, but that doesn’t stop a lot of people from making an intuitive argument for keeping individual elements very small.
A similar issue comes up once again in designing APIs: do you go for minimal but complete, or do you also provide extra help in common cases even if it is technically redundant? The former is “cleaner”, but in practice the latter is often easier to use for those writing a client for that API. Smaller isn’t automatically better.
Re: An Experiment on Code Structure
#17Earlier quoted context omitted.
I’ve come to the conclusion that half or more of the rules we have about “clean” are about avoiding merge conflicts. Few things have been consistently disappointing to me as the inability of coworkers and myself to reason about merges correctly. There are three hard things in software and merges are #3. If anyone ever figures out how to make merges Just Work, then I expect a lot of pressure toward decomposition over…
Can you elaborate a little please? It's unclear to me if you are taking about merging data, code changes, or something else
But too much decomposition also hurts reading comprehension. So if the specter of merge conflicts went away you’re left with readability, which will settle out to somewhere between the extremes of decomposition. I’m suggesting that would result in somewhat larger methods. Especially where crosscutting concerns intersect each other.
Re: An Experiment on Code Structure
#18- Version control conflicts: if developers are editing the same files all the time, there will be more conflicts and therefore more tasks related to resolve them, such as merging, re-testing, fixing bugs related to a bad merge, re-attempting the merge, etc.
- Code so complicated that becomes easy to misunderstand, and a source of an unusually large amount of bugs.
- Code so complicated that cannot be reliably tested without spending an unreasonable amount of time or relying on opaque testing methods.
- Code so complicated that increases the dependency on specific team members, usually the authors, so that the team cannot function optimally if they're unavailable or unwilling to collaborate.
- Code so complex that is impossible for an engineer to determine if the system is in a healthy state, diagnose a problem, obtain a reproduction step from a bug report...
- Code so poorly organized that developers fail to find implementations for a particular problem, causing them to implement the same thing again.
- Having multiple variations of the same code, so when a bug is found you may have to refactor multiple versions of the same code to fix the problem, if you manage to find them all.
And the list goes on and on. And a solution to these problems can have to do with how code is structured, and conventions/good practices.
If I see a piece of code that needs to know about 40 classes and 50 methods to produce a result, I know that it is likely going to be a pain to maintain. It's not subjective.
If I see a function with 1000 lines of code and a cyclomatic complexity of 500, I know that it may take at least 500 test cases to test it and will be a pain to maintain in a way that doesn't break. That is not subjective.
Re: An Experiment on Code Structure
#19Each kind of state change needs to flow through the code in a consistent direction to avoid unexpected state mutations (like sap flows through a tree).
Another developer should be able to understand all the main parts of my program just by looking at the main entry point/file (the trunk of the tree).
Also, no dependency injection should be used; all dependencies need to be listed explicitly and be trackable to its source file. Dependencies need to either be explicitly imported where they are used or passed down through the branches explicitly via method or constructor arguments. Traceability is very important.
About classes/abstractions, they should be easy to explain to a non-technical person. If you can't explain a class or module to a non-technical person, it shouldn't exist because it is a poor abstraction.
Re: An Experiment on Code Structure
#20These days, when I start a new project, I think of my code as a tree. I start at the trunk and write the branches. Each kind of state change needs to flow through the code in a consistent direction to avoid unexpected state mutations (like sap flows through a tree). Another developer should be able to understand all the main parts of my program just by looking at the main entry point/file (the trunk of the tree). Als…
Isn't the latter precisely dependency injection?
https://en.wikipedia.org/wiki/Dependency_injection#Construct...