14 Master Programmer's Job Secrets

14 The Work Secrets of Master Programmers #

Hello, I’m Zheng Ye.

In my previous sharing, I talked about the origins of TDD, and those who are not yet familiar with TDD can be divided into two groups. One group is eager to try it out, while the other group has already practiced but encountered difficulties in implementation. Therefore, it is common to hear people say that TDD is not practical.

But is TDD really not practical?

Like any skill, TDD also requires practice. More importantly, you need to master the “acupoints” of TDD, and this is exactly the focus of this module: task decomposition. Moreover, in today’s content, I will also show you the work style of master programmers. Let’s get started!

Where does TDD come from? #

To learn the most authentic TDD, it is best to start from the source.

In the past, TDD was only popular in small circles, and what really made it well-known in the industry was Kent Beck’s famous book “Extreme Programming Explained”. This is an important work that introduces a software development method: Extreme Programming.

At the time he wrote it, many people were trying to explore software engineering methods beyond the waterfall development method. In addition to Extreme Programming, there were also methods like Feature-Driven Development and Crystal Methods. It was the exploration of these development methods that led to the birth of agile methods.

The greatest contribution of Extreme Programming to the industry is that it introduced a lot of practices, such as continuous integration mentioned earlier, TDD mentioned here, as well as practices like pair programming and on-site customer involvement.

The reason why it is called “Extreme” Programming is because its underlying concept is to push good practices to the extreme.

When we mentioned continuous integration earlier, we have already introduced this concept. If integration is good, we integrate early and push it to the extreme by integrating with every modification, which is continuous integration.

If developer testing is good, we test early and push it to the extreme by writing tests first and adjusting the code according to the tests, which is test-driven development.

If code review is good, we do more reviews and push it to the extreme by doing code reviews anytime and anywhere, which is pair programming.

If customer communication is good, we communicate with customers more and push it to the extreme by having the customer always with the development team, which is on-site customer involvement. This extreme thinking is a good way to approach problem-solving, and I recommend you to try it in your work as well.

Although TDD is just one of the many practices mentioned in “Extreme Programming Explained”, it is the practice that is most closely related to developers.

As TDD gradually became popular, people became more and more interested in how to do TDD. As a result, Kent Beck wrote another book specifically for TDD, called “Test-Driven Development”.

Secrets of Master Programmers #

The book “Test-Driven Development” is very interesting. If you’re only interested in understanding TDD, this book may be boring. In the first part, Kent Beck is just writing a function and adding to it piece by piece.

I read this book twice. The first time, I thought it was ordinary and that I could write that kind of code too. The second time, when I understood his thought process, I was almost shocked because it completely showcased Kent Beck’s way of working. This is also why I included TDD in this section, because what Kent Beck is doing is task decomposition. Task decomposition is the real value of this book.

At the time, I had been working for many years and thought I was already very professional in writing code. But after understanding Kent Beck’s thought process, I realized that I wasn’t as professional as him.

So how does Kent Beck do it? Whenever he encounters a task that needs to be done, he always breaks it down into several smaller tasks and writes them down on a checklist. Then, he goes through the cycle of writing tests, writing code, and refactoring. After completing one cycle, he crosses off the tasks that have been done and starts the next one.

Whenever he encounters any new problems during the problem-solving process, he records these problems on the checklist to ensure they don’t get lost, and then he continues working on the task at hand. As he completes each task, the problem is solved.

You may wonder what’s so special about this? You can answer this question: How long does it take for you to submit code? If your answer is more than half a day, I’m sorry, but your approach is too big. The reason why you can’t make small steps in your submissions is because you’re involving too many related parts.

Kent Beck’s approach is clear and rhythmic. After completing each task, the code is ready for submission. It may seem simple, but most programmers can’t do it.

Only by breaking down tasks into small enough pieces can you achieve small, incremental submissions. Being able to break down tasks into small pieces actually proves that you have thought it through. And the reason why most programmers have low development efficiency is often because they start without thinking it through.

When I was working at ThoughtWorks, everyone had a sponsor, similar to the master-apprentice relationship in a factory. My sponsor at the time was the current CEO of ThoughtWorks, Guo Xiao, who also comes from a coding background. Once, he told me about the scenario of pair programming with Ward Cunningham, the creator of Wiki.

Every day, Ward would receive a requirement, but instead of rushing to write code, he would do task decomposition with Guo Xiao until every task was clear. Only then would they start working. From there, it was simple – completing one task after another.

At the time, Guo Xiao felt that the workload was intense, but the thought process was very clear. Sometimes, he was also amazed because before starting the work, he would think that the problem was very difficult to solve. But once they decomposed it step by step, each step was clear, and they completed it without encountering many difficulties.

The reason why I’m telling you this story about Ward Cunningham is that he was in the same circle as Kent Beck at that time, discussing progress together. Therefore, their thinking process in problem-solving is very much the same.

Why is task decomposition so important for TDD? Because only when tasks are broken down small enough can you know how to write tests.

Many people find TDD exercises simple when they see others doing them, but they don’t know how to approach them when they try it themselves. They are missing the step of task decomposition in between.

