Words like Spaghetti Code, Lasagna Code, Ravioli Code are considered to be pejorative negative words about source code. I would call them ironic, not negative. They are metaphors for the reality in software production, mostly used by the 70% people that maintain code, mostly declined by the 30% that write it. Unfortunately, criticism has not yet been recognized as a valuable source of quality improvement.
Spaghetti
The worst case of Spaghetti code is the usage of GOTO statements. Generally, code written in old and procedural programming languages like COBOL, FORTRAN, C, JavaScript tends to
have the same clean logical structure as a plate of spaghetti
have a complex and tangled control structure, resulting in a program flow that is conceptually like a bowl of spaghetti
But it's also, and maybe even more, about the developer's style. Object-oriented languages like C++ or Java also allow to
have classes whose methods are overly long and messy, or forsake object oriented concepts like polymorphism
In the Java world, I met Spaghetti code mostly in projects where everything was public (accessible), nothing was final (immutable), and no efforts were taken to reduce dependencies, thus nearly every class/package/module used every other class/package/module. They missed encapsulation in every respect. The chance to draw out a spaghetti and replace it by another was very small, and became even smaller when annotation cheese had been added.
Lasagna
This was, historically, the first concept that eased the Spaghetti burden. Lasagna code is organized in layers, each layer fulfilling its responsibility but abstracting others, leaving them for further layers. The onion principle, very useful for the management of any kind of restricted resources. But it tends to be
code whose layers are so complicated and intertwined that making a change in one layer would necessitate changes in all other layers
The problem is the delegation of one layer to the next. Anything the second layer exposes must be exposed in some way also by the first layer, else it won't be available. Result are classes where most methods just forward to a method of the next layer. And, as layers need to be connected to each other, they all tend to be singletons.
Lasagna is a good concept against procedural Spaghetti, but not against object-oriented Spaghetti. In an object-oriented environment, a layer mostly is represented by a single class.
Ravioli
This is about object-oriented code where any class/package/module uses any other class/package/module. Spaghetti code in a class-costume,
lots of well-structured classes that are easy to understand in isolation, but difficult to understand as a whole
If you apply the old C-programming principle "A function should do just one thing" (without side-effects) to object-oriented programming, this results in "A class should perform just one behavior". Consequently you will have a lot of small classes, and the problem to join them together.
Solution could be to have "Facade" classes that encapsulate and organize other classes. This is a step towards layered architecture (Lasagna), but also a step towards having "God classes", which is an anti-pattern.
Personally I believe the dose makes the poison. There is nothing against a package with 5 classes where just one is public and the other 4 contain package-visible implementation details - we don't want to see these details from outside, we want to see simple things.
Conclusion
I believe that all these code structures have their rationale for existence. There is nothing against
- ... Spaghetti when it is private in a class that has no more than 400 lines of code
- ... Lasagna when it serves for separating software components from each other
- ... Ravioli when some facade classes make it easy to use
I would use Lasagna to encapsulate a component. This can make it easy to move a layer to the neighbor component when it is needed there. I would use Ravioli by default. They have the sugo complexity inside, not outside. Some micro-Spaghetti are allowed inside a Ravioli. Enjoy the meal!