The Ultimate Goal of Programming
Tags: programming, beginners, coding, softwareengineering
Published: 2023-12-03 18:56:33

Is to have code that is easy to change.
This is a simple statement, and perhaps it will not even provoke a large number of disputes. Because it is obvious that it is useful to have code that is easy to change. Even if you do not agree that this is the ultimate goal of programming, you probably still agree with the usefulness of the concept as a whole.

Let's break it down. What problem are we trying to solve?
I think this is a fundamental problem for programmers. The thing is, we do not understand, or have false ideas about, why we are programming at all.

For example, when welders weld a structure at a construction site, they know that this structure is needed in order to build a house. They use welding because it is very important for the house to be strong. To prevent the house from collapsing. To prevent people from dying in a collapse. This is vital.

Perhaps this seems too obvious. Perhaps it sounds as if I am describing the fact that water is wet and the sky is blue. But ask yourself: what do programmers think about when writing code? What is the ultimate goal of their activity?
“To make a product” is the answer I received in the vast majority of cases.

The problem is that the very concept of a "product" is constantly changing. Have you ever heard the saying: "In programming, there is only one constant: everything always changes." Just as no business plan survived first contact with the consumer, all projects always change in the course of development. New requirements emerge. Bugs and errors force us to refine and revise.

This is why "making a product" is an extremely wrong way to look at programming. This approach implies that there is a clear plan. That everything is written down and planned so that the plans do not fail or change. That there are requirements that will not change. That there will be no mistakes or bugs.

This vision also does not imply that the concept of a "product" is not only vague and constantly changing, but also constantly expanding. New features and use cases appear for the application, new services emerge. The business will always try to retain current users by expanding functionality and improving the service. Or simply change and adapt to new markets.

I do not want to confuse you with these arguments. The idea here is very simple: there is no such thing as a "finished product" in programming. Our work is never finished. We can support the current product for a long time, we may be reassigned to another or we may simply be fired. For example, because the money ran out. But the situation where the product is built and finished happens extremely rarely. There are probably exceptions to this rule, but I ask you to note that these are precisely exceptions that confirm the rule.

What do I want to say and where am I leading with this?

I’ll draw an example from practice. Because it will help you put this knowledge into practice.

Recently, I was involved in training a new employee. He was given a task:
• the mobile application has a list. The list displays only one item. The remaining list items are hidden.
• If you press "show full list", then the entire list will be displayed instead of just one item. For example, 10 items.
• A change is required. It is necessary for three items to be displayed at the very beginning, rather than just one. Meaning, when you enter the page, you see three items in the list, you press the "show full list" button, and then 10 items are displayed.

The developer approached this issue the same way as many might: he wrote the code so that the list could take an argument. Meaning, you can tell the list how many items should be in the list before the button is pressed. His reasoning was simple: "I want to ensure that I won't have any problems with this list in the future. If I ever need to use this list somewhere else, or use several lists with different initial numbers of elements, I just need to specify the right argument. There will never be any more issues with this element." In this way, he was making it "future proof."

This is absolutely logical. It makes sense. And it is obviously useful.

However, when the developer asked me about my opinion on this solution, it surprisingly made me think. In programming, there are many different principles. There is one that is rarely applied and often even provokes disputes: KISS (Keep It Simple Stupid).

In order to display just one item, we previously had the following line:

>list.sublist(0, 1)

To display any number of items, we planned to use this line:

>list.sublist(0, amountOfInitialItemsToDisplay)

At first glance, the change seems not critical. But upon closer examination, it turned out that the complexity of our code sharply increased. If previously we had only one test that had a very simple description "if the button is not pressed, make sure that only one item is displayed," now we need several tests:
• "if the button is not pressed and if it is indicated to display only one initial item, make sure that only one item is displayed"
• "if the button is not pressed and if it is indicated to display 3 items, then make sure that three items are displayed"
• "if the button is not pressed and if nothing is indicated to display (the argument is missing), then display the default value—three items"
• "if the button is not pressed and if it is indicated to display 0 items, make sure that 0 items are displayed"
• "if the button is not pressed and if a negative number is given, throw an error"
• "if the button is not pressed and if a number is given that exceeds the number of list items, throw an error"

Perhaps the last few tests may seem excessive to you, and these situations are made up. Indeed, I agree with you, this is so. But the key idea is that previously we did not have such potential situations, such problems, and so many tests. And now, due to a small change in logic, our code has become more complex. We have more code and tests that need to be maintained, and it has become more difficult to understand all of this.

On the other hand, we have considerations that we really can change the list in the future (if we change it now, who said we won't change it in the future?), the list may have a different appearance on different pages, and indeed, it doesn't feel future proof.

So what should we choose?

At that moment, I asked myself: what is the ultimate goal of programming? The ultimate goal of programming is to have code that is easy to change. If we look at the problem from this point of view, the answer becomes obvious.

We need to change this:

>list.sublist(0, 1)

To this:

>list.sublist(0, 3)
Then update one test, and the work is done.

This is a way to add the least amount of complexity.

This is a way through which, if we were asked to change this list in any way, we would have the fewest problems.
 
Judge for yourself: if we had a dynamic number of elements, there would be more logic, more checks, more tests, and more code in general.
So when it is necessary to change this list (and this moment will definitely come, because it is an axiom), this greater complexity would hinder us. We would have to take it into account, write new tests and checks. It turns out that complexity leads to even greater complexity, and it can even multiply in geometric progression.

Ultimately, despite the fact that these considerations are very logical, they still seemed to me to be somehow wrong. As if unlawful. Did I spend all my time learning different principles and gaining experience just to change one number to another in the end? What about SOLID? What about DRY? Maybe use some programming pattern? Maybe extract the logic of creating the list? Maybe create two classes instead of one? Maybe through inheritance somehow? Maybe something else?

In my specific example, everything is very simple, and it may seem like these considerations are far-fetched. But if we abstract from the specifics of my example, it becomes clear that this is often how we think. We have our own knowledge, experience, ideas, and concerns. And very soon, if you dig deeper, it becomes clear that we make decisions based on a multitude of factors. Someone writes code so as not to worry about the future. Someone likes to use proven techniques. Someone just wants to close the task as quickly as possible. Someone just doesn't know what they're doing.

And none of these decisions are made from a position of common sense. Because the most sensible thing we can do every day is to write code so that it is easy to change. Because as programmers, we spend the lion's share of our time somehow changing our code.

In conclusion, I would like to say that in my opinion, all the principles and best practices that we apply actually come down to the idea written at the beginning of the article.

DRY == do not duplicate code, write so that changes do not have to be made in many places instead of one.

Architecture == correctly divide the project into layers, so that changes in one layer do not affect others == write code so that it is easy to change.

SOLID == write code so that when adding functionality, we add classes, not change existing ones == write classes so that it is easy to make changes to the project.

Clean code == write code so that it is easy to read and understand what it does. So that it is easy to make changes.

And so on.

Therefore, every time I ask myself questions about how to solve a problem in code, I just ask myself, "What is the ultimate goal of programming?" And then come up with a way through which the code will be easiest to change in the future. I recommend the same to you.