Good software development habits
良好的软件开发习惯
This post is not advice, it's what's working for me.
这篇文章并非建议,只是我目前行之有效的方法。
It's easy to pick up bad habits and hard to create good ones. Writing down what's working for me helps me maintain any good habits I've worked hard to develop. Here's an unordered list of 10 things that have helped me increase speed and maintain a respectable level of quality in the product I'm currently developing.
养成坏习惯很容易,而养成好习惯却很难。记录下对我有效的做法,能帮助我保持那些我努力培养的好习惯。以下列出了十个帮助我提高速度并保持产品开发质量的要点(无序列表)。
Keep commits small enough that you wonder if you're taking this "keep commits small" thing a little too far. You just never know when you have to revert a particular change and there's a sense of bliss knowing where you introduced a bug six days ago and only reverting that commit without going through the savagery of merge conflicts. My rule of thumb: compiling software should be commitable.
保持提交内容足够小,小到你会怀疑自己是不是过于执着于“提交内容要小”这件事。你永远不知道什么时候需要回滚某个特定的更改,而知道六天前你在哪里引入了 bug,并且只需回滚那个提交而无需经历合并冲突的痛苦,这是一种幸福。我的经验法则是:软件编译应该是可提交的。Live Kent Beck's holy words of wisdom: "for each desired change, make the change easy (warning: this may be hard), then make the easy change". Aim for at least half of all commits to be refactorings. Continuous refactoring is thinking of changes I can make in under 10 minutes that improve something. Doing this pays off whenever a bigger requirement comes in and you find yourself making a small change to satisfy it only because of those smaller improvements. Big refactorings are a bad idea.
谨记肯特·贝克的至理名言:“对于每个所需的更改,先使其易于实现(警告:这可能很难),然后再进行简单的更改”。目标是至少有一半的提交是重构。持续重构是指思考一些能在 10 分钟内完成并改进某些内容的更改。当更大的需求出现时,你会发现自己只需进行很小的更改就能满足需求,这正是这些小改进带来的回报。大型重构是个坏主意。All code is a liability. Undeployed code is the grim reaper of liabilities. I need to know if it works or at least doesn't break anything. Tests give you confidence, production gives you approval. The hosting costs might rack up a little with so many deploys but it's a small price to pay for knowing the last thing you did was a true sign of progression. Working software is the primary measure of progress, says one of the agile principles. Working and progress are doing a lot of heavy lifting in that sentence, so I've defined them for myself. Working is something being working enough to be deployed, and if it's code that's contributing to a capability, that's progress.
所有代码都是负债。未部署的代码是负债的死神。我需要知道它是否有效,或者至少不会破坏任何东西。测试能给你信心,生产环境能给你认可。如此频繁的部署可能会增加一些托管成本,但这对于知道你最后做的事情是真正的进步来说,是微不足道的代价。 工作的软件是衡量进展的主要指标,这是敏捷原则之一。在这个句子中,“工作”和“进展”承担了大量的含义,所以我为自己定义了它们。工作是指能够正常部署的东西,如果它是为某种功能做出贡献的代码,那就是进展。Know when you're testing the framework's capability. If you are, don't do it. The framework is already tested by people who know a lot more than you, and you have to trust them that the
useState()
hook does what it's supposed to do. If you keep components small, then you reduce the need for a lot of tests as the framework will be doing most of the heavy lifting in the component. If the component is big, then you introduce more complexity and now you need to write a lot of tests.
明确你是在测试框架的功能。如果是的,就不要这样做。框架已经被比你更了解的人测试过了,你必须相信useState()
钩子能够实现其预期功能。如果你保持组件足够小,那么你就可以减少很多测试的需求,因为框架将承担组件的大部分工作。如果组件很大,那么你就会引入更多复杂性,现在你需要编写大量的测试。If a particular function doesn't fit anywhere, create a new module (or class or component) for it and you'll find a home for it later. It's better to create a new independent construct than to jam it into an existing module where you know deep down it doesn't make sense. Worst comes to worst, it lives as an independent module which isn't too bad anyway.
如果某个特定函数不适合任何地方,就为它创建一个新的模块(或类或组件),以后你就会找到它的位置。创建一个新的独立结构比把它塞进一个现有的模块中要好得多,你内心深处知道这样做是不合理的。最坏的情况是,它作为一个独立的模块存在,这也不是太糟糕。If you don't know what an API should look like, write the tests first as it'll force you to think of the "customer" which in this case is you. You'll invariably discover cases that you would not have thought of if you had just written the code first and tests after. You don't have to be religious about TDD and it's OK to work in larger batches (e.g., write more than just a couple lines of code before making it pass). The amount of code to write in a red/failing state doesn't always have to be small. You know what you're doing, don't let dogma get in the way of productivity.
如果你不知道 API 应该是什么样子,先编写测试,因为它会迫使你去思考“客户”,在这种情况下,客户就是你。你会不可避免地发现一些你如果先编写代码再编写测试就不会想到的情况。你不必死板地遵循测试驱动开发,并且可以批量工作(例如,在使代码通过测试之前编写不止几行代码)。在红色/失败状态下编写的代码量并不总是需要很小。你知道你在做什么,不要让教条妨碍你的生产力。Copy-paste is OK once. The second time you're introducing duplication (i.e., three copies), don't. You should have enough data points to create a good enough abstraction. The risk of diverging implementations of the same thing is too high at this point, and consolidation is needed. It's better to have some wonky parameterization than it is to have multiple implementations of nearly the same thing. Improving the parameters will be easier than to consolidate four different implementations if this situation comes up again.
复制粘贴一次是可以的。第二次引入重复(即三个副本)时,不要这样做。你应该有足够的数据点来创建一个足够好的抽象。此时,实现相同内容的不同实现的风险太高,需要进行整合。拥有某些古怪的参数化比拥有多个几乎相同内容的实现要好。如果这种情况再次发生,改进参数将比整合四个不同的实现更容易。Designs get stale. You can slow the rate at which they get stale by refactoring, but ultimately you'll need to change how things work. Don't feel too bad about moving away from something that was dear to you a while ago and something you felt proud about at the time. You did the right thing then and shouldn't beat yourself up for not getting it right enough that you wouldn't need to change anything. Most of the time writing software is changing software. Just accept it and move on. There's no such thing as the perfect design, and change is at the core of software development. How good you are at changing things is how good you are at software development.
设计会过时。你可以通过重构来减缓它们过时的速度,但最终你需要改变事物的工作方式。不要因为放弃了一些你一段时间前非常珍视的东西,一些你当时感到自豪的东西而感到太难过。你当时做对了,你不应该因为没有做得足够好而需要改变任何东西而自责。大多数时候编写软件就是在修改软件。接受它并继续前进。完美的设想是不存在的,而变化是软件开发的核心。你改变事物的能力有多强,你的软件开发能力就有多强。Technical debt can be classified into three main types: 1) things that are preventing you from doing stuff now, 2) things that will prevent you from doing stuff later, and 3) things that might prevent you from doing stuff later. Every other classification is a subset of these three. Minimize having lots of stuff in #1 and try to focus on #2. Ignore #3.
技术债务可以分为三种主要类型:1) 现在阻止你做事的东西,2) 将来会阻止你做事的东西,以及 3) 可能将来会阻止你做事的东西。其他所有分类都是这三种类型的子集。尽量减少#1 中的大量内容,并尝试关注#2。忽略#3。Testability is correlated with good design. Something not being easily testable hints that the design needs to be changed. Sometimes that design is your test design. As an example, if you find yourself finding it difficult to mock
em.getRepository(User).findOneOrFail({id})
, then chances are you either need to put that call into its own function that can be mocked, or write a test utility which allows for easier mocking of the entity manager methods. Tests go unwritten when it's hard to test, not because you don't want to test.
可测试性与良好的设计相关。某些东西不容易测试,暗示着设计需要更改。有时,这种设计是你的测试设计。例如,如果你发现很难模拟em.getRepository(User).findOneOrFail({id})
,那么你可能需要将该调用放入可以模拟的自身函数中,或者编写一个测试实用程序,以便更容易地模拟实体管理器方法。难以测试时,测试就无法编写,而不是因为你不想测试。
There's probably a lot more, but 10 is a nice number.
可能还有很多,但 10 是一个不错的数字。