|
|
Spaghetti code lacks an easily understandable flow to it. It jumps around in less than obvious ways, basically.
To err is human. Fortune favors the monsters.
|
|
|
|
|
This spaghetti code topic sure stirred the pot, but it's a very worthy topic for all programmers, especially the new ones. Thanx.
"A little time, a little trouble, your better day"
Badfinger
|
|
|
|
|
Something I overlooked yesterday is that you have a detailed spec in the form of these "flow diagrams". It's great when it's easy to see how code implements the spec. If that means spaghetti, so be it. Any blame then lies with the spec writer.
|
|
|
|
|
Well, I don't know... if the spec writer is bad at writing specs, does that mean you need to write bad code? I don't think so... because it's your job to write the code and saying that it's the specs fault, it's a weak excuse.
If I see a bad spec, I would discuss this with the writer. Maybe (s)he is junior and does not know/understand the system very well... who knows?!
Eusebiu
|
|
|
|
|
The worst type of spec is one that fails to address all scenarios that are important to a product's behavior. The developer must then ask the spec writer for clarification or decide what to do in such cases. If the flow diagrams cover all possibilities, they pass this key test. But yes, they should be simplified if possible.
Reviews really help to avoid bad specs. Just as there are code reviews, there should also be spec reviews.
|
|
|
|
|
Correct! So, we agree - it's your job to write good code (as well as you can, ofc) even if the specs are bad!
Eusebiu
|
|
|
|
|
Eusebiu Marcu wrote: and saying that it's the specs fault
Unless of course you are 'writing to spec' then it could be contract violation to do it some other way.
But other than that the problem then is not the code but the design/requirements. And it should be addressed at that level.
Should be noted that the OP states "so many conditions around state changes"
I have seen attempts to make state diagrams into OO and it always looks like idealization run amuck.
|
|
|
|
|
That was actually my precise calculus.
To err is human. Fortune favors the monsters.
|
|
|
|
|
may i please inquire the number of boxes in the flow chart . also the number of lines of code in the final product and the language . if the customer insists on spaghetti one must ask the precise type they prefer . for me it is fettuccini -Best
|
|
|
|
|
It's a screen flow diagram to be specific. There are 6 boxes on the main page loosely corresponding to the screens.
There are about 11 lines connecting them in various directions.
One trick with it, is it wakes and polls a server periodically, and goes to sleep until it's woken up by a user. That adds a bit of complexity.
Another issue is that there are inactivity timeouts on each screen that drop you back to either the home screen or the sleep screen depending on where you are.
Also there are 3 buttons and they are contextual, changing function depending on the screen you're on.
Hopefully I've given you enough of the landscape that you can understand the issue.
To err is human. Fortune favors the monsters.
|
|
|
|
|
it seems i do not understand as it seems i do understand . a state diagram comes to mind rather than a flow diagram though perhaps they are the same as i do not know for certain . the few times i utilized a state diagram i found the logic easy to understand .
|
|
|
|
|
It is pretty much the same, just presented slightly differently. The screens are basically state transition "landings", the arrows, the transitions themselves.
I wish I could just show you the diagram but I'm not at liberty to share it. It would make things clear.
The actual logical flow isn't terrible, per se. But it folds back on itself some (due to inactivity of button presses, or otherwise navigating from screen to screen)
There is a quirk that impacts all of the startup code, and that is the startup code must run on wake or on boot, with a different "start reason" associated with it. The machine otherwise has no indication to determine whether it was woken from sleep vs. powered on. It remembers nothing.
Also, I have a watchdog timer in the code, for robustness - this is out of band, and what it does is it starts counting down, and if it reaches the end, it reboots. This is regardless of what else happens. The reason for that is to prevent a hang, not that my code hangs, but in production, who knows?
So that's a separate flow on top of everything else.
Basically what it amounts to is rather than one state machine, it's several state machines working in tandem. That's messier than what I'd like in an ideal world, but making it cleaner just wasn't going to pay for itself.
To err is human. Fortune favors the monsters.
|
|
|
|
|
Sounds very familiar territory.
|
|
|
|
|
No reason to duck or put the fireproof suit on Honey.
In many of our 'real' worlds, time and budget are real concerns, and we don't have the flexibility to create an RFP that rescopes and projects a timeline. It's nice to work in that world, but it's not what the client always wants.
Had a case last week where a weird set of changes from an outside vendor (don't get me started on the level of their incompetence with 500+ employees) should have required adding a column to the client's DB and creating the framework and structure to handle the change. Into the thousands of dollars and major timeline. We have two weeks and nowhere near the budget. My colleague was panicking over redesigning everything correctly. I messaged him the meme of the dog with spaghetti on his head and said "time for a nice bowl of pasta". Not pretty at all, but done and in production, working fine. I do know why bird's "nests" look like spaghetti, lol.
Your bottom line is 100% correct!
|
|
|
|
|
"assuming not a lot of reuse" is the fulcrum of this kind of decision. Efficiency is using the lowest effort that will get the desired result, so for low reuse, spaghetti is fine. Idk if I could force myself to do it, but then I've been known to use pen and paper, so there's that.
|
|
|
|
|
Spagetti code (in my day) usually only meant the use of GOTO (which has been declared a cardinal sin since the 1970's).
Heck I didn't know C had a goto until after 10 years of using it (c. 2003)
But if you're coding in assembler, I think there's no way you can avoid it (I mean some fools use interrupts all the time, even some compilers, but that wastes a ton of clock cycles).
BASIC, on the other hand, I have not touched since 1997 (Thank god too, I never could find a free Basic compiler then, Qbasic is crap)
|
|
|
|
|
This is C++, and the reason for the spaghetti was maintainability wasn't as a big a concern.
Also, despite being spaghetti, it actually follows the flow diagram I was given pretty directly, albeit with some necessary quirks. In that regard, it's actually got fair documentation for what it is.
But I'm not talking about gotos, but rather, for example, branching all over one C++ source file in response to various incoming events or state changes.
It's something of a Rube Goldberg contraption, in that one thing causes another thing, causes another thing in some scenarios in the code, and that can get confusing.
To err is human. Fortune favors the monsters.
|
|
|
|
|
I just looked at some code I wrote in 2009-ish because we had to enhance it. I don't think it technically is spaghetti code, but it twists and turns on itself enough to act like it. Here's the basic environment:
* SQL & a binary file store values used in the calculations
* COBOL does the calculations
* Delphi is the middleman
* C# is the UI
But wait, the COBOL can't access the SQL to get those values, so the C# gets them and does all the same calculations as the COBOL, then passes just a few values to the Delphi.
The Delphi combines everything via a client data set, then passes it back up to the C# one value at a time. Sometimes it passes values to the COBOL as well.
COM is used 3 different ways during this process.
Oh, and the same value frequently has different names in the 3 program languages, and some of the names are the same for different values. For example, SomeAmount in C# => SomeOtherAmount in Delphi, but SomeAmount in Delphi => ThisAmount in C#.
It's a head-turner and headache-creator for sure.
Bond
Keep all things as simple as possible, but no simpler. -said someone, somewhere
|
|
|
|
|
Yikes! I hate doing interop between different platforms and languages.
I end up having to a lot because of the embedded work I do, and it's no fun.
I've gotten clever about it and added some tools to my toolbelt for covering common scenarios, but even accounting for byte ordering, calling convention differences, and language variations, there's also the transport layer itself and some interop - like over bluetooth comms - is like pulling teeth.
To err is human. Fortune favors the monsters.
|
|
|
|
|
Well as long as it's clean spaghetti code with comments, then I'm OK with that.
But on the flip side, I just spent a couple of years translating/re imagining code and HTML that was beyond spaghetti code in PHP 4.2 to PHP 8, where you have SQL statements buried in HTML, with one comment, "Grace fixed the login" on many of the web pages. 3 different programmers that went to UC Irvine, with 3 different styles of programming. And what made it so bad was simply nomenclature and haystacks, where the 3 different programmers didn't name the variables consistently, with one variable that still eludes me, "session_origin". These 3 kids went back to China in 2008 after graduating, and left the customer with a lump of coal, but it worked for 20 years.
I choose the long process of making it clean, highly organized with reusable code, and everything commented, so the next person can work on it. But I had to tell the customer many times that what they want is too expensive, and that it would blow their budget so fast, stick to the meat and potatoes first and lets get it up and running.
If it ain't broke don't fix it
Discover my world at jkirkerx.com
|
|
|
|
|
And that makes sense, doing what you did. For starters, the code was in use for 20 years.
One thing I didn't mention in the OP and perhaps I should have, is this isn't the final development phase.
We deliver in stages. We're on stage 2, right now, with a 3rd one planned at the very least.
At worst this means coming back to this particular code and starting over, but experience tells me, when I made the 1st iteration a bit cleaner, I had to rewrite it anyway because the spec changed so much.
So it's a risky maneuver, investing in cleaning this up. Worst, like I said, it means "going dark" for a bit while I develop a framework that may or may not even be used on the 3rd round.
To err is human. Fortune favors the monsters.
|
|
|
|
|
Thanks for backing me up on that ... These decisions we make can cost a lot of time, going into years, so we have to be spot on.
Great Post!
If it ain't broke don't fix it
Discover my world at jkirkerx.com
|
|
|
|
|
Well, that's the thing! His decision is a bad one (statistically) and hopes that the next phase will pay for the this one because he always thought there will be no other or the changes will be so big that the client will agree with a new code. Do you still think he was spot on?
Eusebiu
|
|
|
|
|
My decision would be a bad one if I was writing business software on a team, for an enterprise.
Fortunately, I found work that pays better and isn't mind numbing.
But the priorities are different.
If I was making bad decisions I wouldn't be nearly as successful as I am.
I think you have an unrealistic view of your own position and how "right" you are.
To err is human. Fortune favors the monsters.
|
|
|
|
|