分享

我作为初级程序员所犯的错误

 技术的游戏 2023-05-23 发布于广东

学会识别它们,养成避免它们的习惯

让我先澄清一件事。如果您是初学者,本文并不是要让您对可能犯的错误感到难过,而是要让您意识到这些错误,教您发现它们的迹象,并提醒您避免它们。

我过去犯过这些错误,并从每一个错误中吸取教训。我很高兴养成了编码习惯来帮助我避免它们。你也应该这样做。

这些错误在这里没有按任何特定顺序列出。

1)没有计划地编写代码

一般来说,高质量的书面内容不容易创作。这需要仔细思考和研究。优质节目也不例外。

编写高质量的程序是一个流程:
思考研究计划验证修改
不幸的是,没有好的首字母缩略词。您需要养成习惯,始终进行适量的这些活动。

作为初学者,我犯过的最大错误之一就是没有经过太多思考和研究就立即开始编写代码。虽然这可能适用于小型独立应用程序,但它对大型应用程序有很大的负面影响。

就像您在说出任何您可能会后悔的话之前需要三思一样,您在编码任何您可能会后悔的事情之前也需要三思。编码也是一种交流想法的方式。

生气的时候,先数到10再说话。如果很生气,一百。

— 托马斯·杰斐逊。

这是我对那句话的版本:

检查代码时,在重构一行之前数到 10。如果代码没有测试,一百个。

— 萨默·布纳

编程主要是关于阅读以前的代码,研究需要什么以及它如何适应当前系统,以及计划编写具有小的、可测试的增量的功能。实际写的代码行大概只占整个过程的10%。

不要将编程视为编写代码行。编程是一种需要培养的基于逻辑的创造力。

2) 写代码前计划太多

是的。在着手编写代码之前进行计划是一件好事,但是当您做太多事情时,即使是好的事情也会伤害您。喝太多水可能会中毒。

不要寻找完美的计划。这在编程世界中是不存在的。寻找一个足够好的计划,你可以用它来开始。事实上,你的计划会改变,但它的好处是迫使你进入某种结构,使你的代码更加清晰。太多的计划只会浪费你的时间。

我只是在谈论规划小功能。一次规划所有功能应该被取缔!这就是我们所说的瀑布法,它是一个系统的线性计划,有不同的步骤,一个一个地完成。您可以想象这种方法需要多少计划。这不是我在这里谈论的那种计划。瀑布方法不适用于大多数软件项目。任何复杂的事情都只能通过灵活适应现实来实施。

编写程序必须是一种响应式活动。您将添加您在瀑布计划中永远不会想到的功能。您将出于在瀑布计划中永远不会考虑的原因而删除功能。您需要修复错误并适应变化。你需要敏捷。

但是,请始终计划您接下来的几个功能。做这件事要非常小心,因为计划太少和太多都会损害代码的质量,而代码的质量不是你可以冒险的。

3)低估代码质量的重要性

如果您只能关注所编写代码的一个方面,那应该是它的可读性。不清楚的代码是垃圾。它甚至不可回收。

永远不要低估代码质量的重要性。将编码视为一种沟通实现的方式。作为编码人员,您的主要工作是清楚地传达您正在处理的任何解决方案的实现。

我最喜欢的关于编程的名言之一是:

始终编写代码,好像最终维护您的代码的人将是一个知道您住在哪里的暴力精神病患者。

— 约翰·伍兹

绝妙的建议,约翰!

即使是小事也很重要。例如,如果您的缩进和大小写不一致,您应该简单地失去您的代码许可。

tHIS is  WAY MORE importantthan         you think

另一个简单的事情是使用长线。任何超过 80 个字符的内容都很难阅读。您可能想在同一行中放置一些较长的条件,以使 if 语句块更加可见。不要那样做。永远不要超过 80 个字符的限制。

使用lintingformatting工具可以轻松解决许多像这样的简单问题。在 JavaScript 中,我们有两个可以完美协同工作的优秀工具:ESLintPrettier。帮自己一个忙,并始终使用它们。

这里还有一些与代码质量相关的错误:

— 在函数或文件中使用多行。您应该始终将长代码分成更小的块,以便单独测试和管理。我个人认为任何超过 10 行的函数都太长了,但这只是一个经验法则。

— 使用双重否定。请不要不要那样做。

使用双重否定是非常正确的