Task decomposition is a good habit, but to master it, a lot of practice is necessary. I myself have spent a lot of time practicing, and every time I receive a task, I always do task decomposition, thinking about how to break it down into small tasks that can be completed step by step before starting to solve it.

Microtasks #

As I have been practicing task decomposition more, I have come to understand the key to task decomposition is: small.

How small, you may ask? Sometimes it can be so small that you may think it is not worth being its own separate task. For example, upgrading a dependency version or renaming a variable.

What are the benefits of doing this? It ensures that I can stop at any time.

I once read a story about the famous golfer, Tiger Woods, in a book. Golfers can be disturbed by external factors while playing. Typically, if a golfer has already started swinging the club and gets disturbed, most players would still continue with the swing, but the usual result is not ideal.

However, when Woods encounters such a situation, he stops and reattempts the swing, ensuring the standard of each swing.

While Woods can stop, it is not only because of a lot of practice, but also because for others, swinging the club and hitting the ball is one continuous action. However, for Woods, this action is composed of several micro-actions. He has just completed one micro-action without proceeding to the next.

In other words, everyone is completing an atomic operation, but Woods’ atomic operation is much smaller than others'.

Similarly, when we write programs, we do not like to be interrupted because once interrupted, it takes a long time to resume the state. After all, we are not as easily adaptable to context switching as an operating system.

But if the task is small enough, after completing a task, we can choose to move on to the next task or stop. This way, even if we are interrupted, we can quickly finish a task without being too affected.

In fact, this extremely tiny atomic operation has applications in other fields as well. There is a practice called “microhabit,” and taking common fitness as an example, many people find it difficult to stick to it mainly because when people think of fitness, they think of sweating profusely in a gym scene, which discourages them.

But what if you only do one push-up at a time? For most people, this is not a very difficult task, so just do one. If you still want to do more after completing one, continue; if not, then don’t.

One push-up? You might say that this counts as fitness. Indeed, one push-up is a very small movement, but the important thing is that one push-up is something you can consistently complete. Most people cannot even do this every day. We know that the hardest part of forming a habit is consistency. If you have a microhabit, consistency becomes easier.

I once continuously committed code on GitHub for 1000 days. What does that mean? For almost three years, every day I was able to consistently write and commit code, not including code written for work.

For most people, this is unimaginable. But I achieved it, not because I am extraordinary, but because I developed my own microhabit.

The foundation of this continuous commitment is my constant effort to break things down into smaller tasks while practicing task decomposition. This way, I can guarantee at least completing a small step every day. Of course, if I have more time, I will write more. It is through this method that I persisted for 1000 days and mastered the skill of task decomposition.

Once a task is decomposed, the content to focus on is limited, allowing us to think more clearly about all the details regarding that task. One important reason why many people’s code is riddled with bugs is because the granularity of the tasks is too large.

As ordinary people, our ability to consider the scale of a problem is limited, making it difficult to consider every aspect carefully.

Micro-operations and Branching Model #

After practicing this, task decomposition has become my instinct, no longer limited to writing code. Whenever I encounter a problem that needs solving, my first reaction in my mind is always to think about how it can be completed step by step. Once I have determined the decomposition, solving the problem is just a matter of following those steps.

If I cannot decompose the problem well, it means that I haven’t thought it through and I need more information or a better solution.

Once you understand the importance of task decomposition and even reach the level of micro-operations through training, you will easily understand the problems that arise from taking big steps. Let me give you an example of a common problem in development, the branching strategy for code development.

There are many different practices in the industry when it comes to branching strategies. Some teams have everyone writing code on the same branch, while others have each person pull out a branch, write their code, and then merge it back. Have you ever wondered why there is such a difference?

The best practice in the industry is based on the main branch model. Everyone develops on the same branch because pulling out branches can be a hassle, even though the advent of Git has greatly reduced the cost of creating branches.

But why do some people still create a separate branch for development? Most of the time, it’s because the code they write is too extensive, and the amount of changes is too large to be quickly merged back into the main development branch.

Then the next question arises, why did they write so much code? Yes, the answer is that they took too big of steps.

If you understand task decomposition, each task that is decomposed will require only a small amount of code changes, and the impact will be within a controllable range. The code can be quickly merged into the main development branch, so there is no need to create separate branches.

In my actual work, the teams I lead basically adopt the strategy based on the main development branch. Only when doing some experiments will we create a separate development branch, but this is not the norm.

Summary #

TDD is not practical in the eyes of many people. One reason is that they do not understand the meaning of “driving” development through testing. But more importantly, they rarely do task decomposition. Task decomposition is a key point in doing TDD. Only by decomposing tasks to a testable level can we write tests with purpose.

Similarly, different people have different understandings of task decomposition when they hear about it. I define the result of task decomposition as micro-operations, which are much smaller than what most people understand. The smaller we can decompose tasks, the larger the granularity of our atomic operations. Many problems in software development are caused by too large granularity, such as branching strategies.

If you can only remember one thing from today’s content, please remember: divide tasks into smaller ones, the smaller the better.

Finally, I would like to ask you to share whether you have encountered problems due to inadequate task decomposition. Please write down your thoughts in the comments section.

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