This article was first published on the public account: Songhua preserved egg bulletin board
The author works for JINGdong, and has an in-depth understanding of stability assurance, agile development, advanced JAVA, and microservices architecture
Many articles stress not to over-design your system, but there is no reason why, so this article lists some classic over-design in the hope of inspiring you to do some engineering balance and avoid over-design pushing us to another level of complexity
1, Engineering is more clever than Business
Engineers often think they are the smartest, and this first mistake tends to make them too engineering. We planned 100 things, and the business side would come up with the 101st that we hadn’t considered before. If we solve 100 problems, there could be another 1,000. We think we have everything under control, but we have no idea what will happen in the future
In my r&d career, I’ve never seen businesses converge in terms of requirements, they diverge, and that’s the nature of the business, not the fault of the product manager
2, Reusable Business Functionality
When the business side comes up with more and more requirements, our first reaction is to group and generalize the business as much as possible, which is why most MVC architectures end up stacked with a lot of models or controllers. As mentioned earlier, businesses never converge, they always diverge
In a system, shared logic and abstraction should be stable, but as functionality is iterated more and more, they will either remain flat or become fragile. When the opposite happens, the system becomes too big to fail
For example, if there is a business that implements user attribute management, we take the view that everything is similar and complete the common CRUD logic first, but this requirement actually requires satisfying 13 different registration processes, meaning that the common logic code makes no sense. Similarly, an order view and an order edit view flow are completely different, but some people merge views
Before we split the business horizontally, we should try to split the business vertically, and also consider the operability and ease of switching from one way to another, otherwise rewriting the system would be disastrous, that is to say, splitting the behavior is better than forcing the merger
Everything is Generic
If you want to connect to a database, write a generic generic adapter if you want to query a database, write a generic query if you want to validate parameters, if you want to write a generic parameter validator if you want to wrap results, if you want to write a generic data mapper and so on
When implementing requirements, a lot of time is wasted trying to figure out the perfect level of abstraction, even when the answer to the original business problem is obvious. Even when a perfect abstraction is miraculously concluded, it often quickly becomes inapplicable, and the best code design today is to focus on code that is easy to delete, rather than blindly on code that is easy to extend
In fact, repetitive code is better than false abstraction. It is only better to abstract when you see logically repetitive code in the system, and repetitive code exposes many use cases that help make boundary context clear
4, Shallow Wrappers
We tend to use external libraries with a layer of encapsulation that is shallow, and unfortunately, we tend to blur the lines between providing functionality and a well-written wrapper, confusing a lot of business logic and making it neither a good wrapper nor a good business solution. In fact, encapsulating a good library takes a lot of time to write, with high code quality and good code testing, with clear, testable, measurable apis. It is important to note that encapsulation should be the exception, not the norm, and not encapsulation for encapsulation’s sake
Applying Quality like a Tool
High-quality code usually follows SOLID principles and uses appropriate design patterns and code techniques, such as Factory, Builder, Strategy, Generics, enums. If you apply the concept of quality without thinking about it, such as changing all variable modifiers to private final, it doesn’t improve the quality of the code, or change the old way of chain inheritance, where each class has interfaces and implementations that are then injected into the next layer, seemingly meeting the SOLID concept. In fact, SOLID was originally designed to combat the abuse of inheritance and other OOP concepts, but most engineers didn’t understand where and how these concepts came from, and just accepted them as they were, without digesting them mentally, just using them as tools blindly
Learn another language, try to think differently, and become a better developer. Old wine in a new bottle doesn’t help us, we don’t have to mess with a clear design in order to apply a new concept
6. Other advocates are not aware of the software’s merits
Discovering generic techniques and changing a simple “HelloWorldPrinter” to “HelloWorldPrinter” even if it actually only has a specific data type or enough generic type signatures
The policy pattern was discovered, and now each conditional statement is a policy
Enumerations/extension methods /Traits are all over the place
The above all reflect the problem of excessive adaptation
7, < X > – ity
Example 1, implement a CMS, requires extensibility, business people can easily add fields Results: business people never use this function, when they need, will ask the engineer to help, maybe we need is a simple developer’s guide, can add a new field in a few hours, not click on the type of interface
Example 2: to design a can easily configure the all-encompassing database layer, we can easily switch by file configuration data results: after a long time because of some reason need to switch the data source, but modify the configuration file can meet the requirement, we now have a lot of the function of the gap, cause not compatible
In fact, it is recommended to treat the database as part of the solution and discard configurability, otherwise compatibility is difficult to guarantee. When designing, ask yourself what the usage scenarios are, and then dig a little deeper, and you’ll probably find that most features are unnecessary, including configurability, security, extensibility, maintainability, and inheritability. In short, don’t add features that aren’t required, but clearly define and evaluate scenarios, user stories, requirements, and purposes
Article translation modified from:
medium.com/@rdsubhas/1…
Source: www.liangsonghua.me
Liang Songhua, senior engineer of JINGdong, has an in-depth understanding of stability assurance, agile development, JAVA advanced and micro-service architecture