Besides Spaghetti Code there is a development practice which is relatively common and yet people usually fall for it. I like to call it: flag oriented programming.
People usually think it is a good idea to use flags. They create a flag to store states of entities and think the problem is solved. Actually it sometimes duplicates the number of possible new problems.
I once tried to convince a friend not to use it. In a part of the code, I asked him: "Look, how can you guarantee that the states of the flags are consistent here?", he then added code just before the line to check for consistency of the flags. I then said that if the flags fell into an inconsistent state, then it should be fixed there, not when they are checked. He tried another fix: created "set methods" for each of the flags, each method called a function "checkFlags" which detected inconsistencies in the flags and automatically set them to a consistent state.
I continued: "Look, every time you add a new flag you will have to check this function and infer, among possible combinations, which are valid and which are not. If you need states, use an enumeration with few states all of which are consistent."
That is the problem with flag-oriented programming: each new flags doubles the number of possible states and it is very complicated to guarantee consistency between them considering all possible combinations and temptation to just add, set and check a new flag when writing code.
The thermos doesn’t have a toggle (flag) on the outside that the operator sets to “hot” or “cold”. If you want to know you look. And instead of giving it a “hot mode” and “cold mode” you just build it to prevent mutation of the contents.
Likewise its weight or fill level: just look. Having flags is extra work keeping them up to date plus worrying about what to do when they are wrong or in an unexpected combination.
But people write code that way all the time. I do my best to avoid hiring them.
You might want to glance at the article, which is about Turing machine busy beaver programs and has nothing to do with spaghetti code as a development practice.
There are several identifiable categories of common HN commentary: discuss name/title, discuss UI/UX, impromptu ama with the author, everyone dumping their sightly related weird pet theory, unreasonably entertaining anecdotes, 101 q&a on the general domain, field experts with long winded objections over minutae but agreeing with the main thrust of the article, someone learning why HN is not reddit, complaining about downvotes, really good summaries, and maybe even a discussion on the content of the article (what a time to be alive!).
Labeling comments would enable you to filter out stuff you're not interested in reading, but I don't think a separate page is the answer.
Why would you assume this is off-topic? The article is not only about "Turing machine busy beaver programs" but also aims to define "spaghetti code" in a more general sense. In the first paragraph the author says "What’s needed is a formal theory of spaghetti code: an effective procedure that will determine of a given Turing machine program whether (or to what extent) the program is spaghetti".
Note the statement 'given Turing machine program'. It doesn't seem to be scoped to only busy beaver programs. Also note the author's different usages of 'spaghetti code' and the 'Spaghetti Code Conjecture' indicating the discussion of each being different topics. This is further noted by the fact that the author brings the conversation back to the 'Spaghetti Code Conjecture' by writing "With all this in mind, we can restate the Spaghetti Code Conjecture formally: the control flow graph of (sufficiently long) Busy Beaver programs ought to be at least partially irreducible."
With all that said, it seems the original commentator in this thread was discussing the definition of spaghetti code ("In particular, a graph that cannot be reduced at all can be considered utter spaghetti and a graph that can be eliminated completely can be considered thoroughly well-structured.") in relation to the states of a program. Building on the fact spaghetti code can (and has) been observed by the original commentator, they related the discussion of states to the development practice of using multiple flags (which leads to various states in a program)
I often pushed for immutable objects specifically for this reason. At construction time you guarantee the state of the object once, otherwise, as you said, complexity doubles with each mutable property.
I can relate to this. Flags are great, but you need to exercise discipline to not wind up with dangerous spaghetti.
This problem is amplified further because flags can come in so many different shapes and sizes:
- Rollout: binary on/off, percentage rollout, beta group rollout, single user rollout, etc.
- Control plane: eg. emergency shut off valve for the caching layer, switch that controls routing, etc.
- Multi-valued or numeric: ie. not just true/false, typically combined with A/B testing
Conflating these can lead to extremely messy code. Each of these cases deserves its own special type of treatment.
Rollout flags should have a self-destruct or mechanism that strongly encourages engineers to remove them as soon as they're no longer used.
Control plane stuff shouldn't really be mixed with feature flagging at all. It would be dangerous to remove them, and regularly vetting the states should be a necessary requirement for any type of disaster recovery assurance testing.
I really appreciate your call to represent these states as an enumeration. Capturing the flag states at the start of a request and then determining the proper bucket helps to make the problem more concrete. Engineers can then better reason about the explosion of states and the impact to control flow.
Ultimately, I think flagging is one of those "hard problems" that comes with the job territory. You don't want to throw the technique out entirely along with the bath water, because it has plenty of justifiable benefits. There are best practices and proper hygienic steps we can take to make the use of flags easier on ourselves.
I think the OP is referring to the kind of thing you might find in frontend code, like maintaining state for things like isVisible, isExpanded, isSelected.
They might be referring to frontend code (it's kind of unclear), but flags are found everywhere [1] and you need to treat them with diligence at every part of the stack.
You don't want your flag combinations contributing to invalid responses, or worse, persisting invalid states. (A well engineered model will have protections in place, but good flag hygiene helps.)
[1] I've found them in service discovery, monkey patched into 3rd party libraries, load balancer code, etc. Flags get used everywhere.
The need for flags in itself may be a sign of the wrong approach.
You create a shared module.
But for some cases the handling should be different. Let's pass in a flag or configuration. So somewhere in the shared component it will handle things differently. Oh wait, there's another case. Another flag.
Which combinations of flags are meaningful and which aren't? The nearest improvement is to put all the cases in an enumeration. But wait it's not one 'dimension'. Let's have two enumerations. And the extreme end point is that your shared module has a full blown DSL for the parameters it receives. Or even an interpreter.
The other solution that is often more understandable and debuggable in the long run is to not have the shared module call specific code for cases. But to have specific code pull in shared commonalities. No need for flags.
Flags work brilliantly when the states are independent and there is no such thing as an inconsistent state... the moment you have strange interdependence you lose all the benefits.
Theory-curious but mostly workaday programmer here. Does this have any application to detecting spaghetti code in conventional codebases, or is it specific to the Busy Beaver problem?
“Cyclomatic complexity” is as old as the hills, but attempts to count the paths through the code from a begin state to and end state. According to Wikipedia it’s similar to “circuit rank” which is the minimum number of edges to remove in order to break cycles. That smells like it’s related to this graph reduction thing but I lack intuition into this problem.
People are perfectly capable of writing spaghetti code in structured programming languages (without goto) and such programs are always reducible in the sense of this article.
Actually, I have a hunch that every program that is reducible in the standard compiler sense is also reducible in the sense of the article, but the converse does not hold. 1 -> 2 -> 3 -> 4, 1 -> 3 -> 2 is irreducible in the compiler sense (there is a loop with two entries) but reducible in the sense of the article (first merge 2 and 3, then delete self loop, an acyclic graph remains which is trivially reducible).
Low Cyclomatic Complexity is absolutely the result of careful management of interfaces in the system. The inverse is also true that high CC is a sign of poorly structure code, which tends towards spaghetti. Thus I agree it's related to the premise of detecting spaghetti code.
There's nice CC visualizers that can show you at a glance areas that have higher complexity. Sometimes that complexity is related to the problem domain, but sometimes it comes from overhead of poor system architecture.
A related concept to spaghetti code is what I like to call "Jenga code". I.e. code that looks like it started ok, but people kept modifying it without proper support or thought for the modifications, and has now reached a point where the tiniest change will cause the whole thing to collapse.
It’s a fun crack at the problem. I’ll say (very annoyingly) that I have a hunch that the formalization is fundamentally wrong somehow that I can’t quite put my finger on, even though branch factor does seem like a really great candidate for the relevant measure.
One of the first principles I learned in programming was coupling and cohesion. This type of graph-based measurement of program complexity already existed some 60 years ago.
I think your hunch is on point, I think because the author conflates running time of a busy beaver with benchmarks of programmatic coupling and cohesion.
Busy beaver algorithms are single-purpose programs. An eCommerce website is layers upon layers of interrelated modules and functions.
I get where they're going, reducing a problem space down to its simplest representation is ideal. I just don't on first pass understand how busy beaver accomplishes this.
I'd guess it's wrong too. It looks as if it's trying to cluster states, but the part that says "repeat transformations until no changes are possible" suggests it might eliminate too much for the sake of a simple formulation.
In a 2-symbol TM, there are at most two outgoing edges in a state. If you remove the self-reference, you're left with just one. That seems to remove too much of the hidden complexity.
I suppose the thinking behind it is "a loop is not spaghetti code", but a loop in higher level language rarely translates to a single state moving to itself.
OTOH, it's possible to write spaghetti-code in a system that only has while loops and if-then-else by simply having very complex conditions, and modifying the variables on which they depend at seemingly unrelated points in the code. After all, both are TM complete.
This is not the first time I see posts from this blog on this topic here, and I feel like I'm not qualified to read that, since what I've read in these posts is basically all I know about it anyway, and I don't know why should I even be interested in it — I mean, it's pretty useless, isn't it… But almost every time there's something deeply disconcerting in them.
> It’s even been proved that a Collatz counterexample must have certain striking properties, like an enormously long orbit. These proofs are in effect proofs that we will not be able to find a counterexample, even if there is one.
I don't know why, but it hits me hard. Maybe it's not the best example, since mathematically applicable numbers can be quite big too, but it still disturbs me similarly to how when I first realized that "there's a countable number of definable numbers". I'm still wondering if it's even fair to say that all that "other" stuff (which is the most of math, and as pointed out in this post — can include all sorts of interesting properties that nothing we can "reach" has) even exists. Math is basically made up by humans anyway, it's both a product and a property of how we think. So what it even means for Collatz conjecture to be wrong, if there's there's just one particular counterexample out there we could never find?
The article presents an interesting definition for spaghettI. However by using the current known best in each BB class doesn’t really offer a robust analysis in each class. I think it would be pretty interesting to see how much spaghetti it’s possible to make with a 6 states and then examine some of those BB machines vs reducible ones.
Given the very large number of BB(6) programs that exist it’s quite possible that spaghetti ones haven’t really been looked at that much.
Per the author there is only so much spaghetti that’s even possible with less instructions/states.
I have thought about spaghetti code..
spaghetti is..
* Messy.
* Disorganized.
* Every part of it is connected to every other part.
The third one is the actually really bad one. U tug on one thing and it affects another thing and so on.
I call my code rice code, its like spaghetti code but its made of tiny little modules and no part is connected to another.
It also (like spaghetti) has no order / design patterns n stuff. Imo committing to a design pattern is kind of a mini-failure cus your project is now less flexible, you've committed to a certain pattern and everything in future has to follow it. Maybe the next feature does not fit well into this pattern. Ideally you can get to end of project without ever committing to one. (But sometimes u give in and use one to overcome a difficult problem)
IMO Spaghetti code is not having references pointing to and from everywhere.
For me it is having references that do not make sense (why the noodle is there?), are not obvious by reading the code (can't untangle the noodles) and invisible dependencies (where the two noodles must meet in the spaghetti?).
The invisible dependency is the most evil IMO because it tend to be done by "experts of the code" who assume people should know as much as they do about the structure of the code and it tends to produce huge tribal knowledge and policing.
People usually think it is a good idea to use flags. They create a flag to store states of entities and think the problem is solved. Actually it sometimes duplicates the number of possible new problems.
I once tried to convince a friend not to use it. In a part of the code, I asked him: "Look, how can you guarantee that the states of the flags are consistent here?", he then added code just before the line to check for consistency of the flags. I then said that if the flags fell into an inconsistent state, then it should be fixed there, not when they are checked. He tried another fix: created "set methods" for each of the flags, each method called a function "checkFlags" which detected inconsistencies in the flags and automatically set them to a consistent state.
I continued: "Look, every time you add a new flag you will have to check this function and infer, among possible combinations, which are valid and which are not. If you need states, use an enumeration with few states all of which are consistent."
That is the problem with flag-oriented programming: each new flags doubles the number of possible states and it is very complicated to guarantee consistency between them considering all possible combinations and temptation to just add, set and check a new flag when writing code.