Answer Questions and Clarify Doubts How to Promote New Concepts in Practical Work

Answering Questions: How to Implement New Concepts in Practical Work? #

Hello, I’m Zheng Ye.

In the last major section of this column, “Comprehensive Application,” we applied the various principles and knowledge we learned earlier to different scenarios. In the Q&A section of this module, we will also summarize some interesting questions that have appeared throughout the column but haven’t been discussed yet.

Question 1: How to overcome resistance when promoting DDD? #

In his article, Duan Qichao mentioned the significant resistance when trying to promote DDD within a company. He pointed out two main challenges: the lack of understanding of DDD among many people, which requires a certain learning curve, and the high communication cost due to isolation between teams and the difficulty of establishing a common language.

This is a common issue when trying to introduce something new, not limited to just DDD. When you come across something valuable, whether it’s a technology, practice, or idea, and you want to apply it to your own project for better results, you often face resistance within the organization.

In my article “How Can We Stay Competitive?” (link), I mentioned a learning model. By continuously practicing new skills in the learning zone, you can quickly surpass your peers. Most people are comfortable staying in their comfort zone, which limits their progress. Stepping out of the comfort zone can create anxiety and fear of change.

Having the desire to change is within your control, and finding people who are willing to change with you is fortunate. However, expecting everyone to suddenly follow your path is almost impossible, even if you’re a high-level leader trying to achieve complete alignment.

I once worked as an agile consultant for a large company where agile transformation was driven by top leadership. However, it was still challenging. Those who are comfortable in their own comfort zone always find reasons why best practices don’t apply to them.

Did we give up? No, we didn’t. What we did was to start with a pilot team.

In other words, we found a group of people willing to change and applied these best practices to their project. In this situation, everyone had a common goal of improving the project, so they naturally worked together to overcome difficulties. For example, our starting point was continuous integration.

  • Their code was on an outdated ClearCase system, which required competing for file locks before making any changes. This was not conducive to small, frequent commits, so we pushed to migrate to a slightly more advanced Subversion system.

  • The code was written in C language, and given the codebase size, compilation time was long. Therefore, we decided to set up a distributed build system, which required many computers. However, their hardware was strictly controlled, and acquiring additional computers was difficult. Despite the challenges, we eventually succeeded.

  • Previously, the team would only submit code every few days or even weeks. We set a requirement for each team member to submit code at least once a day, and to achieve that, we sat down with the team members to break down tasks and turn big tasks into smaller ones.

When you want to do something, even one reason is enough. However, when you don’t want to do something, there can be a million excuses. Convincing those resistant to change is time-consuming and often yields little result. The best approach is to find people who are willing to change with you and focus on doing something concrete.

We didn’t try to persuade anyone to follow our ideas; instead, we tackled one problem at a time. It took us a long time, but eventually, we established continuous integration, and seeing the green marks on the big screen was truly heartening. Setting up continuous integration, which should have only taken a day or two, took much longer in a complex organization, an experience I had never encountered before.

After we achieved this, other teams saw the results and started to follow suit. The various restrictions that previously existed also started to loosen, such as no longer needing to worry about computer requests. As for those who initially had doubts about our approach, seeing the results shifted their focus to how to achieve success.

Later, I heard that they had established a dedicated continuous integration center within the organization, providing shared build resources for different teams and improving overall efficiency.

As Linus Torvalds once said, “Talk is cheap. Show me the code.” It’s easy to talk about ideas, but it’s difficult to truly convince people. Similarly, it’s challenging to take action, but the tangible results speak for themselves.

In English, there is a saying called “Lead by Example,” which usually describes the leadership style of leading by personal example. When seeking change, regardless of your role, you need to play the role of a leader. So, “Lead by Example” is my advice to you!

Question 2: How to do testing? #

Student andyXH mentioned:

Currently, I’m still trying to understand TDD and I don’t know how to actually use it in project development. In project development, there are often many other calls, such as RPC, database services, and third party services, and I don’t know how to handle them. I’m looking forward to the teacher’s explanation in future articles. - ——"13 | Is writing tests first Test-Driven Development?"

Student 梦倚栏杆 mentioned:

