This article is the notes of the second chapter of “The Way of Programmer Training”. It summarizes some principles and common development patterns that effective programmers need to follow. It has very important guiding significance for us. It is recommended that every programmer should learn and master these principles. If you find this series valuable, we can organize a book draw to encourage you to learn from the original text.

DRY principle

The software development process is always accompanied by maintenance, if there is a lot of duplicate code in the project will cause great trouble to our maintenance work. For example: a piece of code appears in multiple places, once we need to modify multiple places at the same time, if you forget to modify a place may lead to some unnecessary errors. Hence the DRY principle – Don’t repeat yourself.

We often have a lot of repetition, but there are also solutions to avoid repetition. Here are some examples.

  • Code vs. Comments: We’re often told to add comments to our code. However, more comments are not always better; we should save comments for high-level instructions and replace them with code for lower-level instructions. Otherwise, you have repetitive knowledge, and you have to think about modifying comments every time you change code
  • Code vs. Documentation: Documentation always lags behind code, often updating code but forgetting to update documentation. We can use some auxiliary technology or self-developed technology, such as: API Doc, Java Doc and other tools, the code updates, the document will be updated
  • Repetition in design: If a class has a start coordinate and an end coordinate, then adding a distance between two points is a bit repetitive because we can calculate the distance from the start and end points
  • AD hoc duplication: Sometimes we need to do some AD hoc requirements and use a piece of code in the project. We usually just copy it and use it. A more radical approach is that we can abstract some of the more common computing logic into interfaces and form our own toolkit, which can be directly referenced when needed without copying code. For example, some common summations and maximization require that the processing logic of the for loop can be abstracted from a Reduce function
  • Duplication between developers: This is a common type of duplication, and it is not easy to avoid if two groups of developers are working on a project at the same time, which can easily result in the same small logic being implemented by members of both groups. But we can increase communication between teams, we can organize a platform to open up some of the better components. When I do data development, I often use some UDFS to process data. If I don’t have any in my current project, I will search the relevant keywords on the company’s public platform, and usually I can find ready-made UDFs. I prefer to open source the different project code internally, as long as confidential code is not involved. In the development process, I often ask upstream students how to deal with the data and what logic has gone through. On the one hand, this will increase the cost of communication, and on the other hand, it is not true. So I often decomcompile upstream code, need to read the logic in the future without asking others, directly read the code to save a lot of time. The nice thing about open source is that we can learn from other people’s code.

Orthogonal principle

Orthogonality is not interdependent or decoupled. Two or more things are orthogonal if one of them changes without affecting the others. For programming, it is often referred to as high cohesion, low coupling. Orthogonal system has many benefits. It can reduce risks. When a component in the system is modified, as long as the external interface remains unchanged, the component can be modified arbitrarily without affecting the whole system. Orthogonal systems increase productivity because the system components are decoupled, so they can be developed independently and in parallel. For example, in recent years, the front and back end separation technology is a good representative. In the past, students at the back end often need to write some front-end code of the page, and the code at the front and back end of the students are intertwined and interdependent. But after the separation of the front and back end, only need to determine the protocol, the front and back end technology can be decoupled, developers can develop in parallel, improve productivity. Here are several ways to maintain orthogonality

  • Design: When designing systems, we can adopt a layered architecture based on components. Each layer provides one level of abstraction, each layer only uses the abstraction provided by the layers below, the protocols/interfaces between layers are stable and extensible, and changes to the components below are transparent to the upper layers
  • Coding: To keep the code deconstructed, we can develop “shy” code that controls access to logic that does not need to be exposed and is not referenced by other components. Avoid using global data, which involves updating by multiple users at the same time, resulting in high component coupling. Avoid writing similar functions, which often have duplicate code and require multiple updates at the same time
  • Testing: during unit testing, if a certain unit involves a large part of the rest of the system, it indicates that the coupling degree of the unit and other parts of the system is large, which needs to be paid attention to by the developer

Revocability principle

