8 ms·
I agree with this as it puts emphasis on semantics rather than syntax and encourages focusing on intentionally similar code rather than unintentional. A relate
by epage 4y ago
I agree with this as it puts emphasis on semantics rather than syntax and encourages focusing on intentionally similar code rather than unintentional.
A related principle is what I call code locality. Instruction locality is the grouping of related instructons so they can be the CPU's cache (can an inner loop fit all in cache). Similar for data locality. Code locality is for humans to discover and remember related code. As an example of this is those times you make an internal function because you have to but it has a terrible abstraction (say a one-off comprator for a sort), its best for comprehending the caller to be near where its needed within the same file rather than in a separate file or in a dependency in another repo.
Applying code locality to SPOT, when you do need multiple sources of truth, keep them as close together as possible in the code.
- layer8 4y agoRight, “point” can mean any notion of “close vicinity” whenever it’s not practical to reduce something down to literally the same single syntactic expression.
- gary_0 4y agoOn a similar note, tree/graph structures should be avoided versus lists unless there is a good reason. Flat is better than nested. A linear block of code is far easier to reason about than a network of function calls, or (heaven forbid) a class hierarchy. Not that such tools don't have their place, but I've seen too much convoluted code that has broken simple things into little interconnected bits for no reason other than someone thinking "this is how you're supposed to write code" and having fun adding abstractions they Ain't Gonna Need.
- saltsucker 4y agoI'm not sure I follow... Can you provide an example? (junior dev here) If I understand some of it correctly, I was contemplating this when I started writing functions for "single functional concepts" like, "check for X; return true or false", then called each of those functions sequentially in a single "run" function. Is that what you mean? I found that approach much easier to test the functions and catch bugs, but your comment seems to go against that.
- BlargMcLarg 4y agoDisclaimer, it's mostly a personal thing. Dividing things into methods mostly gives the benefit of naming and reuse. If you can skip the method's content and reasonably assume it works in some way, it can make reading easier. If the reader instead feels inclined to read it, they now have to keep context of what they read, jump to the function (which is at a different position), then read the content of the function, then jump back while keeping the information of the function in mind. In bad cases, this can mean the reader has to keep flipping from A to B and back multiple times as they gain more understanding or forget an important detail. The same thing happens with dividing things up in multiple classes. Don't need to read other classes? Great. Do need to read other classes? Now you have to change navigation to not only vertical, but horizontal as well (multiple files). Some people struggle with it, some people hate it, other people prefer it. You're basically making a guess what's best in this scenario, and there isn't a silver bullet. The extreme examples are easy to rationalize why they are bad. The cases closer to the middle, not so much.
- gary_0 4y agoSpecifics might depend on the language, domain, and team (and individual preference), so it's hard to avoid being general. I would say some junior devs can get too fixated on hierarchies and patterns and potential areas of code re-use, though; instead, they should try to write code that addresses core problems, and worry about creating more correct abstractions later. Just like premature optimization is the "root of all evil", the same goes for premature refactoring. This is the rule of YAGNI: You Ain't Gonna Need It. Don't write code for problems you think you might have at some indeterminate point in the future. When it comes to testing, TDD adherents will disagree, but if you ask me it's overkill to test small private subroutines (or even going so far as to test individual lines of code). For example, if I have a hashing class, I'm just going to feed the hash's test vector into the class and call it done. I'm not going to split some bit of bit-shift rotation code off into a separate method and test only that; if there's a bug in that part, I'll find it fast enough without needing to give it its own unit test. That's what debuggers are for. All the unit test should tell me is whether I can be confident that the hashing part of my code is working and won't be the cause of any bugs up the line. Obviously I'm not in the "tests first" camp; instead I write tests once a class is complete enough to have a clearly defined responsibility and I can test that those responsibilities are being fulfilled correctly.
- PathOfEclipse 4y agoI think this can be generalized even further to the "principle of least power": https://www.lihaoyi.com/post/StrategicScalaStylePrincipleofLeastPower.html https://www.lihaoyi.com/post/StrategicScalaStylePrincipleofL... The graph data structure, I believe, is the most generic of data structures, and it can be used to represent any other data structure. This arguably makes it both the most powerful data structure and the, IMO, the one of last resort.
- gary_0 4y agoYes, that's kind of a "fewer moving parts" way of looking at it. To be clear, I (like the parent post) was using data structures as a metaphor for the organization of source code: "trees" being code that is conceptually like a branching flowchart with many separate nodes, and "lists" being lines of code kept together in one function/class/file (which the parent post points out has the benefit of "locality").
- hinkley 4y agoWhile it is a little easier to deal with flat data than tree data, the real test of this comes when you process the data. Trees invite recursion, and as the logic grows telling what is going on gets more and more difficult. While it's easier to process trees recursively, it is possible to iterate over it. It's easier to process lists iteratively, but it is possible to handle them recursively. Some people do, much to the detriment of all of their coworkers. I'm trying to unwind some filthy recursive code on a side project at work. There were two bugs I knew about going in, and two more have been found since, while removing mutually recursive code in favor of self recursion, and then modifying that to iteration. Iteration is much, much easier to scale to add additional concerns. Dramatically so when in a CD environment where a change or addition needs to be launched darkly. Lists can help, especially with how carefully you have to watch the code. They're not the only solution, they're just the easiest one to explain without offering a demonstration.
- BurningFrog 4y agoThis is a pretty good description of the motivation for OOP.
- galaxyLogic 4y agoRight, if the "truth" is encapsulated inside an Object, it is the single location for that truth. If you want a "different truth" create another object. And never assume that the truths of different objects must be the same. That is a difficult condition to achieve in practice we often code based on what we know a method-result "must be, therefore it is ok to divide by it etc."
- AnimalMuppet 4y agoEspecially for an object that has to have some consistency between its members, all the code that has to maintain that consistency (or that can break the consistency, same thing) should be in the same place - functions of that class. So if you ever see a class that has the data in an inconsistent state, you have a very small set of places to look for the culprit.
- sime2009 4y agoA closely related rule of thumb for organising code is: "Things which change together, belong together."
- bcrosby95 4y agoI usually say: if things tend to change together, then they should be closer together. This is also why using layers as your primary method of organization is usually a bad decision. You're taking things that change together and strewing them about in different top level directories.
- slaymaker1907 4y agoI like your terminology of code locality. Personally, I've always thought of it as being the "ctrl+f" principle. If I'm reviewing a PR or looking at the source in GitHub, it's a lot easier if I can find definitions via "ctrl+f" without resorting to an IDE. Sure, it's probably best practice to checkout code changes I'm reviewing and open them in an IDE, but that often isn't what happens in practice and if I'm reading third party code, configuring the IDE to understand the project might take a lot of effort. The stronger rule is that it should not be necessary to use a stronger tool than a project wide find to lookup references to some function/data structure.