Clean code isn't what I thought it was

Clean code isn’t what I thought it was

“整洁代码”并非我曾经以为的那样

What working on real systems taught me about maintainable code. My second job was the first time I worked with an international team where everyone had ten or more years of experience. I had maybe two. It was also the first time I was part of proper code reviews, branching strategies, and pull request workflows. Everything felt new and slightly intimidating. 在真实系统上的工作经历教会了我关于可维护代码的道理。我的第二份工作是我第一次与一个国际团队合作,团队里的每个人都有十年以上的经验,而我当时可能只有两年。那也是我第一次参与正规的代码审查、分支策略和 Pull Request 工作流。一切都让我感到新鲜,且略带一丝畏惧。

One of my first tasks was adding spacing between two elements. It should have been a simple margin or padding change, but I added a <br> tag instead. The feedback on that PR was polite but clear, and it made me a little embarrassed. That moment, along with dozens of similar ones, made me want to get better. I started reading about clean code and caring deeply about how my code looked. Small functions, no repetition, everything abstracted and organized. For a while, that served me well. It helped me grow from a junior developer into someone who could write code that passed review without a wall of comments. 我最初的任务之一是在两个元素之间增加间距。这本该是一个简单的 margin 或 padding 修改,但我却加了一个 <br> 标签。针对那个 PR 的反馈虽然礼貌但很明确,这让我感到有些尴尬。那一刻,连同后来几十次类似的经历,让我渴望变得更好。我开始阅读关于“整洁代码”的书籍,并非常在意代码的外观。小函数、无重复、一切都经过抽象和组织。有一段时间,这对我很有帮助。它帮助我从一名初级开发者成长为能够写出无需大量修改意见就能通过审查的代码的人。

But over time, as I worked on larger systems with real users and real constraints, I started noticing that the rules I had learned didn’t always hold up. Sometimes the “clean” approach made things worse, and sometimes messy-looking code worked better than the elegant version I would have written. This post is about how my definition of clean code expanded. I still believe in the principles I learned early on. I’d just add a few things to them now. 但随着时间的推移,当我开始处理拥有真实用户和实际约束的大型系统时,我开始注意到我所学的规则并不总是适用。有时,“整洁”的方法反而让事情变得更糟,而有时看起来杂乱的代码比我原本会写的优雅版本运行得更好。这篇文章讲述了我的“整洁代码”定义是如何扩展的。我依然相信我早期学到的原则,只是现在我想在其中增加一些新的见解。

What I thought clean code meant

我曾经以为的“整洁代码”是什么

When I first started paying attention to code quality, my idea of clean code was mostly about appearances. If the code looked organized and followed certain patterns, it was clean. If it didn’t, it wasn’t. I believed in small functions for everything. If a function was longer than fifteen or twenty lines, something was wrong. I would extract pieces into helpers even when they were only used once, just because the parent function felt “too long.” 当我刚开始关注代码质量时,我对整洁代码的理解主要停留在外观上。如果代码看起来井井有条并遵循某些模式,它就是整洁的;反之则不然。我坚信一切都应拆分为小函数。如果一个函数超过十五或二十行,那一定是有问题的。即使某些代码片段只被使用一次,我也会将其提取为辅助函数,仅仅是因为我觉得父函数“太长了”。

I was strict about DRY. Any time I saw similar logic in two places, I would immediately pull it into a shared utility or a reusable component. It didn’t matter if the two use cases were genuinely related or just happened to look alike at the time. I also loved building abstractions early. If I was creating a component, I would already be thinking about all the ways it could be reused later and designing it to handle cases that didn’t exist yet. I was making a new component for the smallest of differences, convinced that someday we would need it for a specific use case that never came. 我对 DRY(不要重复自己)原则非常严苛。每当我在两个地方看到相似的逻辑,我就会立即将其提取为共享工具或可复用组件。我不在乎这两个用例是否真的相关,还是仅仅在当时看起来相似。我也热衷于过早地构建抽象。如果我要创建一个组件,我会预想它未来所有可能的复用方式,并将其设计为能够处理那些尚未出现的场景。我甚至会为了极小的差异去创建一个新组件,坚信总有一天我们会用到它,但那个用例从未出现过。

And I’ll admit I had a weakness for clever solutions. Writing a compact, elegant utility function felt like a win, even if it took someone else twice as long to understand what it did. Clever code only feels good while you’re writing it. After that, everyone who touches it pays the price. None of this was coming from a bad place. I genuinely wanted to write good code. But I was optimizing for how code looked in the moment, without thinking much about how it would hold up over time. 我承认我曾沉迷于“巧妙”的解决方案。写出一个紧凑、优雅的工具函数让我很有成就感,即使这会让别人花两倍的时间去理解它的逻辑。巧妙的代码只有在编写时感觉良好,之后每一个接触它的人都要为此付出代价。这些初衷并不坏,我确实想写出好代码。但我当时是在为代码“当下的外观”进行优化,而没有过多考虑它如何经受住时间的考验。

