The Mistakes I Made As a Beginner Programmer国内的话读此文需要,这篇帖子很棒我特将此文翻译如下:
让我先说清楚一件事。如果您是初学程序员,本文并不是为了让您对可能犯的错误感到难过,而是让您了解它们,教您发现它们的迹象,并提醒您避免它们。
我过去犯了这些错误,并从这些错误里面学到了很多东西。我很乐意形成编码习惯来帮助我避免这些问题。相信你也可以。
这些错误在此处不以任何特定顺序呈现。
通常,无法轻松创造出高质量的内容。它需要仔细的思考和研究。高质量的程序也不例外。
编写有质量程序是有流程的:
思考、研究、计划、写、验证、修改 不幸的是,对于这个流程没法用一个词来概括。你需要养成一种习惯,让自己经常做这些事情。
我作为初学者程序员犯下的最大错误之一就是在没有太多思考和研究的情况下立即开始编写代码。虽然这可能适用于小型独立应用程序,但它对较大的应用程序有很大的负面影响。
就像你在说出任何你可能会后悔的事情之前需要思考一样,在编写代码的时候你也需要考虑清楚。编码也是一种沟通思路的方式。
When angry, count to 10 before you speak. If very angry, a hundred.
生气时,在说话之前数到10。如果非常生气,那就一百。
— Thomas Jefferson.
对于程序来说我们可以这样理解:
When reviewing code, count to 10 before you refactor a line. If the code does not have tests, a hundred.
查看代码时,在重构一行代码之前计数到10。如果代码没有测试,那就是一百。
- Samer Buna
编程主要是关于阅读以前的代码,研究需要什么以及它如何适应当前系统,以及规划以小的可测试增量编写功能。代码行的实际写入可能只占整个过程的10%。
不要将编程视为编写代码行。编程是一种需要培养的基于逻辑的创造力。
在开始编写代码之前进行规划是一件好事,但是当你做太多的事情时,即使好的东西也会伤害你。太多的水可能会毒害你。
不要寻找完美的计划,这在编程世界中并不存在。寻找一个足够好的计划,你可以用它来开始。事实上,你的计划会发生变化,但最重要的是这个计划能够促使你的代码拥有更加清晰的结构。太多的计划只是浪费你的时间。
我只是在谈论规划小功能。一次规划所有功能是不合理的。这就是我们所说的瀑布式(**Waterfall Approach** ),它是一个具有不同步骤的系统线性计划,这些步骤将逐一完成。您可以想象该方法需要多少计划。这不是我在这里谈论的方式。大多数软件项目都不适用瀑布式这种开发方式。任何复杂的事情都只能通过对现实的敏捷适应来实现。
编写程序必须是一个响应式活动。您将在瀑布计划中添加您从未想过的功能。您将删除功能,因为您在瀑布计划中从未考虑过这些原因。您需要修复错误并适应变化。你需要敏捷。
但是,请始终计划您的下几个功能。这样做非常谨慎,因为太少的计划和太多的计划都会损害代码的质量,而代码的质量也不是你可能冒的风险。
如果您只关注您编写的代码的一个方面,那么它应该是它的可读性。不清楚的代码是垃圾。它甚至不可回收。
永远不要低估代码质量的重要性。将编码看作是实现沟通的一种方式。您作为编码人员的主要工作是清楚地传达您正在处理的任何解决方案的细则。
关于编程我最喜欢的一句话就是:
Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live.
编码就像是最终维护你的代码的人将会成为一个知道你住在哪里的暴力精神病患者。
-**John Woods *
很棒的建议,**John!**
即使是小事也很重要。例如,如果您的缩进和大小写不一致,则应该丢失代码许可。
tHIS is WAY MORE important than you think
另一个简单的事情是使用长行。一行超过80个字符的任何内容都难以阅读。您可能想要在同一行上放置多个条件以使if语句块更加可见。不要那样做。永远不要超过80个字符。
许多像这样的简单问题可以通过linting和格式化工具轻松修复。在JavaScript中,我们有两个,优秀的工具:ESLint和Prettier。您可以选择一个来经常使用它们。
以下是与代码质量相关的一些错误:
- 在函数或文件中使用许多行。您应该始终将长代码分成较小的块,这些块可以单独进行测试和管理。我个人认为任何超过10行的函数都太长了,但这只是一个经验法则。
- 使用双重否定。请千万不要这样做。
使用双重否定是大错特错
- 使用简短,通用或基于类型的变量名称。为变量提供说明以及非模糊名称。
*There are only two hard things in Computer Science: cache invalidation and naming things. 计算机科学中只有两件事:缓存失效和对事物命名。 - Phil Karlton
- 没有说明的硬编码的原始字符串和数字。如果要编写依赖于固定原始字符串或数字值的逻辑,请将该值放在常量中并为其指定一个好名称。
const answerToLifeTheUniverseAndthing = 42;
- 使用捷径和变通方法,以避免花更多时间解决简单问题。不要徘徊在一个问题上。面对现实。
- 认为更长的代码更好。在大多数情况下,更短的代码更好。除非写的更长给更具有可读性。例如,不要使用聪明的单行和嵌套的三元表达式来保持代码更短,但也不要故意添加一些不需要的代码使代码更长。删除不必要的代码是您在任何程序中可以做的最好的事情。
Measuring programming progress by lines of code is like measuring aircraft building progress by weight.
*通过代码行测量编程进度就像按重量测量飞机构建进度一样。 - * Bill Gates 比尔盖茨
- 过度使用条件逻辑。你认为需要条件逻辑的大部分内容都可以在没有它的情况下完成 考虑所有替代方案,并根据可读性进行选择。相关:避免Yoda条件 和条件内的分配。
当我开始编程时,我记得当我遇到问题时,我会找到一个解决方案并立即运行它。在考虑我的第一个确定的解决方案的复杂性和潜在的失败之前,我会立即着手实施。
虽然第一个解决方案可能很诱人,但一旦您开始质疑您找到的所有解决方案,通常会发现好的解决方案。如果您无法想到这个问题的多种解决方案,那可能表明您并未完全理解该问题。
您作为专业程序员的工作不是找到解决问题的方法。而是找到这个问题的最简单的解决方案。“简单”是指解决方案必须正常工作并且运行良好,但仍然足够简单,可以阅读,理解和维护。
There are two ways of constructing a software design. One way is to make it so simple that there are obviously no deficiencies, and the other way is to make it so complicated that there are no obvious deficiencies.
构建软件设计有两种方法。一种方法是使其变的简单以至于显然没有缺陷,另一种方法是使其变的复杂以至于没有明显的缺陷。
- CAR Hoare
我经常犯的另一个错误是,即使在我发现它可能不是最简单的方法之后,我仍然坚持使用第一个解决方案。这可能与“ 不放弃 ”的心态在心理上有关。在大多数活动中这是一种很好的心态,但它不应该适用于编程。事实上,在编写程序时,这种心态会提前失败并经常失败。
在您开始怀疑解决方案的那一刻,您应该考虑将其丢弃并重新思考问题。无论您在该解决方案上投入多少资金,都是如此。像GIT这样的版本控制工具可以帮助您构建并尝试许多不同的解决方案。学会利用这一点。
Do not be attached to code because of how much effort you put into it. Bad code needs to be discarded.
不要附加到代码,因为你投入了多少精力,坏代码需要被丢弃。
在我应该首先研究它的时候,有很多例子我浪费了宝贵的时间来解决问题。
除非您使用边缘技术,否则当您遇到问题时,其他人可能已经遇到同样的问题并找到了解决方案。节省一些时间和Google It First。
有时候,上网搜索会发现你认为的问题根本就不是问题,你需要做的不是解决问题而是接受网上的一些解决方案。不要以为您知道选择问题解决方案所需的一切。搜索会让你大吃一惊。
但是,请注意Google的目的。新手的另一个标志是在不理解它的情况下复制和使用其他代码。虽然该代码可能正确地解决了您的问题,但您绝不应该使用任何您不完全理解的代码。
如果你想成为一名创造性的程序员,永远不要认为你知道自己在做什么。
The most dangerous thought that you can have as a creative person is to think that you know what you’re doing.
作为一个有创造力的人,最危险的想法就是认为自己知道自己在做什么。
- **Bret Victor 布雷特维克多
这一点不是关于使用面向对象的范例。封装理念的使用总是有用的。不使用封装通常会导致系统难以维护。
在应用程序中,一个功能应该只有一个处理它的地方。这通常是单个对象的职责。该对象应该只显示应用程序的其他对象使用它的必要条件。这不是保守,而是为了减少改程序的不同模块之间的依耦合。坚持使用这些规则可以让您安全地更改类,对象和函数的内部,而无需担心更大范围的影响。
逻辑和状态的概念应该是在他们的类。在类中,我的意思是设计模板。这可以是实际的Class对象或Function对象。您也可以将其标识为模块或包。
在一类逻辑中,自包含的任务应该得到自己的方法。方法应该做一件事,并做好这件事。类似的类应该使用相同的方法名称。
作为一名初学程序员,我并不总是有直觉为一个概念单元创建一个新的类,并且我经常无法确定什么是自包含的。如果你看到一个“ Util
”类内使用了许多不属于它的东西,那就是新手代码的标志。如果您进行了简单的更改,然后发现更改具有级联效果,并且您需要在其他地方进行许多更改,那么这是新手代码的另一个标志。
在向类中添加方法或添加更多职责之前,你需要时间思考并质疑自己的直觉。不要跳过或认为你稍后会重构。一次就把这个类改好。
这里的一个重要观念是你希望你的代码具有高内聚和低耦合,这只是一个迷人的术语,它意味着将相关代码保持在一起(在一个类中)并减少不同类之间的依赖关系。
当你正在编写的解决方案的时候通常会想一些其他事情。你写的每一行代码都会让你想到各种假设。这对于测试边缘情况是一件好事,但是将其用作潜在需求的驱动程序是错误的。
您需要确定您的假设属于哪两个主要类别。不要编写您今天不需要的代码。不要为未知的未来做好准备。
编写一个功能,因为你认为将来可能需要它是完全错误的。不要做。
始终为您正在实施的解决方案编写当前所需的最少代码量。当然,处理边缘情况,但不要添加边缘特征。
Growth for the sake of growth is the ideology of the cancer cell.
为了增长而成长是癌细胞的意识形态。
- **Edward Abbey
在准备面试时,初学者程序员通常会过多地关注算法。识别好的算法并在需要时使用它们是很好的,但是记住它们可能永远不会归因于你的编程天赋。
但是,记住您可以在您的语言中使用的各种数据结构的优点和缺点肯定会使您成为更好的开发人员。
在这里,使用错误的数据结构是一个醒目的广告牌,上面写着“新手代码”。
本文不是为了教你数据结构,而是让我提几个简单的例子:
- 使用列表(数组)代替映射(对象)来管理记录
最常见的数据结构错误可能是使用列表而不是映射来管理记录列表。要管理记录列表,您应该使用MAP。
请注意,我在谈论一个记录列表,其中每个记录都有一个标识符,用于查找该记录。使用标量值列表是可以的,如果使用的焦点是将值“推”到列表中,则通常是更好的选择。
在JavaScript中,最常见的列表结构是一个数组,最常见的地图结构是一个对象(现代JavaScript中也有一个地图结构)。
使用地图上的列表来管理记录通常是错误的。虽然这一点对于大型收藏品来说确实是真实的,但我会说它始终坚持下去。这一点很重要的主要原因是,当使用标识符查找记录时,地图比列表快得多。
- 不使用堆栈
在编写需要某种形式的递归的任何代码时,总是很想使用简单的递归函数。但是,通常很难优化递归代码,尤其是在单线程环境中。
优化递归代码取决于递归函数返回的内容。例如,优化一个返回两个或多个自身调用的递归函数比优化递归函数要困难得多,递归函数只返回一个对自身的调用。
作为初学者,我们倾向于是,有一种替代方法可以使用递归函数。您可以使用Stack结构。自己将函数调用推送到堆栈,并在准备好遍历回调时开始弹出它们。
想象一下,你得到了这样一个凌乱的房间:
然后要求您向该房间添加项目。因为它已经是一个大混乱,你可能会想把它放在任何地方。您可以在几秒钟内完成任务。
不要什么都不要做更不要让它变得更糟!始终保持代码比开始使用时更清晰。
对上面的房间做正确的事情就是清理所需的东西,以便将新物品放在正确的位置。例如,如果物品是需要放在衣柜中的衣服,则需要清除该衣柜的路径。这是正确完成任务的一部分。
以下是一些错误的做法,通常会使代码变得比它更糟(不完整列表):
复制代码。如果你复制/粘贴代码部分只是在那之后更改一行,你只是复制代码并造成更大的混乱。在上文凌乱的房间列子中,这就像引入另一个具有较低底座的椅子,而不是投资于可调节高度的新椅子。始终将抽象概念保留在您的脑海中并尽可能使用它。
不使用配置文件。如果需要在不同环境或不同时间使用可能不同的值,则该值属于配置文件。如果需要在代码中的多个位置使用值,则该值属于配置文件。当您为代码引入新值时,只要问自己这个问题:这个值是否属于配置文件?答案很可能是肯定的。
使用不必要的条件语句和临时变量。每个if语句都是一个需要至少经过双重测试的逻辑分支。如果你可以避免条件而不牺牲可读性,你应该。这个问题的主要问题是使用分支逻辑扩展函数而不是引入另一个函数。每当你认为你需要一个if语句或一个新的函数变量时,你应该问自己:我是在正确的级别更改代码还是应该在更高级别考虑问题?
关于不必要的if语句的主题,请考虑以下代码:
function isOdd(number){ if(number%2 === 1){ return true; } else { return false; } }
isOdd
上面的功能有一些问题,但你能看到最明显的问题吗?
它使用不必要的if语句。这是一个等效的代码:
function isOdd(number){ return(number%2 === 1); };
我已经学会了尽可能避免写注释的困难方法。大多数注释可以替换为代码中名称较好的元素。
例如,替换如下代码:
// This function sums only odd numbers in an array const sum = (val) => { return val.reduce((a, b) => { if (b % 2 === 1) { // If the current number is even a+=b; // Add current number to accumulator } return a; // The accumulator }, 0); };
可以在没有上面的注释的情况下编写相同的代码:
const sumOddValues = (array) => { return array.reduce((accumulator, currentNumber) => { if (isOdd(currentNumber)) { return accumulator + currentNumber; } return accumulator; }, 0); };
只使用更好的函数和参数名称会使的大多数注释变得不必要。在撰写注释之前请记住这一点。
但是,有时您会被迫进入可以添加到代码中的唯一清晰度的情况是通过注释。这是你应该构建你的注释来回答为什么这个代码的问题,而不是这个代码做什么的问题。
如果您非常想写一篇评论来澄清代码,请不要指出明显的问题。下面是一些无用的注释示例,只会在代码中增加噪点:
// create a variable and initialize it to 0 let sum = 0; // Loop over array array.forEach( // For each number in the array (number) => { // Add the current number to the sum variable sum += number; } );
不要做这类程序员。不该接受这样的代码。如果您必须修这些代码,请删除这些注释。最重要的是,教育程序员写这样的注释,他们是多么的糟糕。如果您碰巧雇用编写上述注释的程序员,请让他们知道他们可能会因此失去他们的工作。是的......这有多糟糕。
我要简单地说明这一点。如果你认为自己是一名专业的程序员,并且这种想法让你有信心在没有测试的情况下编写代码,那么你就是我文中所说的新手程序员。
如果您没有在代码中编写测试,那么您很可能会以其他方式手动测试您的程序。如果您正在构建Web应用程序,那么在每几行代码之后,您将刷新并与应用程序进行交互。我也这样做。手动测试代码没有任何问题。但是,您应该手动测试代码以确定如何自动测试它。如果您成功测试了与应用程序的交互,则应返回代码编辑器并编写代码,以便在下次向项目添加更多代码时自动执行完全相同的交互。
你是一个人,在每次代码更改后,您可能忘记测试所有以前成功的验证。让电脑为您做到这一点!
如果可以的话,在编写满足它们的代码之前,首先要猜测或设计验证。测试驱动开发(TDD)不仅仅是一些花哨的东西。它会让您对功能的思考方式以及如何为它们提供更好的设计产生积极影响。
TDD不适合所有人,并且它不适用于每个项目,但如果您可以使用它(甚至部分),您应该完全这样做。
看看这个实现该 sumOddValues
功能的函数。它有什么问题吗?
const sumOddValues = (array) => { return array.reduce((accumulator, currentNumber) => { if (currentNumber % 2 === 1) { return accumulator + currentNumber; } return accumulator; }); }; console.assert( sumOddValues([1, 2, 3, 4, 5]) === 9 );
断言通过。结果是对的,是吗?
上面代码的问题是它不完全。它正确地处理了一些情况(并且使用的断言恰好是这些情况之一)但除此之外还有许多问题。让我来看看其中几个:
- 问题#1:没有处理空输入。在没有任何参数的情况下调用函数时会发生什么?现在,当发生这种情况时,您会收到错误,显示函数的实现:
TypeError: Cannot read property 'reduce' of undefined.
由于两个主要原因,这通常是代码错误的表现。
您的用户不知道这个函数的实现细节。
该错误对用户没有帮助。你的函数对他们没用。然而,如果错误更清楚描述这个问题,他们会知道他们错误地使用了该功能。例如,您可以选择让函数抛出用户定义的异常,如下所示:
TypeError: Cannot execute function for empty list.
也许不是抛出错误,而是需要设计你的函数来忽略空输入并返回一个总和为 0
的值。无论如何,必须为这个问题做点什么。
- 问题#2:没有处理无效输入。如果使用字符串,整数或对象值而不是数组调用函数会发生什么?
这是函数现在抛出的内容:
sumOddValues(42); TypeError: array.reduce is not a function
这很不幸,因为 array.reduce
绝对是特定的函数!
由于我们命名了函数的参数array
,因此在函数42
中调用函数的任何内容(在上面的示例中)标记为array
。错误基本上是说这42.reduce
不是一个函数。
你看到这个错误是如何混淆的,对吧?也许更有帮助的错误是:
TypeError: 42 is not an array, dude.
问题#1和#2有时被称为边缘情况。这些是一些常见的边缘案例,但是通常也需要考虑不太明显的边缘案例。例如,如果我们使用负数会发生什么?
sumOddValues([1, 2, 3, 4, 5, -13]) // => still 9
好吧,-13
是一个奇数。但这是您希望此功能具有的行为吗?它应该抛出错误吗?总和应该包含负数吗?或者它应该只是忽略它现在正在做的负数?也许你会意识到该功能应该被命名sumPositiveOddNumbers
。
对这个示例做出决定很容易。更重要的一点是,如果你不编写一个测试用例来记录你的决定,你的函数的未来维护者将不知道你忽略负数是故意的还是错误的。
It’s not a bug. It’s a feature.
— Someone who forgot a test case
- 问题#3:并非所有有效案例都经过测试。忘记边缘情况,这个函数有一个合法且非常简单的情况,它无法正确处理:
sumOddValues([2,1,3,4,5])// => 11
2
不应该包含在上述内容内。
解决方案很简单,reduce
接受第二个参数作为初始值accumulator
。如果未提供该参数(如上面的代码中所示),reduce
则只使用集合中的第一个值作为该参数的初始值accumulator
。这就是为什么上面测试用例中的第一个偶数值包含在总和中。
一旦您发现了这个问题,或者在编写代码时,这些测试用例应该首先包含在测试中,以及许多其他测试用例,例如全偶数,列表里面有0
一个空列表。
如果您看到不处理许多情况的最小测试或忽略边缘情况,那么这是新手代码的另一个标志。
除非你是一个总是单独工作的超级编码员,否则毫无疑问你会在生活中遇到某种愚蠢的代码。初学者不会认识到它,并且他们通常认为它是好的代码,因为它似乎正在工作,并且它已经作为代码库的一部分很长一段时间了。
更糟糕的是,如果坏代码使用不良做法,那么初学者可能会在代码库中的其他地方重复这种不良做法,因为他们从他们认为好的代码中学到了这一点。
有些代码看起来很糟糕,但它可能有一个特殊的条件,迫使开发人员以这种方式编写它。这是一个详细评论的好地方,它可以向初学者传授有关该条件以及为何以这种方式编写代码。
作为初学者,您应该假设您不理解的任何未记录的代码都是不良的候选者。问一下。问一下。git blame
它!
如果该代码的作者早已消失或无法记住它,请研究该代码并尝试理解其中的所有内容。只有当你完全理解代码时,你才会形成一个观点:它是坏还是好。在此之前不要假设任何事情。
我认为“最佳做法”一词实际上是有害的。这意味着不需要进一步的研究。这是有史以来最好的做法。不要质疑它!
没有最佳实践。今天可能有很好的做法,对于这种编程语言。
我们之前认为是编程中最佳实践的一些内容今天被标记为不良实践。
如果你投入足够的时间,你总能找到更好的做法。不要再担心最佳实践,而要关注自己能做得最好的事情。
不要因为你在某处读到的一句话而做某事,或者因为你看到其他人这样做,或者因为有人说这是最好的做法。这包括我在本文中给出的所有建议!质疑一切,挑战所有理论,了解所有选择,并做出有根据的决定。
Premature optimization is the root of all evil (or at least most of it) in programming
过早优化是编程中所有邪恶(或至少大部分)的根源
*- Donald Knuth (1974)
虽然自Donald Knuth撰写上述声明以来编程已发生重大变化,但我认为它今天仍然是很有价值的建议。
要记住这个问题的好规则是:如果您无法使用代码测量可疑的性能问题,请不要尝试对其进行优化。
如果您在执行代码之前进行优化,那么您可能会过早地执行此操作。您投入时间优化也很有可能是完全没必要的。
当然,在引入新代码之前,您应该始终考虑一些明显的优化。例如,在Node.js中,不要泛洪事件循环或阻止调用堆栈是非常重要的。这是一个您应该始终牢记的早期优化示例。问问自己:我正在考虑的代码会阻塞调用堆栈吗?
在没有测试的情况下对任何现有代码执行的任何非显而易见的优化都被认为是有害的,应该避免。您认为可能是性能提升,如果成功,可能是好的,意外那就是错误。
不要浪费时间优化未经测试的性能问题。
向应用程序添加功能的最简单方法是什么?从您自己的角度来看,或许它如何适应当前的用户界面。对?如果该功能是从用户捕获某种输入,则将其附加到您已有的表单。如果该功能是添加指向页面的链接,则将其添加到您已有的链接的嵌套菜单中。
不要那样的开发者。成为最终用户之一的专业人士之一。他们想象这个特定功能的用户需要什么以及他们的行为方式。他们考虑了使用户能够轻松查找和使用该功能的方法,而不是在应用程序中以某种方式存在该功能的简单方法,而不考虑该功能的可发现性和可用性。
每个人都有他们最喜欢的工具列表,以帮助他们进行与编程相关的活动。有些工具很棒,有些工具很糟糕,但大多数工具对于某个特定的场景都很好,一般没有一个能使用所有场景的工具。
锤子是将钉子钉入墙壁的好工具,但对于螺丝钉来说它就不适用了。不要因为你“爱”那把锤子而在螺丝钉上使用锤子。不要在螺丝上使用锤子,因为这是亚马逊上最受欢迎的锤子,有5.0个用户评分。
依靠工具的受欢迎程度而不是它适合问题的程度是一个真正的新手的标志。
关于这一点的一个问题是,您可能不会知道某项工作的“更好”工具。在您目前的知识范围内,一个工具可能是您所知道的最佳工具。但是,与其他选项相比,它不会成为顶级列表。您需要熟悉可用的工具,并对可以开始使用的新工具保持开放的态度。
一些程序员拒绝使用新工具。他们对现有工具感到满意,他们可能不想学习任何新工具。我理解这一点,但这完全是错误的。
您可以使用原始工具建造房屋并享受您的美好时光,或者您可以投入一些时间和金钱购买优质工具并更快地建造更好的房屋。工具在不断改进,您需要熟悉它们并使用它们。
程序的一个重要方面通常是管理某种形式的数据。该程序可以添加新记录,删除旧记录和修改其他记录的接口。
即使程序代码中最小的错误也会导致其管理的数据处于不可预测的状态。如果对数据的所有验证都完全通过相同的错误程序完成,则尤其如此。
在代码 - 数据关系方面,初学者可能不会立即建立俩者的关系。他们可能觉得可以继续在生产中使用一些有缺陷的代码,因为不起作用的功能X并不是非常重要。问题是有缺陷的代码可能会不断引入一开始并不明显的数据完整性问题。
更糟糕的是,修复错误而不修复由这些错误引起的细微数据问题的代码只会累积更多数据问题,这些问题将案例纳入“不可恢复级别”标签。
你如何保护自己免受这些问题的影响?您可以简单地使用多层数据完整性验证。不要依赖单一用户界面。在前端,后端,网络通信和数据库上创建验证。如果这不是一个选项,那么您必须至少使用数据库级约束。
熟悉数据库约束并在向数据库中添加列和表时使用所有这些约束:
一个NOT NULL列上约束意味着空值将被拒绝该列。如果您的应用程序假定该字段存在值,则应在数据库中将其源定义为非null。
一个独一无二的列约束意味着列不能在整个表中有重复值。例如,这对于Users表上的用户名或电子邮件字段非常有用。
一个CHECK约束是必须评估为true,以接受该数据自定义表达式。例如,如果您有一个正常的百分比列,其值必须介于0到100之间,则可以使用检查约束来强制执行该操作。
一个PRIMARY KEY约束意味着列的值均为非空和独特的为好。你可能正在使用这一点。数据库中的每个表都应有一个主键来标识其记录。
外键约束意味着该列的值必须在另一表的列,这通常是一个主键相匹配的值。
另一个与数据完整性相关的新问题是缺乏对事务的思考。如果多个操作需要更改相同的数据源并且它们彼此依赖,则它们必须包装在可以在其中一个操作失败时回滚的事务中。
这是一个棘手的问题。在编程中,一些轮子值得重新发明。编程不是一个明确定义的领域域。许多事情变化得如此之快,新需求的引入速度比任何团队处理需求的速度都要快。
例如,如果您需要一个根据一天中的时间以不同速度旋转的轮子,而不是定制我们都知道和喜爱的轮子,也许我们需要重新考虑它。但是,除非您确实需要一个未在其典型设计中使用的车轮,否则不要重新发明它。只需使用该死的轮子。
在许多可用选项中挑选所需轮子的品牌有时具有挑战性。做一些研究,然后再购买!关于软件“轮子”的一个很酷的事情是它们中的大多数都是免费的,并且可以让你看到它们的内部设计。您可以通过内部设计质量轻松判断编码的轮子。如果可以的话,使用开源的轮子。开源软件包可以轻松调试和修复。它们也可以轻松更换。此外,更容易在内部支持它们。
但是,如果你只需要一个轮子,就不要购买一辆全新的汽车,并将你所维护的汽车放在那辆新车的顶部。不要只是为了使用一个或两个函数而引入整个库。关于这个的最好的例子是JavaScript中的lodash库。如果您只需要对数组进行混洗,只需导入该shuffle
方法即可。不要导入整个疯狂的lodash库。
编码新手的一个标志是,他们经常将代码审查视为批评。他们不喜欢Code Reviews。他们不欣赏他们。他们甚至害怕Code Reviews。
这是错的。如果你有这种感觉,你需要马上改变这种态度。将每个代码审查视为学习机会。欢迎他们并欣赏他们。向他们学习。最重要的是,感谢你的评论者教给你一些东西。
你是一个永远的代码学习者。你需要接受这一点。大多数代码评审会教你一些你不知道的东西。将它们归类为学习资源。
有时,审阅人也会出错,轮到你教他们一些东西。但是,如果您的代码中的某些内容并不明显,那么在这种情况下您的代码可能需要进行修改。如果你需要教你的审阅者什么,只要知道教是你作为程序员可以做的最有价值的活动之一。
新手有时会低估一个好的源/修订控制系统的力量,我的意思是Git。
源代码控制并不仅仅是推动您的更改以便其他人拥有和构建。它比那要大得多。资源管理可以让代码又清晰的历史。代码可能受到质疑,该代码的进展历史将有助于回答一些棘手的问题。这就是我们关心提交消息的原因。它们是另一个沟通您的实现的渠道,并通过小的提交点帮助代码的未来维护者了解代码是如何到达当前状态的。
经常提交并提前提交,并且为了一致性,请在提交主题行中使用现有时态动词。请详细说明您的消息,但请记住它们应该是摘要。如果你需要多行,这可能表明你的提交太长了。重新修改提交!
不要在提交消息中包含任何不必要的内容。例如,不要列出在提交摘要中添加,修改或删除的文件。该列表存在于提交对象本身中,可以使用一些Git命令参数轻松显示。它只是摘要消息中的噪音。有些团队希望每个文件拥有不同的摘要更改,我认为这样提交太宽泛。
源码控制也是关有助于发现问题的。如果您遇到一个函数并开始询问它的需求或设计,您可以找到引入它的提交并查看该函数的上下文。提交甚至可以帮助您确定哪些代码将错误引入程序。Git甚至在提交(bisect
命令)中提供二进制搜索,以找到引入错误的单个提交。
即使在更改成为正式提交之前,也可以以极好的方式利用源代码控制。使用诸如分段更改,选择性修补,重置,存储,修改,应用,差异,反转和许多其他功能,为您的编码流程添加了一些丰富的工具。了解它们,学习它们,使用它们并欣赏它们。
你知道的Git功能越少,那你基本上属于我书中所讲编程新手。
对于函数式编程与其他范式相比,这也不是一个问题。这是另一篇文章的主题。
这就是共享状态是问题根源的事实,如果可能的话应该避免。如果不可能,则应将共享状态的使用保持在绝对最小值。
作为一名初学程序员,我没有意识到我们定义的每个变量都代表一个共享状态。它包含的数据可以由与该变量相同范围内的所有元素更改。范围越全局,这个共享状态的跨度越差。尽量保持小范围内的新状态,并确保它们不会向上泄漏。
当多个资源需要在事件循环的相同标记中一起更改该状态时(在基于事件循环的环境中),共享状态的大问题开始发生。竞争条件将会发生。
事情就是这样:新手可能会试图使用计时器作为此共享状态竞争条件问题的解决方法,特别是如果他们必须处理数据锁定问题。那是禁止的。不要做。注意它,在代码评论中指出它,永远不接受它。
程序报错是件好事。错误意味着你正在取得进步。错误意味着您可以轻松进行后续更改以取得更多进展。
专家程序员喜欢错误。新手讨厌他们。
如果看到这些美妙的小红色错误信息打扰到你,你需要改变这种态度。你需要将它们视为帮助者。你需要处理它们。你需要利用它们来取得进步。
有些错误需要升级到异常。异常是您需要计划处理的用户定义的错误。有些错误需要避免。他们会使得应用程序崩溃并使其退出。
你是一个人,你的大脑需要休息。你的身体需要休息。你经常会在状态中而忘记休息。我认为这是新手的另一个标志。这不是你可以妥协的事情。在工作流程中集成某些内容以强制您休息。多休息一会。离开你的椅子,走一小段路,然后来思考下一步你需要做什么。用新鲜的眼睛回到代码。
这是一个很长的帖子。你应该休息一下。
谢谢阅读。