Question

Many questions have been asked about the nature of elegant code and design, but the underlying assumption seems to be that elegance is a good thing. Therefore it is acceptable or even desirable to make some extra effort to create elegant solutions.

But is it really worth it?

I am not asking what is elegant; neither am I asking about good code. I assume that "you know it when you see it", and it can also be vaguely characterized by verifying that there is nothing left to take away, in a sense that the mental model you have in your mind is expressed directly and unobscrured in the language of your choice (programming language, object oriented design, relational schema etc.), without distractions.

However, a corollary to it is that elegance is always relative (and in a way subjective); it depends on what you think the problem is and what the solution should look like. The problem of course is that both the problem domain and your understanding of it constantly change (additionally, your knowledge about the programming language / patterns / tools develops, but that's not what I am mainly concerned about).

This implies that in order to keep your design elegant, you have to constantly rethink it as the problem / your undestanding of it evolves. Practically, doing so would result in almost endless refactoring.

Furthermore, I even maintain that by striving to develop elegant solutions maintainability necessarily suffers. The most elegant solutions are the most tightly coupled to the problem domain, for if they weren't, then there would be something to take away from it, hence they would not be most elegant anymore. If there is something abundant in your solution, then you can simplify it and make it more elegant by removing that abundant part, right?

But then, sometimes, when you have a really elegant design and are required to implement a change, you first have to "untighten" your solution, just in order to be able to make an unforeseen (as are most of them) change at all. However, the open/closed-principle states that this should not be necessary. So, another more concrete formulation of the question is:

To some degree, aren't elegance and open/closed-principle at odds with each other?

What do you think?

Was it helpful?

Solution

Isn't striving for elegance counter-productive?

Past a certain point, yes.

But is it really worth it? I am not asking what is elegant; neither am I asking about good code. I assume that "you know it when you see it"

Well ... you probably should [ask "what is elegant"]. "You know it when you see it" implies that you don't know it, until you see it as well -- and then, your effort to write elegant code is effort spent with lacking direction (or, effort spent in the wrong direction).

This implies that in order to keep your design elegant, you have to constantly rethink it as the problem / your undestanding of it evolves. Practically, doing so would result in almost endless refactoring.

No, it doesn't imply that you should constantly rething the problem. You should think the problem well at the beginning. The more thought you put in at the beginning, the less you will have to change afterwards. If you do not have a clear thought of where you are/should be (at the beginning) then you will need to change the code. To avoid endless refactoring, you should (first) decide how the solution should look like, how an elegant solution should look like, then implement that.

You will know you are doing it right, when you end up spending up to 10 times* on interface design (and tests) then you spend on implementation.

Furthermore, I even maintain that by striving to develop elegant solutions maintainability necessarily suffers.

If a solution is not maintainable, how can you call it elegant? An elegant solution should be (by my definition) a solution that fits the problem, design, maintainability and performance requirements well. Otherwise, it may not be a solution to your problem at all (let alone an elegant one). This also makes my point above (about needing to ask "what is elegant" before you go searching for elegant solutions).

The most elegant solutions are the most tightly coupled to the problem domain, for if they weren't, then there would be something to take away from it, hence they would not be most elegant anymore.

In my book, those are not elegant solutions, they are inflexible solutions (and by definition, as opposed to elegant as you can get).

If there is something abundant in your solution, then you can simplify it and make it more elegant by removing that abundant part, right?

Uhh ... no. Doing so would depend on what you would gain by such a change, and if the gain is worth it at all.

you first have to "untighten" your solution

AFAIK, this is called implementing a loosely coupled design, and is considered a cornerstone of writing elegant code. It is not something that goes against elegant design, it is a part of it.

To some degree, aren't elegance and open/closed-principle at odds with each other?

No. An elegant solution would be one that took the open/closed principle into account from the start.


  • this idea was taken from Alexander Stepanov, the designer of the C++ STL (and it's an idea I can confirm through experience).

OTHER TIPS

I see no justice in this statement:

... by striving to develop elegant solutions maintainability necessarily suffers. The most elegant solutions are the most tightly coupled to the problem domain, for if they weren't, then there would be something to take away from it, hence they would not be most elegant anymore.

An example of an elegant solution that is not tightly coupled to the domain is generic sort algorithms. You have six items in a list (vector/array/whatever) that need to be sorted; do you hand craft the 12 lines of code that take advantage of the inherent partial ordering of the list, or do you invoke a sort function in one line?

Similarly, do you explicitly craft an algorithm and re-implement it several places or do you extract a method and make it generic?

In both of those cases, the elegant solution is much shorter and more understandable. The given solutions are also more maintainable.

This is a straw man argument:

in order to keep your design elegant, you have to constantly rethink it as the problem / your undestanding of it evolves. Practically, doing so would result in almost endless refactoring.

Striving for elegance is always balanced against all of the other needs of the project.

However, the open/closed-principle states that this should not be necessary. This isn't a useful interpretation of the open/closed principle.

There's always a requirement your design can't accommodate. Think of it this way - a program can be thought of as a domain-specific library whose components are combined in a particular way by the main function to solve some problem. If that library is flexible enough to do anything without changes to its source code, it's turing complete and you've created a programming language on top of your programming language and are no better off than when you started. For the library to be useful, it must be tailored to a particular, well-defined task, and when the scope of that task changes, so must the library.

(The alternative would be to write every possible program, so that when someone comes to you with a new problem, you've already got a program for it. The problems with this approach are obvious.)

Furthermore, the Open/Closed Principle was originally coined in 1988, when our understanding of OOP was still young. Bertrand Meyer probably had extension through inheritance in mind when he coined it, but it turns out you can't override arbitrary methods from a class without your subclasses breaking further down the line when the parent class is updated. In other words, the base class must be designed up front with inheritance in mind and you can only safely override whatever the base class says you may override. The promise of being able to handle unforeseen tasks through ad-hoc inheritance was false. This is known as the fragile base class problem.

I'd argue that a more useful interpretation of the open/closed principle is that when the scope of your requirements expand to include a new task, your code shouldn't be hard-coded to that particular instance of the task. E.g. If you now need to sort a list of dates, it's very likely you'll need to sort other kinds of things as well. Rather than writing a function that only sorts lists of dates, you might as well write a function that can sort lists of anything that can be compared.

Once you acknowledge that it's insane to anticipate every possible change, "elegance" is simply finding the most clear, simple, or "beautiful" way of expressing the solution to your particular problem.

it can also be vaguely characterized by verifying that there is nothing left to take away, in a sense that the mental model you have in your mind is expressed directly and unobscrured in the language of your choice (programming language, object oriented design, relational schema etc.), without distractions.

I'm not sure I agree with this. I know of many barebones, or concise solutions for a problem that are directly and clearly implemented without being elegant. I mean, if I brute force your password, the code will be a clear description of what I'm trying to accomplish - but the solution itself isn't elegant in the least.

To me, an elegant solution is one where things "fall away". Consider the composite pattern. Many times, judicious use of this pattern yields this sort of effect. Without the pattern, the "is this a single thing, or is it many things" problem has to be propagated everywhere that uses the interface. By using the composite pattern, you hide away that complexity so that consumers need not care.

Elegant solutions are ones that don't just solve a problem, but prevent problems from ever happening. So no, striving for elegance isn't counter productive.

When you take time to study elegance outside the constructs of code, you gain an appreciation for what truly is elegant. According to the great art masters (like Da Vinci), and some of the greatest mathematical minds (like Einstein), a very common attribute of elegance is simplicity.

What Elegance Is Not

In reading your question, it seems to me that you are equating elegance with fancy solutions that use complex black boxes that hide implementation details from you. That is the furthest thing from elegance, for the following reasons:

  • Hidden details and swallowed exceptions can hide what the real problem is.
  • Reflection as a major feature of a framework/etc. makes it very difficult to trace what is going on in an application.
  • It muddies understanding of the code, and of the problem domain.
  • It requires you to learn a new language and thought process in addition to the language you are using.

These are attributes of the cumbersome problems that probably started as a good idea, and then carried too far. These issues all complicate maintaining code, have difficult to grasp concepts, and in general do not improve the bottom line. Even if some people have a simple solution and tout solutions like this as "elegant", if it doesn't remain elegant in a more complex solution then it truly is not elegant.

Defining Elegance

Elegant code simplifies the solution in a way that is comprehensible by all members of the team. If you are working with an object oriented language, an elegant solution does not force you to change the way you think in terms of a functional language. The reverse also applies. The following are attributes of elegant solutions:

  • Debugging and following code flow is very easy
  • It greatly reduces clutter code that hides the intent of a method
  • The concepts and method of thinking fit well within the language used, and truly elegant also makes the problem domain easier to comprehend in the code.
  • It improves the efficiency of the developers working with the code

Can we learn newer ways of approaching a problem that are more elegant than the ones we are using? Absolutely. Is it worth retrofitting existing code with the newer, simpler, and more elegant solution? Not always. That's a project management decision that requires you to balance the cost of the change against what gain that solution will provide.

Should We Pursue Elegance?

The pursuit of elegance is synonymous with simplifying code and making it more understandable. Anything you can do to lighten the already heavy cognitive load a developer has when maintaining code, the better. Sometimes all you can afford to do are small improvements (light refactoring) here and there as you go in and work on code. Sometimes there is so much code rot due to changing requirements that you really need to rewrite a section of the code to satisfy the current needs better and enable new features. It doesn't change the fact that pursuing elegance is synonymous with making your life easier and improving the maintainability of the code.

Licensed under: CC-BY-SA with attribution
scroll top