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.
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.