Source: Tencent Technology Engineering , author: cheaterlin, Tencent PCG background development engineer
Foreword
As a director of the Golang chapter of the company’s code committee, I have reviewed a lot of code and read a lot of other people’s review comments. I found that many students’ code review and the level of writing good code need to be improved. Here, I would like to share some of my ideas and ideas.
why technologists, including leaders, do code reviews
The proverb is: ‘Talk Is Cheap, Show Me The Code’. It is difficult to know what is easy to do, and it is difficult to combine knowledge and action. It’s always easy to say it in your mouth, it’s easy to memorize what others have said, organize the language, and speak it again. Never know this matter to bow down. You may have heard some hearsay and thought you had mastered it, but would you do it? Do you have the ability to think about and improve your current practice and the details of code in practice? To put it bluntly, many people simply know and agree with a certain design concept, and then have a false sense of reassurance— their technology is not bad. However, he did not practice these design concepts at all, or even practiced these design concepts at all, in terms of results, whether he understood these principles / concepts, what is the difference? It became self-deception.
Code is where the design concept is landed, and it is the presentation and root of technology. Students can achieve landing communication in the review process, no longer air-to-air discussion, can produce collisions of thinking in practical problems, learn from each other, and everyone masters the best practice accumulated in the team! Of course, if the leader does not have time to write code, just review the code, point out that other students have some practices are not good, to give good practice advice, even if you do not write the code yourself, it is necessary to think a lot about best practices.
Why do students think and summarize best practices in the reviewI
will first give my own summary: the so-called architect is to master a large number of design concepts and principles, practical methods implemented in various languages and attached tool chains (ecology), vertical industry model understanding, customized system model design and engineering practice specification rules. In turn, control the development convenience, maintainability, testability, and operational quality of 300,000+ lines of code projects.
Powerful technical people can be mainly divided into the following directions:
master a lot of skills, and discover a series of ideas, such as many programming competitions, compared to this. However, this does not seem to be very useful for engineering.
John Carmack, for example, created the methodology for efficient rendering of modern computer graphics. Whether or not someone would have invented it without him, he was the first to invent. In 1999, Carmack was ranked 10th on Time magazine’s list of the 50 most influential people in technology. However, there are few similar hall-level positions, not enough for everyone to share, and it is none of our business.
In the eighties, Dr. Kai-Fu Lee insisted on adopting the framework of the implicit Markov model and successfully developed the world’s first large-vocabulary continuous speech recognition system, Sphinx. My generation of engineers, it seems that very few people are good at this.
Brother Xiaolongg is the benchmark.
This is something that anyone can do, according to the definition of an architect above. If you go well on this road, you can build a technical team for any company and organize the construction of high-quality systems.
From the above discussion, it can be seen that the evolution path of our ordinary engineers is to constantly polish the best practice methodology and implement the details.
root cause of
bad code Before we discuss what code is
good code, let’s discuss what is bad. Computers are man-made disciplines, and we create a lot of problems ourselves, and then think about solutions.
Repeated code
BatchGetQQTinyWithAdmin to get the tinyID of QQ uin, requiring the tiny of the main uin and the login state
friendUins can be an empty list, As long as the tiny
func of admin uin BatchGetQQTinyWithAdmin(ctx context. Context, adminUin uint64, friendUin []uint64) (
adminTiny uint64, sig []byte, frdTiny map[ uint64] uint64, err error) {
var friendAccountList []*basedef. AccountInfo
for _, v := range friendUin {
friendAccountList = append (friendAccountList, &basedef. AccountInfo{ AccountType: proto. String(def. StrQQU), Userid: proto. String(fmt. Sprint(v)), }) } req := &cmd0xb91. ReqBody{ Appid: proto. Uint32(model. DocAppID), CheckMethod: proto. String(CheckQQ), AdminAccount: &basedef. AccountInfo{ AccountType: proto. String(def. StrQQU), Userid: proto. String(fmt. Sprint(adminUin)), }, FriendAccountList: friendAccountList, }
Because the protocol was poorly designed at the beginning, the first person to use the interface, without code like the above function, implemented a code embedded in the logic code to fill in the request structure structure, which was good at first. But when a second person or a third person does something similar, we will no longer be able to refactor the protocol, and we will have to be troublesome forward compatible. And every student must understand how to fill in the above protocol, understand that if there is a problem, it will trigger a bug. Or, if a misunderstanding is widespread, we have to find all these duplicate fragments and correct them all.
When you want to read a piece of data and find that there are two places, you don’t know which one to choose. When you want to implement a function and find that two RPC interfaces and two functions can do it, you don’t know which one to choose. Have you ever faced such a ‘life problem’? In fact, it doesn’t matter how you choose, the code you wrote has taken a solid step on the road to shit.
However, A little copying is better than a little dependency. Let’s mention it here, not expand it.
Here, I must say an additional word. Everyone uses TRPC. I feel like I’m encouraged to ‘do one git per service’. So, the code that accesses db, the code of RPC, and all kinds of reusable code in your service are the code under git that everyone reuses? Write it again every time, the db field details are changed, and the git corresponding to each server that has used db is changed? This generic git already written interface should not know which git code has forever abandoned forward incompatible changes because of its own non-forward compatible modifications?
Early effective decisions are no longer effective
Many times, we wrote the first version of the code, and there is no big problem. For example, the following code
Update incremental update
func (s *FilePrivilegeStore) Update(key def. PrivilegeKey,
clear, isMerge bool, subtract []*access. AccessInfo, increment []*access. AccessInfo,
policy *uint32, adv *access. AdvPolicy, shareKey string, importQQGroupID uint64) error { Get
the previous data info, err := s.Get(key) if err != nil {
return err } incOnlyModify := update(info, &key, clear, subtract, increment, policy, adv, shareKey, importQQGroupID) stat := statAndUpdateAccessInfo(info) if !incOnlyModify {
if stat.groupNumber > model. FilePrivilegeGroupMax {
return errors. Errorf(errors. PrivilegeGroupLimit, group num %d larger than limit %d, stat.groupNumber, model. FilePrivilegeGroupMax) } } if !isMerge {
if key. DomainID == uint64(access. SPECIAL_FOLDER_DOMAIN_ID) &&
len(info. AccessInfos) > model. FilePrivilegeMaxFolderNum {
return errors. Errorf(errors. PrivilegeFolderLimit, folder owner num %d larger than limit %d, len(info. AccessInfos), model. FilePrivilegeMaxFolderNum)
} if len(info. AccessInfos) > model. FilePrivilegeMaxNum {
return errors. Errorf(errors. PrivilegeUserLimit, file owner num %d larger than limit %d, len(info. AccessInfos), model. FilePrivilegeMaxNum)
} } pbDataSt := infoToData(info, &key) var updateBuf []byte
if updateBuf, err = proto. Marshal(pbDataSt); err != nil {
return errors. Wrapf(err, errors. MarshalPBError, FilePrivilegeStore.Update Marshal data error, key[%v], key) } if err = s.setCKV(generateKey(&key), updateBuf); err != nil {
return errors. Wrapf(err, errors. Code(err), FilePrivilegeStore.Update setCKV error, key[%v], key) } return nil
}
Looking at it now, this code is quite good, the length does not exceed 80 lines, and the logical price comparison is clear. But when isMerge judges the logic here, if you add more logic and support the number of local lines to more than 50 lines, this function will taste bad. Two problems arise:
1) The code inside the function is not at a logical level, reading the code,
originally reading the top-level logic, suddenly fell into the logic processing details of isMerge of up to 50 lines, and before reading it, the reader has forgotten what the previous code said, and needs to look back and forth to challenge the cache size of their brains.
2) After the code is problematic, the students who add the new code, will they change or not change the code written by the predecessors? Who will carry the bug? This is a soul torture.
Everyone has heard a lot
about premature optimization, so I will not repeat it here.
There is no demand for rationality
, ‘
both ways to write OK, you can pick any one’, ‘I’m okay like this’, this is what I hear a lot.
Get IP
func (i *IPGetter) Get(cardName string). string { i.l.RLock() ip, found := i.m[cardName] i.l.RUnlock() if found {
return ip } i.l.Lock() var err error
ip, err = getNetIP(cardName) if err == nil {
i.m[cardName] = ip } i.l.Unlock() return ip
}
i.l.Unlock() can be placed in the current position or under i.l.Lock() to make defer. Both seem to work when they were first constructed. At this time, many students’ attitude became resolute. Actually, it has to be defer here.
i.l.Lock()
defer i.l.Unlock() var err error
ip, err = getNetIP(cardName) if err != nil {
return 127.0.0.1 } i.m[cardName] = ip return ip
Such a modification is highly likely to happen, it still has to become defer, then, why not start with defer and enter the most reasonable state? If you don’t get into the most reasonable state at the beginning, other students are likely to make mistakes in subsequent collaboration!
Always object-oriented/always like to encapsulate
I came from a software engineering background. The first programming language I learned was C++. The textbook is this one. At that time, after reading the textbook, I first entered the door of programming, and I was shocked by the “packaging” mentioned in it, what a wonderful design, object-oriented, what a smart design. However, over the years, I have seen the ridicule of ‘Yunfeng’s ‘graduates who use MySQL API like to make a class wrapper and reuse’; I have seen various inexplicable definitions of class; I realized that I often have to look at an inexplicable inheritance tree, and I must read the entire inheritance tree as a whole to confirm a small logical branch; I have experienced many times that I need to work hard to suppress my resistance, to refine a clever encapsulated code, and to confirm my bugs. In addition to UI class scenarios, I think less inheritance and more combination.
templateclass CSuperAction : public CSuperActionBase { public: typedef _PKG_TYPE pkg_type; typedef CSuperAction this_type; ...}
This is the code for sspp. CSuperAction and CSuperActionBase, one moment super, one moment base, super and superBase are at what two levels of abstraction, no one can understand without reading through the code. I want to confirm any detail, I have to read through multiple levels of code, what is the encapsulation?
Well, you said that the author did not get the class name well. So, the question is, can you achieve well? Can a new T1.2 student design the class name and class tree well? Even for simple business models, it takes countless ‘bad’ object abstraction practices to cultivate a classmate with qualified class abstraction ability, which is not destructive to large but loose team collaboration? There is already a set of inheritance trees, if you want to add functions, you can only add them in this inheritance tree, the previous inheritance tree is no longer suitable for new needs, all the classes on this inheritance tree, and where they are used, you go to change? No, a normal person will give up and start piling up mountains of.
Encapsulation, that is, I can not care about implementation. However, to make a stable system, each layer of design can go wrong. ABI, there are always suitable usages and inappropriate usages, is there really a part that we can not care about at all about the encapsulation How to achieve it? No, you can’t. Bugs and performance problems often occur when you use the wrong way to use a wrapped function. Even Android, iOS APIs, Golang, Java ready-made APIs, we often have to explore the implementation in order to use the API well. So, should we start up and make a function with strong transparency, which is more reasonable? Users want to know the details, come in, my implementation is very easy to read, you can understand it, you will not get lost when using it! For logically complex functions, we should also emphasize the presentability of the internal working mode of the function ‘allows the reader to imagine and present the complete process in the brain’, so that the user can easily read and understand, be sure, and use it without getting lost!
There is no design at all This is the most terrible, all the requirements, getting started is a meal, ‘What
is design?
I have 5w lines of a file and 5k lines of a function, and I can’t finish the requirements? ‘From the first line of code, it is undesigned, randomly stepping on mud puddles all over the ground, without feeling the eyes of others, one person dances solo, produces code, completes the requirements, and destroys the person who takes over his own code. This is not an example, every student should be able to find this kind of code in their own project class.
must think metaphysically
often, and students like to listen to some trivial ‘work’ when they listen to lectures and open classes. There is no problem with this. However, how many years have you worked and learned about dry goods? Build your own technical thinking ‘face’, enter the three-dimensional ‘engineering thinking’, and connect the technical details and the needs that the system wants to meet in thinking? When listening to a requirement, can you think about how your code packages should be organized and how functions should be organized?
So, how to connect the technical point with the demand? The answer is simple, you need to summarize in time, summarize some clear principles, thought processes. Thinking about how to summarize is especially like thinking about philosophical questions. From some trivial details, from specific situations rise to some principles and axioms. At the same time, when you accept the principle, you should not accept and remember the principle itself, but the structural principle, let this principle be re-reasoned here, and fully grasp the scope of application of this principle.
To be more specific, the metaphysical thinking process of engineering best practices is:
to
classify the problems encountered in engineering practice from the two perspectives of problem type and solution type, and summarize some principles of limited application, from point to point. Combining many summarized principles into your own project code is to combine multiple aspects to build a three-dimensional best practice scheme. When your solution can adapt to a project with 30w+ lines of code, a project with more than 30 people, you are an architect to get started! When your project is multi-terminal, multi-lingual, with more than 300w lines of code and more than 300 participants, the code quality is still very high, the code is still iterating efficiently, eliminating outdated code every day, and filling in high-quality replacement old code and new code. Congratulations, you’re already a very senior architect! Further, you have a unique or comprehensive understanding of a business model, build a set of industry-first solutions, combined with the ability to implement high-quality just now, to achieve such a project. There is nothing to say, you are already an expert engineer. No matter how high the level, I will not understand and will not discuss it here.
So, we need to start from scratch to accumulate thinking and summarizing? No, there is a book called “The Art of Unix Programming”, I read it 3 times at different times, and in a moment, I will talk about some of the principles mentioned in it, which I think are especially worth talking about at Tencent. These principles can be used as the yardstick for everyone to judge the quality of code during code review. But, before that, I have to talk about another very important topic, model design.
model design without reading the OAUTH 2.0 RFC, to design
a third-party authorized login, eventually return to invent another skimming OAuth.”
In 2012, I had just graduated and I was chatting with a South China Polytechnic graduate who went to Guangzhou Unicom. At that time, he said that he was very unhappy at work, because he did not often write code at work, and thought that he had the ACM competition gold medal algorithm proficiency + familiarity with CPP code, writing down pointers to operate memory, what programs could not be written, and what things could not be done well. At the time, I thought, it makes sense, with the programming tools in hand, what can’t I do?
Now, I’ll tell him that complexities like linux, Chromium engines, Windows office, you can’t do it. The reason was that he hadn’t entered the engineering world of software engineering at all. You can’t move bricks to build the Hong Kong-Zhuhai-Macao Bridge. However, this answer is not good, and the arguments used for proof are too far away from us. From one small clue one can see what is coming. I will answer now, you can’t do it, it’s as simple as a permission system, do you know how to do it? Pile up a bunch of logical hierarchies that unfold in one dimension? Simple as a shared file management, do you know how to do it? Pile up a bunch of logical hierarchical one-dimensional unfolded ife LSEs? You have tens of thousands of servers, how do you write a management platform? Pile up a bunch of logical hierarchical one-dimensional unfolded ife LSEs?
Up is to do, can achieve the three seemingly simple requirements mentioned above? Think about how many years Amazon and Alibaba Cloud tossed before finally finding the big killer of containers + Kubernetes. Here, how many years of Google practice on the BORG system is required to come up with an excellent service orchestration domain model. In the field of permissions, there are RBAC, DAC, MAC and other models, and when it comes to business, there will be differences in details. As Domain Driven Design says, without good domain thinking and model abstraction, the logical complexity is n^2 exponential, and you have to write how many ifelse, how many possible if paths you have to think about, to cover all the unexpected. You must have the ability to think and explore, disassemble/abstract/build Domains. I’ve been asked, how do I effectively acquire this ability? I failed to answer this question, as if asking me, how can I get the academic ability of an MIT Ph.D.? I can’t answer. The only answer is that to enter a certain field, it is to first look at the thinking of the predecessors, stand on the shoulders of the predecessors, and then use their own general knowledge ability to think further. As for how to build a good general thinking ability, you may have to go to the Ivy League to read a book 🙂 Or, just think and exercise this ability in engineering practice!
At the same time, the code based on the model design can better adapt to the changing needs of product managers. For example, a calendar app, think simple, not too simple! Isn’t it done to record a user’s daily schedule with ‘userid_date’ as the key? Just one step forward, design a task, the upper limit is distributed to 100w individuals, to create such a task, is to add a record under 100w people? You have to change the previous design and change the db. One step further, to pull out all the things that a user and a person want to participate in, is it to do all the tasks of two people to join? Seems ok. What if it’s all the missions with 100 people? 100 people task to join? It’s unrealistic. Okay, you introduce a group ID, so is your original ‘userid_date’ key design going to be modified and migrated again? Often come to a demand, you have to overturn the system and start over, or simply can only refuse the user’s needs, such combat effectiveness, it’s good to call yourself an engineer? You should start by thinking about the business domain you are facing, thinking about the possible model boundaries of your calendar application, taking all the possible capabilities to think about it, building a model, designing a set of common store layer interfaces, and logical code based on common interfaces. When the product continues to evolve, it is to keep filling the model with content, not overturning and starting over. This, thinking about model boundaries and building model details, are two very important capabilities, which are also the capabilities that most Tencent product managers do not have, and you have to have, which is extremely beneficial to the whole team. When you meet product managers, listen to their needs out of responsibility for the user experience, and go to yourself and cover these bits and pieces with a complete model.
Model design is an aspect of metaphysical thinking, a particularly important aspect. Next, let’s plagiarize the practice of plagiarizing the UNIX operating system to summarize the previous practical experience and ‘axioms’. In your own coding/code review, think on the shoulders of giants. Instead of rediscovering classical mechanics, we move towards the theory of relativity.
UNIX design philosophy
and people who don’t understand Unix are doomed to end up inventing an apostrophic Unix.” –Henry Spenncer, 1987.11The
following passage is so classic that I must reproduce it (from The Art of UNIX Programming): “Each branch of engineering and design has its own technical culture. In most engineering fields, some unwritten industry literacy is as important as standard manuals and textbooks in terms of the composition of a professional’s literacy (and often more important than books) as the experience of professionals accumulates. Senior engineers accumulate a large amount of tacit knowledge in their work, and they pass it on to their juniors through words and deeds in a way similar to Zen Buddhism. Software engineering is an exception to this rule: technology is changing so fast, the software environment is changing rapidly, and the culture of software technology is on the rise. However, there are exceptions among the exceptions. There are very few software technologies that have proven durable enough to evolve into a strong technology culture, distinctive art, and design philosophy passed down from generation to generation. Next
, I will use my understanding to explain a few principles that we often fail to do.
Keep It Simple Stuped!
The KISS principle, everyone should be thunderous. But, are you really complying? What is Simple? Simple? Rob Pike, one of the main designers of the golang language, said “Avenue to simplicity”, does this “simplicity” mean the same as simplicity?
First of all, simplicity is not a problem, we imprint the solution of the first image as simple. I say a word and feel it. It is easy to make a thing, and it is a difficult thing to do it in the simplest and most effective way. For example, to make a three-way license, OAUTH 2.0 is very simple, all concepts and details are compact, complete, and easy to use. Do you think it’s easy to design to OAUTH2.0? To be simple, you must have a comprehensive understanding of the problem you are dealing with, and then you need to accumulate thinking to understand the problem from all angles and levels, and polish a popular, compact, and complete design, just like the interaction design of iOS. Simplicity is not easy to do, you need to accumulate thinking in the process of continuous time and code review, trigger thinking in PK, summarize thinking in communication, in order to do better and better, close to ‘road to simplicity’.
Two classic model diagrams, simple and comprehensive, feel it, don’t understand, you can immediately google and learn by yourself: RBAC:
logging:
principle 3 Composition principle: Consider stitching combinations when designing
about OOP and inheritance, which I have said earlier. So how do we organize our modules? Yes, in combination. The Linux operating system is so close to us, how is it architected? To put it in a small way, we concatenate a collection of data for a business request, if we use BaseSession, XXXSession inherit BaseSession design, in fact, this inheritance tree, it is difficult to adapt to endless changes. However, if you use combinations, you can disassemble various parts that may be needed, such as UserSignature, and combine them when needed, constantly adding new parts without the mental burden of remembering the old inheritance tree.
Using a combination is about making it clear which part you have right now. If there are too many parts, in fact, the step of combining the final product will have a high mental burden, and each part will be unfolded, dazzling and dazzling. For example, QT, a general UI framework, looks at its class list, there are more than 1000. If you don’t organize it without inheritance trees, spread it out, and combine it to form a page, it will become mentally burdensome to bear. OOP effectively controls the complexity in scenarios with high complexity such as ‘requiring countless elements to be displayed at the same time’. ‘So, Guldan, at what cost? The price is that at the beginning, this top-down design is involved, and every adjustment becomes extremely difficult.
In actual projects, students of different career levels work together to modify the code of a server, and it will appear, and students with low ranks cannot change anywhere, and they have no ability to modify at all, and high-level students can modify correctly, and they are not willing to modify on a large scale, and the whole project becomes more and more unreasonable. Students who do not fully understand the entire inheritance tree are not qualified to make any changes that have adjustments to the inheritance tree, and collaboration becomes difficult. The code changes have become the result of relying on a senior architect to monitor changes in the inheritance system intensively, and low-level students are tied up. Combination, it solves this problem very well, the problem is constantly subdivided, and each student can well overcome the points they need to overcome and realize a package. Product logic code, only need to combine each package, can achieve the effect.
This is the
definition of http request in the golang standard library, which is the result of the collection of all the features of the HTTP request. The parts of the general/mutant/multiple implementations are abstracted through duck interfaces, such as Body io. ReadCloser。 If you want to know what details, start with the parts that are combined into a request, and to modify, you only need to modify the corresponding parts. [After this code, compare .NET’s HTTP OOP-based abstraction].
// A Request represents an HTTP request received by a server
// or to be sent by a client.
//
// The field semantics differ slightly between client and server
// usage. In addition to the notes on the fields below, see the
// documentation for Request.Write and RoundTripper.
type Request struct {
// Method specifies the HTTP method (GET, POST, PUT, etc.).
// For client requests, an empty string means GET.
//
// Go's HTTP client does not support sending a request with
// the CONNECT method. See the documentation on Transport for
// details.
Method string // URL specifies either the URI being requested (for server
// requests) or the URL to access (for client requests).
//
// For server requests, the URL is parsed from the URI
// supplied on the Request-Line as stored in RequestURI. For
// most requests, fields other than Path and RawQuery will be
// empty. (See RFC 7230, Section 5.3)
//
// For client requests, the URL's Host specifies the server to
// connect to, while the Request's Host field optionally
specifies the Host header value to send in the HTTP
// request. URL *url. URL // The protocol version for incoming server requests.
//
// For client requests, these fields are ignored. The HTTP
// client code always uses either HTTP/1.1 or HTTP/2.
// See the docs on Transport for details.
Proto string // HTTP/1.0
ProtoMajor int // 1
ProtoMinor int // 0 Header contains the request header fields either received
// by the server or to be sent by the client.
//
// If a server received a request with header lines,
//
// Host: example.com
// accept-encoding: gzip, deflate
// Accept-Language: en-us
// fOO: Bar
// foo: two
//
// then
//
// Header = map[string][]string{
// Accept-Encoding: {gzip, deflate},
// Accept-Language: {en-us},
// Foo: {Bar , two},
// }
//
// For incoming requests, the Host header is promoted to the
// Request.Host field and removed from the Header map.
//
// HTTP defines that header names are case-insensitive. The
// request parser implements this by using CanonicalHeaderKey,
// making the first character and any characters following a
hyphen uppercase and the rest lowercase.
//
// For client requests, certain headers such as Content-Length
// and Connection are automatically written when needed and
values in Header may be ignored. See the documentation
// for the Request.Write method. Header Header // Body is the request's body.
//
// For client requests, a nil body means the request has no
// body, such as a GET request. The HTTP Client's Transport
// is responsible for calling the Close method.
//
// For server requests, the Request Body is always non-nil
// but will return EOF immediately when no body is present.
// The Server will close the request body. The ServeHTTP
// Handler does not need to. Body io. ReadCloser // GetBody defines an optional func to return a new copy of
// Body. It is used for client requests when a redirect requires
// reading the body more than once. Use of GetBody still
// requires setting Body.
//
// For server requests, it is unused.
GetBody func() (io. ReadCloser, error) // ContentLength records the length of the associated content.
// The value -1 indicates that the length is unknown.
// Values >= 0 indicate that the given number of bytes may
// be read from Body.
//
// For client requests, a value of 0 with a non-nil Body is
// also treated as unknown.
ContentLength int64 // TransferEncoding lists the transfer encodings from outermost to
// innermost. An empty list denotes the identity encoding.
// TransferEncoding can usually be ignored; chunked encoding is
// automatically added and removed as necessary when sending and
// receiving requests.
TransferEncoding []string // Close indicates whether to close the connection after
// replying to this request (for servers) or after sending this
// request and reading its response (for clients).
//
// For server requests, the HTTP server handles this automatically
// and this field is not needed by Handlers.
//
// For client requests, setting this field prevents re-use of
// TCP connections between requests to the same hosts, as if
Transport.DisableKeepAlives were set.
Close bool // For server requests, Host specifies the host on which the
// URL is sought. For HTTP/1 (per RFC 7230, p 5.4), this
// is either the value of the Host header or the host name
// given in the URL itself. For HTTP/2, it is the value of the
// :authority pseudo-header field.
// It may be of the form host:port. For international domain
// names, Host may be in Punycode or Unicode form. Use
// golang.org/x/net/idna to convert it to either format if
// needed.
// To prevent DNS rebinding attacks, server Handlers should
// validate that the Host header has a value for which the
Handler considers itself authoritative. The included
// ServeMux supports patterns registered to particular host
// names and thus protects its registered Handlers.
//
// For client requests, Host optionally overrides the Host
// header to send. If empty, the Request.Write method uses
// the value of URL. Host. Host may contain an international
// domain name.
Host string // Form contains the parsed form data, including both the URL
// field's query parameters and the PATCH, POST, or PUT form data.
// This field is only available after ParseForm is called.
// The HTTP client ignores Form and uses Body instead. Form url. Values // PostForm contains the parsed form data from PATCH, POST
// or PUT body parameters.
//
// This field is only available after ParseForm is called.
// The HTTP client ignores PostForm and uses Body instead. PostForm url. Values // MultipartForm is the parsed multipart form, including file uploads.
// This field is only available after ParseMultipartForm is called.
// The HTTP client ignores MultipartForm and uses Body instead. MultipartForm *multipart. Form // Trailer specifies additional headers that are sent after the request
// body.
//
// For server requests, the Trailer map initially contains only the
// trailer keys, with nil values. (The client declares which trailers it
// will later send.) While the handler is reading from Body, it must
// not reference Trailer. After reading from Body returns EOF, Trailer
// can be read again and will contain non-nil values, if they were sent
// by the client.
//
// For client requests, Trailer must be initialized to a map containing
// the trailer keys to later send. The values may be nil or their final
// values. The ContentLength must be 0 or -1, to send a chunked request.
// After the HTTP request is sent the map values can be updated while
// the request body is read. Once the body returns EOF, the caller must
// not mutate Trailer.
//
// Few HTTP clients, servers, or proxies support HTTP trailers. Trailer Header // RemoteAddr allows HTTP servers and other software to record
// the network address that sent the request, usually for
logging. This field is not filled in by ReadRequest and
// has no defined format. The HTTP server in this package
// sets RemoteAddr to an IP:port address before invoking a
// handler.
// This field is ignored by the HTTP client.
RemoteAddr string // RequestURI is the unmodified request-target of the
// Request-Line (RFC 7230, Section 3.1.1) as sent by the client
// to a server. Usually the URL field should be used instead.
// It is an error to set this field in an HTTP client request.
RequestURI string // TLS allows HTTP servers and other software to record
// information about the TLS connection on which the request
// was received. This field is not filled in by ReadRequest.
// The HTTP server in this package sets the field for
// TLS-enabled connections before invoking a handler;
// otherwise it leaves the field nil.
// This field is ignored by the HTTP client. TLS *tls. ConnectionState // Cancel is an optional channel whose closure indicates that the client
// request should be regarded as canceled. Not all implementations of
// RoundTripper may support Cancel.
//
// For server requests, this field is not applicable.
//
// Deprecated: Set the Request's context with NewRequestWithContext
// instead. If a Request's Cancel field and context are both
// set, it is undefined whether Cancel is respected.
Cancel <-chan struct{} // Response is the redirect response which caused this request
// to be created. This field is only populated during client
// redirects. Response *Response // ctx is either the client or server context. It should only
// be modified via copying the whole Request using WithContext.
// It is unexported to prevent people from using Context wrong
// and mutating the contexts held by callers of the same request. ctx context. Context}
Looking at the
abstraction of web services in .NET, I can’t know where a detail I care about is just looking at the ends, without looking at the complete picture of the entire inheritance tree. Furthermore, if I want to modify any function in the entire HTTP service system, I can’t put aside the understanding and familiarity of the overall complete design, and it is very easy to destroy the overall design unconsciously.
When it comes to combination, there is another closely related word called pluginization. Everyone is happy with vscode, where is it more successful than Visual Studio? If VSCODE achieves what Visual Studio does by adding a bunch of plugins, it becomes something else similar to Visual Studio, called VS Studio. You should find the problem, we don’t really need most of the features of Visual Studio many times, and want to flexibly customize some of the more niche capabilities, using some niche plugins. Even, we want to choose plugins of the same type with different implementations. This is the power of combination, a variety of different combinations, it is simple, but it meets various needs, flexible and changeable, to achieve a plug-in, do not need to master a huge system in advance. The same is true for the code. At least the back-end development field, combined, than OOP, ‘fragrant’ a lot.
Principle 6 Principle of miserliness: Unless there is no other way, do not write huge programs
Some students may feel that it is better to write a large program to evaluate T11 and T12. When leaders look at the review plan, it is easy to think: big, good, comprehensive. But do we really need to write such a big program?
I’m going to say again, Guldan, at what cost? The trade-off is that the more code, the harder it is to maintain and tweak. Ken Thompson, the father of the C language, said that deleting a line of code gives me a greater sense of accomplishment than adding a line. We are stingy with code. If you can make the system small, don’t make it big. Tencent has no shortage of 200w+ line clients, which are big and very good. However, the students asked themselves if they could still adjust the structure. Hand Q students, look at their code, have you ever sighed? Do small things that can be done, seek generalization, isolate modules and capabilities through duck interfaces (even multi-processes, multi-threading for isolation capabilities), and always think of deleting the amount of code, in order to maintain the maintainability of the code and face future needs and architecture, and adjust their own vitality. Client code, UI rendering module can be complex to blow up the sky, non-UI parts should pursue the simplest, ability interface, replaceable, recombination ability.
When landing on everyone’s code, when reviewing, you should pay the most attention to the core struct definition, build a complete model, core interface, clarify the dependence of the abstract model on the outside, and clarify the ability of the abstract model to provide to the outside world. The rest of the code is to use the simplest, plain code to implement the internal details of the model.
Principle 7 Transparency Principle: Design to be visible for review and debugging
First, define what transparency and visibility are.
If there are no dark corners and hidden depths, the software system is transparent. Transparency is a passive quality. If you can actually predict all or most of the program’s behavior and build a simple mental model, the program is transparent because you can see what the machine is doing.
A software system is visible if it contains functions designed to help people build the right mental model of what to do and how to do it. So, for example, good documentation helps improve explicability for users; For programmers, good variable and function names help improve explicability. Explicability is an active quality. To achieve this in software, it is not enough to be obscure, it must also be as helpful as possible.
If we want to write good programs and reduce bugs, we need to increase our control over the code. You always do it and understand how the function/reused code you call is implemented. Otherwise, you may call IO-blocking functions in the server of a single-threaded state machine, and your server throughput will drop to the end. Furthermore, in order to ensure that everyone can have control over their code, the functions written by everyone must have a high degree of transparency. Instead of writing some functions/code that I can’t understand for a while, and as a result, people who are forced to use your code directly give up the pursuit of control, or even give up reusing your code, and start abyss towards ‘creating duplicate code’.
Transparency is actually relatively easy to do, and people can do well if they consciously exercise for a month or two. Explicability is not easy. There is a phenomenon that each function you write does not exceed 80 lines, and I can understand each line, but you call layer by layer, many function calls, how to combine to achieve a certain function, read twice, or I can’t understand. The third pass may be able to understand it roughly. I probably understand, but it’s too complicated to build your overall process of achieving this function in your brain. As a result, the reader simply doesn’t have good control over your code.
The standard of explicability is very simple, everyone looks at a piece of code, whether you understand it or not, you will understand it at once. But how do you do a good job of explicability? That is, to pursue reasonable function grouping, reasonable function upper and lower levels, the same level of code will appear in the same function, the pursuit of easy-to-understand function grouping layering, is the road to explicit.
Of course, complex such as Linux operating systems, Office documents, the problem itself is very complex, disassembled, layered, combined reasonably, it is difficult to establish a mental model. At this point, complete documentation is required. Complete documentation also needs to be in the place closest to the code, so that people ‘know that the complex logic here is documented’, not the actual documentation, but the reader does not know. Take a look at the http in the golang standard library above. Request, feel its efforts on explicability? Yes, just learn it.
Principle 10 Popular principle: Interface design avoids
unconventional
design procedures that are too unconventional, which may make it more difficult for others to understand.
In general, we define a ‘point’ like this, using x for the abscissa and y for the ordinate:
type Point struct { X float64
Y float64
}
You just want to be different and precise:
type Point struct {
VerticalOrdinate float64
HorizontalOrdinate float64}
is very good, you
use words very precisely, and ordinary people can’t refute you. However, most people read your VerticalOrdinate just don’t read X to understand it quickly, easily understand, and conveniently. You’re deliberately creating collaboration costs.
The above examples are common, but they are not the least obvious problem that the principle of least innovation is most trying to illustrate. Imagine a program in which you add elements to an array with the symbol ‘+’ instead of mathematically ‘add’, ‘result:= 1+2’ –> ‘result = []int{1, 2}’ instead of ‘result=3’, then you are unconventional and destructive to the program. The flip side of the principle of least disagreement is to avoid appearing to want to die when in fact it is slightly different. This can be extremely dangerous, because superficial similarity often leads to false assumptions. So it’s better to make things distinct, rather than looking almost identical. — Henry Spencer。
You implement a db. The Add() function does db. AddOrUpdate(), someone used your interface and mistakenly overwrote the data.
Principle
11 The Principle of Silence: If a program has nothing to say,
the principle of silence
should be one of the most frequently violated principles. A short piece of code inserts various ‘log(cmd xxx enter)’, ‘log(req data + req. String())’, very afraid that I didn’t print enough information. I’m afraid that I don’t know that the program execution is successful, and I always have to ‘log(success)” in the end. But, let me ask you, have you really had the patience to read a bunch of logs from code written by others? Didn’t you need which one, just in a bunch of logs, and then print a log with a special tag ‘log(this_is_my_log_ + xxxxx)’? As a result, the log printed by the first author has no value at all when the code is handed over to others or collaborated with others, but it makes it more difficult for everyone to read the log.
As soon as a service runs up, it frantically hits logs, and requests are processed normally and a bunch of logs are played. The rolling log drowns in the error log. The error log loses its effect, and simply tail looking at the log, dazzled, and can’t see any problems, isn’t it ‘in order to catch the problem’ and making yourself ‘unable to catch the problem at all’?
Silence is golden. In addition to a simple stat log, if your program ‘sounds’, then the message it throws must be valid! Printing a log(‘process fail’) is also worthless, what the hell fails? Which user with what parameters and how to fail in which link? If you speak up, you must give all the necessary information. Otherwise, it is silent and shows that you are working well. Not being silent is the best news, and now everything is fine with my work!
A well-designed program treats the user’s attention as a finite and valuable resource that is only required when necessary. The programmer’s own workhorse is also a valuable resource! Only when necessary, the log comes to remind the programmer, ‘I have a problem, come and see,’ and must be given enough information to explain what is going on. Instead of programmers, they need a lot of aids to figure out what’s going on.
Whenever I publish a program, I spot check a machine and look at its logs. I was very happy when I found that there were only external access, internal RPCs/latency distribution logs per minute. I know, this minute, its success rate is again 100%, no problem!
Principle 12 Remedy Principle: When an exception occurs, exit immediately and give enough error information
,
the problem is very simple, if an exception occurs, the exception does not exist because we try to cover it up. Therefore, program errors and logical errors should be treated strictly separately. It’s a matter of attitude.
‘Exceptions are the norm for internet servers’. Logical errors are counted through metrics, and we do an alarm analysis. For bugs, we must be strict about collecting the necessary information at the earliest point of the problem, and telling the developer and maintainer in a high voice, ‘I have an abnormality, please fix me immediately!’. It can be a panic that has not been captured. You can also do a unified recovery mechanism at a topmost position, but you must be able to obtain accurate exception information about the exact abnormal position when recovering. There can be no intermediate catch mechanism, after the catch loses a lot of information and then passes it up.
Many Java development students, who do not distinguish between program errors and logic errors, are either very tolerant or strict, which is devastating to the maintainability of the code. There were no bugs in my program, and if there were, I solved it at the time. Only in this way can the quality of the program code be kept relatively stable, and extinguishing the fire when the flame appears is the best way to extinguish the fire. Of course, the more effective way is the prevention of fully automated testing 🙂<
a href=” http://mp.weixin.qq.com/s?__biz=MzIxMTE0ODU5NQ==&mid=2650246959&idx=1&sn=4eb69030407247cbff8c1ccf8fac1ec8&chksm=8f5ae573b82d6c65ab034c6169dda6fbf66152e248d1557683a013eafc753139584e3ca25164&scene=21#wechat_redirect”> specific practice points
have raised many questions about the direction of thinking. Big principle issues and directions. Here, I will briefly list a few details and implementation points for you. After all, everyone has to get started, starting with implementation, and then summarizing and thinking, and being able to copy my way of thinking. The following is for the golang language, which is slightly different from other languages. Also, I can’t think of all the rules I have implemented for a while, which is why I emphasize the importance of ‘principles’, which are enumerable.
-
100% strict implementation of code format specifications, seriously not tolerating a little sand.
-
The file must not exceed 800 lines, and if it does, be sure to think about how to open the file. Engineering thinking is accumulated when dismantling documents.
-
The function pair must not exceed 80 lines, exceed, be sure to think about how to disassemble the function, think about the function grouping, the level. Engineering thinking is accumulated when dismantling documents.
-
code nesting level cannot exceed 4 levels, if it exceeds it, it must be changed. Think more about whether you can return early. Engineering thinking is accumulated when dismantling documents.
if !needContinue { doA() return
} else { doB() return
}
if !needContinue { doA() return}doB()
return
The following is early return
, the code on both ends is logically decoupled.
-
from directories, packages, files, structs, and functions layer by layer, information must not be redundant. For example, file. FileProperty is such a definition. Only when each ‘definite’ appears in only one position does it open up the possibility of ‘doing a good job of logic and defining grouping/hierarchies’.
-
Use multiple levels of directories to organize the information your code carries, even if some intermediate directories have only one subdirectory.
-
expands, the old code violates some design principles and should be immediately partially refactored in place to maintain the quality of the code from slippery. For example: unpacking documents; Split function; Use Session to save all the information of a complex process-type function; Restructure the directory.
-
Based on the previous considerations, we should try to make the code of the project have a certain organization and hierarchical relationship. My personal current practice is to put all but the particularly general code in one git. The code that is particularly common and has few modifications gradually becomes independent of git and connects to the current project git as a child git, so that the refactor feature of goland and various refactor tools can help us quickly and safely refactor locally.
-
Your own project code should have an endogenous hierarchy and logical relationship. flat tile unfolding is very detrimental to code reuse. How to reuse and how to organize reuse will definitely become a ‘life problem’. The students of T4-T7 are simply not capable of solving this problem.
-
If the code being reviewed is short, but you look at it and don’t understand it, there must be a problem. I can’t see it, so I talk to high-level classmates. This is a growing moment for you and your classmates who review code.
-
Logs should be played less. To log the key, you must bring the key index information with you. The necessary logs must be hit.
-
Ask when in doubt, and don’t be afraid to ask the wrong question. Let the code author give an explanation. Don’t be afraid to ask extremely low questions.
-
Don’t say ‘suggestion’, ask questions, just now, you PK but me, you have to change!
-
Please actively use TRPC. Always have to stand with the boss! Only a consensus on code quality construction reached with the boss can better do a good job in code quality construction in the team.
-
Eliminate repetition! Eliminate repetition! Eliminate repetition!
As the code
backbone developmentFinally
, let me say one more word for ‘backbone development’. Quite simply, only if less than 500 lines of code are reviewed at a time, the reviewer can read it quickly and almost nothing. With more than 500 lines, the reviewer can’t look closely, only roughly browsing. Moreover, it is much easier to let you adjust the logic within 500 lines of code than to adjust 3000 lines or more, not just by 6 times, but by orders of magnitude or two. There is a problem, it is adjusted when it first appears, and it will not bring a large modification burden to the person who is revew.
There are many good materials and books about CI (continuous integration), and everyone should learn in time.
The Art of Unix Programming recommends
that
you find this book and read it. In particular, students at T7 and higher. You have accumulated a lot of code practices, and you urgently need to think about ‘engineering’. Many engineering methodologies are outdated, and the content of this book is the exception of the exception. What it expresses is not obsolete by the constant replacement of software technology.
Zen Buddhism talks about ‘no writing’ (not standing up words, teaching other teachings, pointing directly at people’s hearts, seeing sex to become Buddhas), many truths and feelings cannot be conveyed in words, and the ability to express words cannot be expressed. People often feel a sense of reassurance because they have heard and know a certain truth, thinking that I understand this truth, but I can’t do it in practice. Knowing is easy to do, but knowing but not being able to do it, in engineering practice, there is no difference between ‘not understanding this truth’.
Once, I
interviewed the director of another company, and I talked like a set, the code was pulled out for a walk, and I didn’t do it at all, just hearsay. His exploration of engineering practice can be said to have been basically cut off. I can only wish you to do a good job in upward management and follow your own path of pure management. Please stop saying that you have a pursuit of technology and are a technical person!
Therefore, you should not just read my article, but continue to practice and accumulate your own ‘foreign teaching’ in practice.
end
public number (zhisheng) reply to Face, ClickHouse, ES, Flink, Spring, Java, Kafka, Monitoring < keywords such as span class="js_darkmode__148"> to view more articles corresponding to keywords.
like + Looking, less bugs 👇