DRY, KISS, but you may gotta need it

这篇文章并非使用中文书写。您可以用Google Translator来辅助阅读。

I would like to talk about YAGNI this time. I have expressed my disagree to this idiom several times. That comes with a reason.

First, I have see lots of time when devs and businessmen were talking about why we need YAGNI, clean archi, to satisfy the clients. Seems to be some great talks about software engineering, but the talks about YAGNI comes and things became interesting.

Let's see how the YAGNI was defined:

YAGNI: Then I would say that the first thing to assure is to remove unnecessary code. Otherwise you can spend a lot of time and energy on unnecessary or soon-to-be-removed product. The job isn’t to write code. The job is to do the job — nothing less, but also nothing more. YAGNI along with making it work guarantee that we have just enough code in our editor.

In summary:

  • Delete all codes that you don't need. The job is not writing code, the job is the job, no more no less.

Some people may find it very arbitrary and want to argue:

My understanding is that the YAGNI principle emphasizes focusing only on the directly useful parts of the current specific needs. For example, in Clean Code, I only use 10% of the content that is immediately useful in my current situation. According to this principle, if a problem can be solved with one class, I will only write one class; if 2-3 classes are needed, simple copy and paste is better than complex abstraction; but when faced with more complex requirements, I will consider Build appropriate abstractions. In short, YAGNI is about choosing the simplest one among all reasonable solutions.

Then another person will find holes in his argument:

So for you, isn't it a violation of the YAGNI principle to develop something that "will be used in the future" (e.g. making a class generic when it only has one use or implementation now)?

My idea is that, I don't agree with YAGNI if it comes with such a definition.

Discussions regarding design patterns and idioms is becoming a strange religion. However, I am not interested in labelling some good architectural designs with a bias, I am no more interested in some design principles that negates the value of abstraction and encapsulation. I believe that the core of SWE is abstraction, including abstraction of the real world and modeling of process. The core of a computer program is not to write what the client asked, and not no more and no less.

Some people may argue that it's not the real YAGNI, it's juste people are using it wrong. But if many people are using it wrong then maybe the concept itself is problematic.

But maybe it doesn't matter if people are using it wrong. Some people may think they really understand YAGNI and the previous talk was nonsense. But can they give an example that's more convincing than find the most simple solution and which won't be refuted by no, we have only one usage?

Then I would like to say why I disagree with YAGNI.

Let's take the previous table driven month-printing function as an example. If you remember, I used an array of strings to save each month, then read numbers from user input and print the corresponding month.

It's not hard to see that even such a basic example can illustrate the value of encapsulation and abstraction: new programmers often code it as a chained if-else statements or a switch control flow, which is here abstracted to a table lookup process.

What's the benefit of this? Easy to maintain and easy to extend.

A step further, this code can be further abstracted, like lifting core logic into a function, using IOC (hmmm, not the Spring IOC yet) to support multiple languages (Chinese, Japanese, French, ...), or use return values to eliminate side effects, etc.

Like what I said in another article, you don't need 100 factories, 200 constructors or 300 singletons to do all this.

That's why I am not against DRY or KISS. Indeed, KISS reminds me of the Less is more philosophy.

However, You ain't is a very strong assertion. Packaging software design with satisfying clients, no more no less is much more confusing. Not to mention that this no more mo less just negates the value of design and refuses any abstraction. It ask programmers to only do what is asked, but, even if you are working in LeetCode exercises, you won't receive a penalty for using generic Java types.

What's more, in SWE projects, often, many important designs are heavily influenced by existing codes. In another word, an early design flavor decides how the project will evolve.

A classic example is about parallelism and concurrency. In general, when we talk about concurrency, from threads to thread pool, to structural concurrency, to task composition, and coroutines, this is a step-by-step process and new abstractions are built on older abstractions.

But what about YAGNI? No, you don't have enough client and you ain't gotta need it for what is above threads. What do we do in a new Web project? Single threads, locks, then we got deadlocks over deadlocks. Nobody can help, even with project Loom.

Another example is monolithic vs microservices. When microservices and containerization are becoming mainstream, monolithic projects are often limited by their early tech debts and it's just impossible for them to have any scalability.

Another example is the upgrade of Java projects. We all know that Java JDKs are backward compatible, unlike C#/.NET. Obsolete APIs are often marked long before they are removed, and Java codes enjoy performance and security improvements from upgrading to LTS versions. But till now there are always lots of old (<7) version codebase, because it's too much pain to upgrade.

Like this codebase that failed to upgrade from 1.6 to 1.7.

But if we investigate in depth, nobody guarantees that HashMap elements are ordered. Some versions of Java achieved it by coincidence doesn't mean that this is an expected behavior. Are we indeed doing Bug-Oriented Programming yet?

Is this a kind of YAGNI?

To sum up, there should be a process of review and critical-thinking in evolution of any project. This called retrospective in Agile. But this process is deleted with the YAGNI spirit.

Yeah, maybe all these are not important, as you ain't gotta need it, and that may mean that the project and programmers could spend less time or money. But in long term, that's how the software rots.

Anyway, keeping an opened mind for future extension, considering possibilities without setting a barrier, leaving rooms for future collaboration, that's always better than a rude YAGNI.

Maybe in some decades, YAGNI had its value and it had directed some good designs. But in a context where people tend to neglect the value of programming design and theoretical computer science, I think it's more than a bad habit to do YAGNI.

It's always worth talking and valuable to leave optimization rooms for future, and to define the need of improvement or refactoring. Maybe sometimes they are not justified, but we should acknowledge the potential risks when we hardcode something. As a software engineer, maybe we should preview and plan for various potentials instead of an arbitrary YAGNI.

If we really want to seek some philosophy, I think composition is something that I am looking for. That's said, with lambda, macro and continuation, programmers can achieve most of features that they need or they don't need.

Those YAGNI advocates will say, you ain't gotta need it。

So, you choose it.


DRY, KISS, but you may gotta need it
http://inori.moe/2023/11/17/dry-kiss-but-you-may-gotta-need-it/
作者
inori
发布于
2023年11月17日
许可协议