今日推荐英文原文：《The 3 Skills That Helped Me Become a Better Software Engineer》作者：Victor Savkin
The 3 Skills That Helped Me Become a Better Software Engineer
When asked “How to become a good software engineer?”, I recommend the following three skills:
- Divide and Simplify
- Develop Good Mental Models
- Learn Your Tools
Don’t get me wrong, there are no shortcuts here — it takes many years of deliberate practice to become a decent engineer. The three skills above are something most good engineers practice. Almost without exception, when I interview a person who is good at the three skills, I know she is going to be a remarkable engineer.
Divide and Simplify
Since we are unable to keep more than 5–7 objects in working memory, any non-trivial problem is too large to think about in detail. We can either think about the problem abstractly, or think about a small part of the problem in detail. Both are important. It’s also important to be aware of the level of abstraction you are currently operating at. Either think abstractly, using metaphors and bulk approximations, or think precisely. Try not to mix the two.
This is the process I follow when dividing a problem into subproblems:
Step 1. Make sure you understand the problem.
- State the problem on paper.
- Write down all the constraints you know of.
- Write down the things you don’t know that could have been helpful. Find them out.
Step 2. Draw a diagram of the problem domain.
- A few boxes and arrows, nothing fancy.
- This diagram should give you an idea on how to divide the problem into subproblems.
- Find a way to draw a diagram that divides the problem domain differently. If you cannot do it, you probably don’t understand the problem domain well enough.
- Pick one way of dividing the problem.
Step 3. Pick a subproblem and use the same process to subdivide it further.
- Stop when problems (problem subdomain) are small and clear.
- Solve them individually and combine the results.
Subdividing works well when dealing with deep problems because every division removes one layer. Real-world systems aren’t just deep, they are also broad.
This means that even though you might be at the right level of abstraction, there is still too much detail for you to solve the problem. In this case, take the problem and come up with a simple reproduction illustrating it. Solve it in this simplified context, and then apply the fix to the real-world problem.
Imagine you are investigating a performance issue. Say by dividing the problem over and over again, you figured out that the issue is with a grid component. Unfortunately, you have dozens and dozens of rows of components rendered by the grid that obscure your investigation. After spending an hour trying to solve it as is, step back to figure out a way to simplify it. Create a new application using this grid reproducing the issue. Make it as simple as possible (e.g., maybe one column showing text). Use it to fix the problem and then apply the fix to the real world situation.
Use Scientific Method
Use the scientific method to improve your understanding of the problem. It works as follows:
- Write down the question you are trying to answer.
- Write down a hypothesis.
- Write down a prediction that is the result of the hypothesis.
- Test the prediction and write down the result.
- Repeat until the question is answered.
Imagine we created a simplified application using the grid component. Now we can use the scientific method to figure out why it is slow.
Write down the question: Why does it take 5 seconds to render the grid?.
After thinking about it for a while, we can get this idea: Maybe the change detection runs too many times?. This is our first hypothesis, so let’s write it down.
A prediction we can use to check the hypothesis is that appRef.tick will appear multiple in the dev tools profiler.
We run the experiment and the result confirms our hypothesis. Let’s write it down.
Now, to the next question: Why does the change detection run hundreds of times?.
Steps 1, 3, an 4 of this process are well defined. They can almost feel mechanical. Step 2 — coming up with a hypothesis — is where the creative work happens. And that’s where good mental models and intuitions are of great help.
Develop Good Mental Models
“Point of view is worth 80 IQ points” Alan Kay
A high school student who knows basic algebra, geometry and calculus can solve more math problems than talented mathematicians of ancient Rome. That’s not because the student is “smarter”. Her IQ isn’t higher. She can do it because she has good mental models.
Developing good mental models is one of the most important investments we can make as engineers. For instance, look at this list:
- a programming language
- a program
- a compiler
- type systems (optional, mandatory)
- functional programming, imperative programming, logical programming
- a vm
- an interpreter
- a garbage collector
- a database
- distributed systems
Do you have good mental models of all of them?
Having a good mental model about X, does not mean you have to be an expert in X. It means that you can draw a diagram showing how X works, and, if given enough time, can build a simple version of X.
The mental models above are generic. These are some frontend-oriented mental models:
- Change detection
- Observable objects
- Virtual DOM
- CQRS/event sourcing/Redux
Develop Mental Models
How do you develop good mental models?
First, you read a lot. Read books, read papers.
Use “Narrow” Technologies
Second, find a piece of technology that does just that thing you are trying to learn and does only that. If you are trying to learn functional programming, pick up Elm, and spend a week trying to solve problems with it. Since Elm is pure, it won’t let you take shortcuts — you will have to do it by the book. You also won’t be distracted by the complexity of a more generic tool, such as Scala.
Build It Yourself
Finally, build a simplified version of what you are trying to learn from scratch. Learning about a compiler? Build your own compiler. Learning about logical programming? Build your own prolog interpreter. Learning about distributed systems? Build a tiny program illustrating message passing between different nodes in the system. Use it to learn about CAP.
It takes a year to build a compiler for a mainstream programming language. That’s because you want it to be fast, always correct, and handle all the weird corner cases of that language. Take a simpler language (e.g., some variation for lisp), ignore performance issues, you can do it in a day.
Note, this is play, so you cannot fail at this. You don’t have to ship anything. The only purpose of this is your learning. So even if you haven’t finished your compiler, but you learned a lot — you succeeded.
Learn Your Tools
Software engineers are professionals that rely on tools. These are some of the tools I use every day:
- Editors and IDEs
- Package managers (brew, npm, yarn, …)
- Languages (typescript, css, …)
- Dev tools (Chrome Dev tools, node debugger, source maps, …)
- Frameworks and tools (angular, rxjs, webpack, rollup, …)
Whereas mental models are abstract, tools are concrete. You can be good at VSCode, but know nothing about VIM, even though both are text editors. It takes months or even years to learn a new keyboard layout or master an IDE. That’s why this list is a lot narrower.
There are two reasons to learn your tools well:
You are more effective at executing tasks if you know the right tools. If you know Angular well, you can build a simple app in an hour, without having to Google stuff. Similarly, you will be able to troubleshoot an issue with a debugger much faster comparing to debugging with console.log.
An even more important reason to learn your tools well is so you can use them without thinking. Take editor shortcuts. Using editor shortcuts is better not because it is faster (even though it is), but because you can use them automatically, without having to go through menus, without thinking about it at all. So your conscious mind is free to think about the real problem.
Being good at your tools doesn’t mean everything you use has to be custom. I use an ergonomic programmable keyboard because I spent a lot of time thinking about how my fingers move while typing. You don’t have to go that far. Just get good at whatever you use.
I find the following to be true:
Every engineer who knows their tools well is a good engineer. A person that doesn’t know their tools well is very unlikely to be a good engineer.
You Need to Be More Than a Good Engineer to Make an Impact
Mastering the three skills doesn’t mean you will be effective at your job or will make a substantial impact. Being able to troubleshoot a bug or write a new library is not enough to make an impact. These are some other skills that matter just as much, if not more:
- Your social skills. Most interesting projects are built by teams. So they are built by people with different skills, backgrounds, and personalities. You need to have strong social skills to work in such environments.
- Your writing skills. You have to communicate your ideas to your team, your clients, and the community. Writing is the most scalable way to do it. Practice writing.
- Your work ethic.
These are the foundational skills of a good software engineer:
- The “Divide and Simplify” skill helps us tackle complexity. This is how you think, and it is the most fundamental skill upon which everything is built. We use it when learning technologies, writing software, and debugging issues.
- The “Learn Good Mental Models” skill helps us formulate hypotheses while debugging and helps us create novel solutions. This is the foundation that enables creativity.
- The “Learn Your Tools” skill help us execute ideas: run experiments, write software. It also helps us unload all the trivial aspects of our work, so we can focus our conscious mind on non-trivial problems.