Do I need to write tests for querying database or third party APIs? How should I write tests for this? If tests are not needed, it seems strange that many display-oriented systems don’t require tests. - ——"16 | Why are your tests not good enough?"

Student 闷骚程序员 mentioned:

Let’s assume that the function I want to test is a network sending function related to TCP. I would like to ask the teacher how to implement unit tests for such a function. - ——"39 | How should you deal with legacy systems"

Student TimFruit mentioned:

I have a question. Web services generally rely on databases. How do you perform good unit tests for this part? It is difficult to test the corresponding SQL statements by removing the database. - ——"39 | How should you deal with legacy systems"

Everyone has seen it, these are very typical questions. Generally speaking, when writing tests for business logic, most people know how to do it. But once it involves external systems and databases, many people don’t know what to do.

Let’s answer one question first, what do you want to test in the external system?

Of course, you would say that my entire system relies on the external system and without it, my system cannot run and complete work! But my question is, what do you want to test?

I know that many people’s first reaction when it comes to external systems is, “My code depends on the external system entirely, and because the external system is difficult to test, I can’t test my code.” If you think this way, it means that the dependency on the external system is scattered throughout your business code, which is a severe coupling issue. The external system should only be an interface to you.

I mentioned in 13 | Is writing tests first Test-Driven Development? that to write good tests, you need to think from a testable perspective. Let’s assume I agree with your point that the external system is difficult to test. In that case, what you should do is test as much as you can. Keep the dependency on the external system within a small scope.

A good approach is to design an interface for the business code to depend on, and keep all third-party dependencies within a specific implementation of this interface. I mentioned the SOLID principle in 34 | How does your code get messy?, and this approach represents the Interface Segregation Principle (ISP).

If you can think from the perspective of system integration, this is the integration point between systems. I mentioned DDD in 37 | Do DDD right before talking about microservices, it is just one form of deployment. In the strategic design of DDD, there is a concept called Context Mapping, and the most common pattern for integration between different contexts is the Anti-Corruption Layer (ACL).

Many systems lack an Anti-Corruption Layer when implementing, resulting in severe coupling issues. Any modification to an external service will cause significant changes to one’s code, and in extreme cases, I have seen a gateway system directly relying on JSON objects passed by third-party services in its business logic, leading to significant memory waste and extreme instability.

Therefore, you should design an Anti-Corruption Layer for any external system to isolate it with an interface. This way, you can ensure that your business code is testable. If the external system is indeed difficult to test, this approach will significantly reduce the percentage of untestable code and increase test coverage as much as possible.

The assumption we made earlier was that the external system is difficult to test, but is it really that difficult?

As the author of Moco, a mock server, I certainly do not agree with this statement. If your system relies on the most common REST services, Moco is designed for this scenario. Let me show you the simplest example of using Moco:

@Test
public void should_response_as_expected() throws Exception {
    HttpServer server = httpServer(12306);
    server.response("foo");

    running(server, new Runnable() {
        @Override
        public void run() throws IOException {
            Content content = Request.Get("http://localhost:12306").execute().returnContent();
            assertThat(content.asString(), is("foo"));
        }
    });
}

In this example, you configure the behavior of the external service to return specific content according to your needs. Then, you run your service to access this external service, and the effect is the same as accessing the real service. Moreover, with Moco, you can even simulate effects that a real service cannot provide, such as connection timeout.

The example provided here is written in Java. If you are using another language, you can also use Moco’s Standalone mode to configure a mock server with JSON.

For database testing, if you are using Spring Framework, it provides a complete solution. For example, you can insert some data when running the tests, and after the tests are completed, roll back the data to ensure test repeatability.

In fact, Spring Framework has strong support for testing, not limited to databases. If you are using Spring Boot, the support for testing is even more complete, although it is based on Spring Framework. If you use a real database, it is best to use a separate local database to ensure environment control.

For testing external services, if you can mock, then mock; if you can do it locally, then do it locally. If your service does not have ready-made tools to support testing, it may be a good opportunity to create a new tool.

In summary, for testing external systems, you can first isolate them through interfaces, and then test them through mock servers or locally controllable approaches.

Alright, that’s all for Q&A today. What are your thoughts on these questions? Feel free to share your ideas in the comments.

Thank you for reading. If you find this article helpful, please feel free to share it with your friends.