— 使用短的、通用的或基于类型的变量名。为您的变量提供描述性和明确的名称。

计算机科学中只有两件难事:缓存失效和命名事物。

— 菲尔·卡尔顿

— 没有描述的硬编码原始字符串和数字。如果您想编写依赖于固定原始字符串或数字值的逻辑,请将该值放入常量中并为其命名。

const answerToLifeTheUniverseAndEverything = 42;

— 使用草率的捷径和解决方法来避免在简单问题上花费更多时间。不要绕着问题跳舞。面对你的现实。

— 认为代码越长越好。在大多数情况下,越短的代码越好。只有在使代码更具可读性的情况下才编写更长的版本。例如,不要仅仅为了使代码更短而使用巧妙的单行代码和嵌套三元表达式,也不要在不需要时故意使代码更长。删除不必要的代码是您在任何程序中都能做的最好的事情。

用代码行数衡量编程进度就像用重量衡量飞机制造进度。

— 比尔盖茨

— 过度使用条件逻辑。大多数你认为需要条件逻辑的东西都可以在没有它的情况下完成。考虑所有备选方案,并仅根据可读性进行选择。除非可以测量,否则不要优化性能。相关:避免条件句中的Yoda conditions和赋值。

4) 选择第一个解决方案

当我开始编程时,我记得当我遇到问题时,我会找到解决方案并立即运行它。在考虑我第一个确定的解决方案的复杂性和潜在失败之前,我会立即着手实施。

虽然第一个解决方案可能很诱人,但一旦您开始质疑找到的所有解决方案,通常就会发现好的解决方案。如果你想不出一个问题的多种解决方案,这可能表明你没有完全理解这个问题。

作为专业程序员,您的工作不是找到问题的解决方案。就是找到最简单的解决问题的方法。我所说的“简单”是指解决方案必须能够正确工作并充分执行,但仍然足够简单以供阅读、理解和维护。

有两种构建软件设计的方法。一种是简单到明显没有缺陷,另一种是复杂到没有明显缺陷。

— 汽车霍尔

5) 不放弃

另一个我经常犯但不愿承认的错误是,即使在我确定第一个解决方案可能不是最简单的方法之后,我仍然坚持使用第一个解决方案。这可能与“不放弃”的心理有关。这是大多数活动中都应有的良好心态,但不适用于编程。其实写程序,正确的心态是早点失败,经常失败

在您开始怀疑解决方案的那一刻,您应该考虑放弃它并重新考虑问题。无论您对该解决方案投入了多少,这都是事实。GIT 等源代码控制工具可以帮助您分支并试验许多不同的解决方案。利用那个。

不要因为你付出了多少努力就执着于代码。坏代码需要被丢弃。

6)不谷歌搜索

在很多情况下,我本应首先研究问题,却浪费了宝贵的时间来解决问题。

除非您使用的是前沿技术,否则当您遇到问题时,其他人很可能遇到了同样的问题并找到了解决方案。为自己节省一些时间并首先使用 Google It

有时,谷歌搜索会发现你认为是问题的东西实际上不是问题,你需要做的不是修复它,而是拥抱它。不要假设您知道选择问题解决方案所需的一切。谷歌会让你大吃一惊。

但是,请注意您使用 Google 搜索的内容。新手的另一个标志是在不理解的情况下照原样复制和使用其他代码。虽然该代码可能会正确解决您的问题,但您永远不应使用您不完全理解的任何代码行。

如果你想成为一名有创造力的编码员,永远不要认为你知道自己在做什么。

作为一个有创造力的人,您可能拥有的最危险的想法就是认为您知道自己在做什么。

— 布雷特·维克多

7)不使用封装

这一点与使用面向对象范式无关。封装概念的使用总是有用的。不使用封装通常会导致系统更难维护。

在一个应用程序中,一个功能应该只有一个地方来处理它。这通常是单个对象的职责。该对象应该只显示应用程序的其他对象使用它所绝对必要的内容。这与保密无关,而是关于减少应用程序不同部分之间依赖性的概念。遵守这些规则可以让您安全地对类、对象和函数的内部进行更改,而不必担心在更大范围内破坏事物。

逻辑和状态的概念单元应该有自己的。对于类,我指的是蓝图模板。这可以是实际的Class对象或Function对象。您也可以将其标识为ModulePackage