What production systems taught me

生产系统教会我的事

Messy code that works is not the enemy 能运行的杂乱代码并非敌人

I’ve inherited a frontend codebase written by a backend developer twice in my career. If I had a nickel for every time that happened, I’d have two nickels. That’s not a lot, but it’s funny that it happened twice. By any textbook standard, both were messy. Lots of copy-pasting, components stretched into thousands of lines, and the kind of structure that makes you sigh when you first open it. And yet, both systems worked. They were stable, shipped, and served their purpose. Users didn’t care what the code looked like underneath. 在我的职业生涯中,我曾两次接手由后端开发者编写的前端代码库。如果每发生一次我就能得到五分钱,我现在就有两枚五分硬币了。虽然不多,但两次发生这种事确实很有趣。按照任何教科书标准,这两套代码都很乱。大量的复制粘贴、长达数千行的组件,以及那种让你第一次打开时就会叹气的结构。然而,这两套系统都能正常运行。它们稳定、可交付,并实现了预期的功能。用户并不关心底层的代码长什么样。

That forced me to ask myself an uncomfortable question: if the code works and users are happy, what exactly is the problem? I’m not saying we should all write sloppy code on purpose. Those codebases were genuinely hard to navigate, and working in them was slower than it needed to be. But they taught me that “messy” and “broken” are not the same thing. I’ve seen ugly systems serve users reliably for years and elegant ones collapse under the first real requirement change. What actually causes problems is code that’s hard to understand and hard to change, regardless of how it looks. 这迫使我问自己一个令人不安的问题:如果代码能运行且用户满意,那问题到底出在哪里?我并不是说我们应该故意写草率的代码。那些代码库确实难以导航,在其中工作比预期的要慢。但它们教会了我,“杂乱”和“损坏”并不是一回事。我见过丑陋的系统可靠地服务用户多年,也见过优雅的系统在第一次真正的需求变更下就崩溃了。真正导致问题的,是那些难以理解且难以修改的代码,无论它看起来多么整洁。

Early abstraction costs more than duplication 过早抽象的代价高于重复

I used to treat duplication as a problem that needed to be solved immediately. The DRY principle was practically a reflex. What I didn’t appreciate was that duplication is only a real problem when the duplicated pieces are likely to evolve together. And in practice, they often don’t. Two components that look the same today might need to go in completely different directions next month, and if you’ve already tied them together with a shared abstraction, you’re now fighting the code to pull them apart. 我过去把重复视为必须立即解决的问题。DRY 原则几乎成了我的本能。但我没有意识到的是,只有当重复的部分很可能一起演变时,重复才是一个真正的问题。而在实践中,它们往往不会。今天看起来相同的两个组件,下个月可能需要走向完全不同的方向;如果你已经通过共享抽象将它们绑定在一起,那么现在你将不得不为了拆分它们而与代码进行斗争。

The clearest lesson came when I was building an MVP for my own app. We made a deliberate decision to skip early optimization and tolerate duplication, because we weren’t sure if we were going to keep any of that code. Sure enough, we pivoted a few months later and scrapped most of it. Had we spent the time making everything DRY and properly abstracted, all of that effort would have been wasted. Duplication is cheap to fix later once you actually understand the pattern. Bad abstractions are a different story, because everything that depends on them has to change when you try to undo them. 最深刻的教训来自于我为自己的应用构建 MVP(最小可行性产品)的时候。我们刻意决定跳过过早优化并容忍重复,因为我们不确定是否会保留那些代码。果不其然,几个月后我们调整了方向,废弃了大部分代码。如果我们当初花时间把一切都做得 DRY 且抽象得当,所有的努力都将付诸东流。一旦你真正理解了模式,修复重复的成本是很低的。但糟糕的抽象则完全不同,因为当你试图撤销它们时,所有依赖于它们的东西都必须随之修改。

Architecture matters more than style 架构比风格更重要

Early in my career, most of my attention went to the code inside a file. Was the function clean? Were the variable names good? Was the logic well-structured? Those things matter, but they matter a lot less than where the code lives in the first place. In one project, I extracted inline data fetching logic from large components into separate service files. Each service was maybe fifty or sixty lines of straightforward code. But the difference was enormous. When something broke in an API call, we could open a single small file and find the problem, instead of scrolling through a thousand-line component trying to figure out where. 在职业生涯早期,我大部分注意力都集中在文件内部的代码上。函数是否整洁?变量名是否合适?逻辑结构是否良好?这些固然重要,但它们远不如代码存放的位置重要。在一个项目中,我将大型组件中的内联数据获取逻辑提取到了单独的服务文件中。每个服务大约只有五六十行直观的代码。但带来的差异是巨大的。当 API 调用出现问题时,我们可以直接打开一个小的文件找到问题所在,而不是在千行组件中滚动查找。