There was a question on Zhihu: how can you tell a programmer’s skill level? Based on my experience reviewing code in recent years, I can’t help but rant a bit about the topic of engineering craftsmanship—just in time for the second post in the “Writing Good Code” series.
Mental Framework
Less skilled programmers often fall short in “abstraction”.
What is abstraction ability? In short, it’s the capacity to categorize and draw analogies. Through extensive practice and reading, problems are broken down orthogonally into atomic knowledge, which is highly reusable; then, by combining and reasoning with this atomic knowledge, new problems can be solved creatively. In other words: induction and deduction.
Author: Woodpecker Notes https://www.qtmuniao.com/2022/10/12/how-to-write-code-abstract Please indicate the source when reprinting
When designing systems, this manifests as precise descriptions of the concepts involved: for a single concept, defining its intension and extension; for multiple concepts, clarifying their hierarchical relationships. In code, this maps to a clear separation of responsibilities among different classes.
What problems arise when abstraction ability is lacking?
- Fragmented knowledge system. Unable to categorize scattered knowledge into a coherent system. This shows up in problem diagnosis: jumping around randomly, unable to see the thread of the problem, and naturally failing to analyze its root cause.
- Difficulty communicating with others. Unable to explain a problem at different levels of abstraction. When presenting solutions, one naturally tends to use the lowest level of abstraction—“pseudocode”-style descriptions—exposing lots of implementation details. If the other person shares very little context with you, it’s hard for them to understand what you’re saying. Therefore, you need to generalize (which is also abstraction) the problem differently for people with different contexts, trimming away context through abstraction until you can align with them.
- Obscure and hard-to-understand code. This can be understood from at least two angles. First, from a design perspective: for a given problem, if you can find a more essential abstraction, the corresponding code implementation will certainly be more concise and easier to understand. Second, from an organizational perspective: if a problem is complex but can be reasonably decomposed and abstracted layer by layer, it will definitely be easier to understand than a “flat pancake” style of organization.
Engineering Craftsmanship
Less skilled programmers are often “careless” with their code, which is actually a sign of poor engineering craftsmanship.
What does careless code look like? For example, variables with the same meaning using different names; paired functions (like CRUD) lacking a consistent naming scheme; similar code repeated without reuse; counterintuitive logic without any comments; submodules that are top-heavy; good encapsulation being punctured with holes; and so on. (For naming, you can refer to my previous article)
What’s wrong with this carelessness?
- Complexity explosion. One of the primary goals of software engineering is controlling complexity. Because the essence of engineering is to solve the complexity introduced by scale. Careless code, through poor naming, casual breaches of boundaries, and chaotic data flow, causes complexity to rise exponentially, eventually requiring a complete rewrite.
- Easy to hide bugs. Code that is inelegant, counterintuitive, and unclear easily conceals subtle bugs. They’re not caught during writing, and reviewers struggle to spot them too. Eventually, it becomes a complete black box: even though the code is right there, and you can understand each individual statement, you have no idea what it’s doing when put together.
- Drives colleagues crazy. Good code has a certain rhythm when you read it, and can make your whole day better; bad code feels like swallowing a fly, ruining your mood. And the worst part is, these careless people often deliver incredibly fast with great KPIs, but once they get promoted, they couldn’t care less about the aftermath. Care for your colleagues, reduce the mountain of technical debt, start with yourself.
However, I should note that different tasks require different levels of attention to detail. So how do you decide? Here I’d like to introduce a reference metric: lifecycle. The longer a code’s lifecycle, the cleaner it must be written; temporary code, such as small scripts, can afford to be less meticulous. Conversely, it’s precisely clean code that enables an exceptionally long lifecycle.
When you look at other people’s code or your own past code, what do you want to rant about? Feel free to discuss in the comments.