在一类逻辑中,独立的任务片段应该有自己的方法。方法应该做一件事并且把那件事做好。相似的类应该使用相同的方法名。

作为一名初级程序员,我并不总是有为概念单元开始新课程的本能,而且我常常无法确定什么可以自包含。如果你看到一个“Util”类被用作许多不属于一起的东西的垃圾场,那就是新手代码的标志。如果你做了一个简单的改变,然后发现这个改变会产生级联效应,你需要在其他地方做很多改变,这是新手代码的另一个标志。

在向类添加方法或向方法添加更多职责之前,请思考并质疑您的直觉。你在这里需要时间。不要跳过或认为您稍后会重构它。第一次就做对。

这里的主要想法是您希望您的代码具有高内聚低耦合,这只是一个奇特的术语,意味着将相关代码放在一起(在一个类中)并减少不同类之间的依赖性。

8)为未知做计划

人们常常想超越您正在编写的解决方案进行思考。随着您编写的每一行代码,各种假设都会浮现在您的脑海中。这对于测试边缘情况来说是一件好事,但作为潜在需求的驱动程序则是错误的。

您需要确定您的假设属于这两个主要类别中的哪一个。不要编写您今天不需要的代码。不要为未知的未来做计划。

因为您认为将来可能需要它而编写功能是完全错误的。不要做。

始终为您正在实施的解决方案编写您今天需要的最少代码量。当然,处理边缘情况,但不要添加边缘特征

为了增长而增长是癌细胞的意识形态。

— 爱德华·艾比

9) 没有使用正确的数据结构

在准备面试时,初级程序员通常会过分关注算法。识别好的算法并在需要时使用它们是件好事,但记住它们可能永远不会归功于你的编程天才。

但是,记住您可以在您的语言中使用的各种数据结构的优点和缺点肯定会让您成为更好的开发人员。

使用错误的数据结构是一个巨大而强烈的广告牌标志,在这里尖叫着新手代码。

本文并不是要教您有关数据结构的知识,但让我举几个简单的例子:

— 使用列表(数组)代替映射(对象)来管理记录

最常见的数据结构错误可能是使用列表而不是映射来管理记录列表。是的,要管理记录列表,您应该使用 MAP。

请注意,我在这里谈论的是记录列表,其中每条记录都有一个用于查找该记录的标识符。对标量值使用列表是可以的,而且通常是更好的选择,特别是如果使用的重点是将值“推送”到列表中。

在 JavaScript 中,最常见的列表结构是数组,最常见的映射结构是对象(现代 JavaScript 中也有映射结构)。

使用列表而不是 MAP 来管理记录通常是错误的。虽然这一点实际上只适用于大型收藏,但我会说一直坚持下去。这很重要的主要原因是因为在使用标识符查找记录时,地图比列表快得多。

— 不使用堆栈

在编写任何需要某种形式的递归的代码时,使用简单的递归函数总是很诱人。但是,通常很难优化递归代码,尤其是在单线程环境中。

优化递归代码取决于递归函数返回什么。例如,优化返回两次或更多次调用自身的递归函数比优化仅返回一次调用自身的递归函数要困难得多。

作为初学者,我们往往会忽视的是,除了使用递归函数之外,还有一种替代方法。您可以只使用Stack结构。自己将函数调用推送到堆栈,并在准备好遍历调用时开始将它们弹出。

10) 使现有代码更糟

想象一下,你有一个这样凌乱的房间:

然后要求您向该房间添加一个项目。因为它已经是一团糟了,你可能会想把那个东西放在任何地方。您可以在几秒钟内完成任务。

不要用混乱的代码来做那件事。不要让它变得更糟!始终让代码比开始使用它时更干净一些。

对上面的房间正确的做法是清理所需的东西,以便将新物品放在正确的地方。例如,如果该物品是一件需要放在壁橱中的衣服,则您需要清除通往壁橱的路径。这是正确完成任务的一部分。