The principle of revocability means that when a part of the system needs to be changed, we can undo the existing code and flexibly adapt to the new change. Because requirements change all the time, revocability always exists. For example, if we are developing a database visualization software, the initial requirements are based on Mysql. If we do not do hierarchical design, we will write JDBC code where we need to add, delete, change and check. However, how to support MongoDB for our underlying data someday, because our JDBC code is distributed in the code of each module of the project, almost impossible to undo, then the system will have to rewrite. If we had abstracted the data access layer as a component at the beginning of the design, then the upper layer would only need to invoke the abstracted interface to manipulate the database. The advantage of this is that when we need to support another database, we only need to write database access code for that database that supports our abstract interface, so the system is revocable. In fact, we often use ORM framework in Web projects is also based on the principle of revocability, ORM can map database tables into objects, add, delete, change and check at the bottom layer transparent to the top layer, so you can flexibly adjust the bottom database.

Tracer bullets and prototypes

Tracer bullets are often used to strike military targets in the dark, leaving a firework trail between the gun and the point of impact to indicate the trajectory and target, thus helping the shooter correct the trajectory. In fact, goals in the dark are like the unknown systems we face in development. Faced with an unknown system, if we make a lot of documentation, list every requirement, and try to identify all the unknowns, it’s like doing a lot of calculations in advance on an unknown target in the dark and then shooting it, which obviously takes a lot of computing power and doesn’t necessarily hit the target. Pragmatic programmers tend to prefer tracer bullets. The core advantage of tracer bullets is that feedback is immediate. For example, we want to develop an RPC framework that supports various serialization formats. At the beginning, can we use the simple default serialization method of the system to set up the whole system framework and make the system run? After running, we can get feedback from users in a timely manner, fix existing problems, add multiple serialization frameworks, and iterate.

After tracer bullets, let’s talk about prototyping. I remember learning software Engineering in college when I got into prototyping. A prototype is more like a Demo, not a code implementation, but a quick way to create something that meets the requirements of the product. For example, if we need to determine the UI requirements of a certain system, we can use the fastest development language to develop a Demo. Its code does not need to be standardized, and the interactive interface does not need to be too beautiful, because the prototype will probably not be used in the subsequent implementation of the actual project.

Here’s a quick summary of the differences between the two development models. Tracer bullets emphasize whether we can develop a small but all-in-one system to get feedback quickly and guide us to further iterations. In tracer development mode, subsequent code is dependent on the first version of the code. Prototype development focuses more on the stage of clarifying requirements. A quick Demo is developed to determine the requirements of the system based on the prototype. This stage does not care about code, integrity, robustness, or correctness of the system, because prototype code is rarely used in real system development.

Domain language and estimation

This section of content I think usually not much application, understanding is not profound, so a simple summary. My understanding of domain language is to use (create) a specification of pseudocode, why pseudocode? Assuming that we communicate with the product, we can use words, but we all know that Chinese characters are profound and profound, and the meaning expressed by others may be inconsistent with our understanding. Otherwise, we do not need to work so helplessly and painfully. Of course, it is not feasible to communicate directly with the programming language. You need an intermediate layer of pseudo-code that clearly expresses the requirements and can be understood by the product or programmers using different programming languages.

For the estimation section, the author introduces some guidance on some common estimation problems (estimation of project time and flow), but it feels more theoretical and not strong in practice. But the one that impressed me the most was:

Estimates given at the coffee machine will come back to haunt you like coffee.

summary

This chapter focuses on three principles that, if summarized and applied in development, will help us develop effectively. The DRY principle avoids repetitive work, the orthogonal principle avoids the maintenance of the entire system when changing a component, and the revocation principle avoids the system crash when changing a component. Finally, I introduced two rapid development modes of tracer bullet and prototype development, especially tracer bullet. I prefer to use tracer bullet. First, build a skeleton and run at a small cost, and then continue to enrich the flesh and blood.

Welcome to follow the public account “du code”, I will share more excellent books content, organize regular book drawing activities