这里有一些错误的做法,通常会使代码比原来更乱(不是完整列表):

  • 复制代码。如果你复制/粘贴一个代码部分只是在那之后改变一行,你只是在复制代码并制造更大的混乱。在上面凌乱的房间示例的背景下,这就像引入另一把底座较低的椅子,而不是投资购买高度可调的新椅子。始终将抽象的概念牢记在心,并在可能的时候使用它。

  • 不使用配置文件。如果您需要使用在不同环境或不同时间可能不同的值,则该值属于配置文件。如果您需要在代码中的多个位置使用一个值,则该值属于配置文件。当您向代码中引入新值时,请始终问自己这个问题:这个值是否属于配置文件?答案很可能是肯定的。

  • 使用不必要的条件语句和临时变量。每个 if 语句都是一个逻辑分支,需要至少进行双重测试。当您可以在不牺牲可读性的情况下避免条件语句时,您应该这样做。这样做的主要问题是使用分支逻辑扩展功能而不是引入另一个功能。每次您认为需要一个 if 语句或一个新的函数变量时,您应该问问自己:我是在正确的级别更改代码,还是应该在更高级别考虑问题?

关于不必要的 if 语句,请考虑以下代码:

function isOdd(number) {  if (number % 2 === 1) {    return true;  } else {    return false;  }}

上面的isOdd函数有一些问题,但你能看出最明显的一个吗?

它使用了不必要的 if 语句。这是等效的代码:

function isOdd(number) {  return (number % 2 === 1);};

11)写关于显而易见的事情的注释

我学会了尽可能避免写注释的艰难方法。大多数注释都可以替换为代码中更好命名的元素。

例如,而不是下面的代码:

// This function sums only odd numbers in an arrayconst sum = (val) => {  return val.reduce((a, b) => {    if (b % 2 === 1) { // If the current number is odd      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);};

仅仅为函数和参数使用更好的名字就可以让大多数注释变得不必要。在写任何注释之前请记住这一点。

但是,有时您会被迫陷入这样的境地:您可以通过注释为代码添加的唯一清晰度。这时你应该构建你的注释来回答为什么这个代码的问题,而不是这个代码在做什么的问题。

如果您很想写一个 WHAT 注释来澄清代码,请不要指出显而易见的地方。下面是一些无用注释的示例,这些注释只会给代码增加噪音:

// create a variable and initialize it to 0let sum = 0;// Loop over arrayarray.forEach(  // For each number in the array  (number) => {    // Add the current number to the sum variable    sum += number;  });

不要成为那个程序员。不要接受那个代码。如果您必须处理这些注释,请将其删除。最重要的是,教育编写此类注释的程序员他们有多糟糕。如果您碰巧雇用了编写上述注释的程序员,请让他们知道他们实际上可能因此而丢掉工作。是的……就是这么糟糕。

12) 不写测试

我将保持这一点简单。如果您认为自己是专家级程序员,并且这种想法使您有信心在不进行测试的情况下编写代码,那么您就是我书中的新手。

如果您没有在代码中编写测试,您很可能会以其他方式手动测试您的程序。如果您正在构建 Web 应用程序,您将在每隔几行代码后刷新并与应用程序交互。我也这样做。手动测试代码没有错。但是,您应该手动测试您的代码以弄清楚如何自动测试它。如果您成功测试了与您的应用程序的交互,您应该返回到您的代码编辑器并编写代码以在您下次向项目添加更多代码时自动执行完全相同的交互。

你是一个人。您将忘记在每次代码更改后测试所有先前成功的验证。让电脑为你做这些!

如果可以,甚至在编写代码以满足它们之前,就可以从猜测或设计验证开始。测试驱动开发 (TDD) 不仅仅是一些花哨的炒作。它对您思考功能的方式以及如何为它们提出更好的设计产生积极影响。

TDD 并不适合所有人,也不是对每个项目都适用,但如果你可以利用它(即使是部分),你应该完全这样做。

13)假设如果事情正常,那么事情就是对的

看一下实现该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

在这种情况下做出决定很容易。更重要的一点是,如果您不编写测试用例来记录您的决定,那么您函数的未来维护者将不知道您忽略负数是有意还是错误。

这不是错误。这是一个特点。

— 忘记测试用例的人

— 问题 #3:并非所有有效案例都经过测试。忘掉边缘情况吧,这个函数有一个合法且非常简单的情况,但它无法正确处理:

sumOddValues([2, 1, 3, 4, 5]) // => 11

以上2不应该包含在总和中。

解决方案很简单,reduce接受第二个参数作为accumulator。如果未提供该参数(如上面的代码),reduce将仅使用集合中的第一个值作为accumulator。这就是为什么上面测试用例中的第一个偶数值被包含在总和中的原因。

虽然您可能马上或在编写代码时就发现了这个问题,但这个揭示它的测试用例应该首先包含在测试中,连同许多其他测试用例,如全偶数、列表里面有0,还有一个空列表。

如果您看到不处理许多情况或忽略边缘情况的最小测试,那是新手代码的另一个标志。

14) 不质疑现有代码

除非你是一个总是独自工作的超级编码员,否则毫无疑问你会在生活中遇到某种愚蠢的代码。初学者不会认出它,他们通常认为它是好的代码,因为它似乎可以工作,并且它已经成为代码库的一部分很长时间了。

更糟糕的是,如果糟糕的代码使用了糟糕的做法,初学者可能会试图在代码库的其他地方重复这种糟糕的做法,因为他们是从他们认为好的代码中学到的。

有些代码看起来很糟糕,但它可能有一个特殊的条件,迫使开发人员以这种方式编写它。这是一个详细注释的好地方,可以教初学者了解该条件以及为什么以这种方式编写代码。

作为初学者,您应该假设您不理解的任何未记录的代码都可能是错误的。质疑它。询问它。git blame它!

如果该代码的作者早已不在或不记得了,请研究该代码并尝试了解它的所有内容。只有当您完全理解代码时,您才能形成对代码好坏的看法。在此之前不要假设任何事情。

15) 执着于最佳实践

我认为“最佳实践”这个词实际上是有害的。这意味着不需要进一步的研究。这是有史以来最好的做法。不要质疑它!

没有最佳实践。对于这种编程语言,今天可能有一些好的做法。

我们以前认为是编程最佳实践的一些东西今天被标记为不良实践。

如果您投入足够的时间,您总能找到更好的做法。不要再担心最佳实践,而是专注于您最擅长的事情。

不要因为你在某处读到的引述,或者因为你看到别人这样做,或者因为有人说这是最佳实践而做某事。这包括我在本文中给出的所有建议!质疑一切,挑战所有理论,了解所有选择,只做出有根据的决定。

16) 执着于表现

过早的优化是编程中万恶之源(或至少是万恶之源)

— 唐纳德·高德纳 (1974)

尽管自 Donald Knuth 写下上述声明以来编程已经发生了显着变化,但我认为它在今天仍然具有宝贵的建议。

记住这一点的好规则是:如果您无法用代码测量可疑的性能问题,请不要尝试优化它。

如果您在执行代码之前进行优化,那么您很可能过早地进行了优化。您投入时间进行的优化也很有可能完全没有必要。

当然,在引入新代码之前,您应该始终考虑一些明显的优化。例如,在 Node.js 中,不要淹没事件循环或阻塞调用堆栈是极其重要的。这是您应该始终牢记的早期优化示例。问问自己:我正在考虑的代码会阻塞调用堆栈吗?

在没有测量的情况下对任何现有代码执行的任何非显而易见的优化都被认为是有害的,应该避免。你认为可能会带来性能提升的东西,如果完成了,可能会成为新的、意想不到的错误的来源。

不要浪费时间优化无法测量的性能问题。

17) 不以最终用户体验为目标

向应用程序添加功能的最简单方法是什么?从你自己的角度来看它,或者它如何适应当前的用户界面。正确的?如果该功能是从用户那里捕获某种输入,则将其附加到您已有的表单中。如果该功能是向页面添加链接,则将其添加到您已有的嵌套链接菜单中。

不要成为那个开发者。成为设身处地为最终用户着想的专业人士之一。他们想象这个特定功能的用户需要什么以及他们的行为方式。他们考虑如何让用户更容易找到和使用该功能,而不是考虑以某种方式使该功能存在于应用程序中而不考虑该功能的可发现性和可用性的简单方法。

18) 没有为工作选择合适的工具

每个人都有自己最喜欢的工具列表,以帮助他们进行与编程相关的活动。有些工具很棒,有些则很差,但大多数工具对某一特定事物非常有用,而对其他许多工具则不是那么好。

锤子是将钉子钉入墙壁的好工具,但它是与螺钉一起使用的最糟糕的工具。不要仅仅因为您“喜欢”那把锤子就在螺丝上使用锤子。不要仅仅因为它是亚马逊上最受欢迎的锤子,用户评论为 5.0,就在螺丝上使用锤子。

依赖工具的受欢迎程度而不是它对问题的适用程度是真正的新手的标志。

关于这一点的一个问题是,您可能不知道针对某项工作的“更好”工具。在您目前的知识范围内,工具可能是您所知道的最好的工具。但是,与其他选项相比,它不会排在首位。您需要熟悉可用的工具,并对可以开始使用的新工具保持开放的心态。

一些程序员拒绝使用新工具。他们对现有工具很满意,可能不想学习任何新工具。我明白这一点,我也能理解,但这完全是错误的。

您可以使用原始工具建造房屋并享受美好时光,或者您可以花一些时间和金钱购买优质工具并更快地建造更好的房屋。工具在不断改进,您需要轻松地了解和使用它们。

19) 不理解代码问题会导致数据问题

程序的一个重要方面通常是某种形式的数据的管理。该程序将是添加新记录、删除旧记录和修改其他记录的界面。

即使是程序代码中最小的错误也会导致其管理的数据处于不可预测的状态。如果对数据的所有验证都完全通过同一个错误程序完成,则尤其如此。

当谈到代码 - 数据关系时,初学者可能不会立即将这些点联系起来。他们可能会觉得继续在生产中使用一些有缺陷的代码是可以的,因为不起作用的功能 X 并不是特别重要。问题是有缺陷的代码可能会不断引入一开始并不明显的数据完整性问题。

更糟糕的是,发布修复错误的代码而不修复由这些错误引起的细微数据问题只会积累更多的数据问题,使案例成为“不可恢复级别”标签。

您如何保护自己免受此类问题的困扰?您可以简单地使用多层数据完整性验证。不要依赖单一的用户界面。在前端、后端、网络通信和数据库上创建验证。如果那不是一个选项,那么您至少必须使用数据库级约束。

熟悉数据库约束并在向数据库添加列和表时使用所有这些约束:

  • 列上的NOT NULL约束意味着该列将拒绝空值。如果您的应用程序假定该字段存在值,则其源在您的数据库中应定义为非空。

  • 列上的UNIQUE约束意味着该列不能在整个表中具有重复值。例如,这非常适合用户表中的用户名或电子邮件字段。

  • CHECK约束是自定义表达式,必须计算为 true 才能接受数据。例如,如果您有一个正常的百分比列,其值必须在 0 到 100 之间,您可以使用检查约束来强制执行该列。

  • PRIMARY KEY约束意味着该列的值既非空又是唯一的。你可能正在使用这个。数据库中的每个表都应该有一个主键来标识其记录。

  • FOREIGN KEY约束意味着该列的值必须与另一个表列(通常是主键)中的值相匹配。

另一个与数据完整性相关的新手问题是缺乏交易方面的思考。如果多个操作需要更改同一个数据源并且它们相互依赖,则它们必须包装在一个事务中,该事务可以在其中一个操作失败时回滚。

20) 重新发明轮子

这是一个棘手的问题。在编程中,有些轮子值得重新发明。编程不是一个定义明确的领域。很多事情变化得如此之快,新需求的引入速度比任何团队都快。

例如,如果您需要一个根据一天中的时间以不同速度旋转的轮子,而不是定制我们都知道和喜爱的轮子,也许我们需要重新考虑它。然而,除非你真的需要一个在典型设计中没有使用的轮子,否则不要重新发明它。只需使用该死的轮子。

在众多可用选项中选择所需车轮的品牌有时具有挑战性。做一些研究,然后再购买!软件“轮子”最酷的地方在于它们中的大多数都是免费的,并且可以让你看到它们的内部设计。您可以通过内部设计质量轻松判断编码轮。如果可以,请使用开源轮子。可以轻松调试和修复开源包。它们也可以很容易地更换。此外,更容易在内部支持他们。

但是,如果您需要一个轮子,请不要购买一辆全新的汽车,而是将您正在保养的汽车放在新车的上面。不要为了使用其中的一两个函数而包括整个库。最好的例子是 JavaScript 中的lodash库。如果您只需要打乱一个数组,只需导入该shuffle方法即可。不要导入整个该死的 lodash 库。

21) 对代码审查的错误态度

编码新手的一个标志是他们经常将代码审查视为批评。他们不喜欢他们。他们不欣赏他们。他们甚至害怕他们。

这是错误的。如果你有这种感觉,你需要立即改变这种态度。将每次代码审查视为一次学习机会。欢迎他们并感谢他们。向他们学习。最重要的是,感谢审稿人教给你一些东西。

你是一个永远的代码学习者。你需要接受这一点。大多数代码审查都会教你一些你不知道的东西。将它们归类为学习资源。

有时,审稿人会出错,轮到你教他们一些东西。但是,如果仅从您的代码中看不出来,那么在这种情况下您的代码可能需要修改。如果无论如何你都需要教你的审阅者一些东西,只要知道教学是你作为程序员可以做的最有价值的活动之一。

22) 不使用源代码管理

新手有时会低估一个好的源/版本控制系统的力量,我指的是Git

源代码控制不仅仅是将您的更改推送给其他人使用和构建。它比那大得多。源代码控制是关于清除历史的。代码将受到质疑,该代码的进展历史将有助于回答一些棘手的问题。这就是我们关心提交消息的原因。它们是另一个沟通您的实现的渠道,将它们与小的提交一起使用可以帮助您的代码的未来维护者弄清楚代码是如何达到现在所处的状态的。

经常提交并尽早提交,为了保持一致性,请在提交主题行中使用现在时动词。详细说明您的消息,但请记住它们应该是摘要。如果您需要其中的几行以上,这可能表明您的提交太长了。变基!

不要在提交消息中包含任何不必要的内容。例如,不要在提交摘要中列出添加、修改或删除的文件。该列表存在于提交对象本身中,并且可以使用一些 Git 命令参数轻松显示。它只是摘要消息中的噪音。有些团队喜欢更改每个文件的不同摘要,我认为这是提交过大的另一个标志。

源代码控制也与可发现性有关。如果您遇到一个功能并开始质疑它的需求或设计,您可以找到引入它的提交并查看该功能的上下文。提交甚至可以帮助您确定哪些代码将错误引入了程序。Git 甚至在提交(bisect命令)中提供二进制搜索来定位引入错误的单个有罪提交。

即使在更改成为正式提交之前,也可以以美妙的方式利用源代码控制。暂存更改、有选择地修补、重置、存储、修改、应用、差异化、反转等功能的使用为您的编码流程添加了一些丰富的工具。了解它们、学习它们、使用它们并欣赏它们。

您知道的 Git 功能越少,在我的书中您就越像个新手。

23) 过度使用共享状态

同样,这不是关于函数式编程与其他范例的区别。这是另一篇文章的主题。

这只是因为共享状态是问题的根源,应该尽可能避免。如果那不可能,则共享状态的使用应保持在绝对最低限度。

作为初学者,我没有意识到的是,我们定义的每个变量都代表一个共享状态。它保存的数据可以被与该变量处于同一范围内的所有元素更改。范围越大,这种共享状态的跨度就越差。尝试将新状态包含在小范围内,并确保它们不会向上泄漏。

当多个资源需要在事件循环的同一滴答中(在基于事件循环的环境中)一起更改该状态时,共享状态的大问题开始发生。竞争条件将会发生。

事情是这样的:新手可能会想使用计时器作为这种共享状态竞争条件问题的变通方法,尤其是当他们必须处理数据锁定问题时。这是一个很大的危险信号。不要做。注意它,在代码审查中指出它,永远不要接受它。

24) 对错误有错误的态度

错误是一件好事。他们意味着你正在取得进步。它们意味着您可以轻松进行后续更改以取得更多进展。

专业程序员喜欢错误。新手讨厌他们。

如果看到这些美妙的红色小错误消息让您感到困扰,您需要改变这种态度。您需要将他们视为帮手。你需要对付他们。您需要利用它们来取得进步。

有些错误需要升级为异常。异常是您需要计划的用户定义的错误。有些错误需要单独处理。他们需要使应用程序崩溃并使其退出。

25) 不休息

你是一个人,你的大脑需要休息。你的身体需要休息。你会经常在区域中而忘记休息。我认为这是新手的另一个标志。这不是你可以妥协的事情。在你的工作流程中加入一些东西来迫使你休息一下。多做短暂的休息。离开你的椅子,走一小段路,用它来思考你下一步需要做什么。以全新的眼光回到代码。

这是一篇很长的文章。你应该休息一下。

更多资源

我每天都为软件开发人员写一些新东西。进入每日列表以接收更新。

如果你喜欢我的文章,点赞,关注,转发!

    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多