[关闭]
@sambodhi 2020-03-06T10:33:28.000000Z 字数 7125 阅读 1211

十大全栈工程要避免的错误

行内代码过多,石墨无法渲染,源码已发给万佳。

undefined

作者 | Pranay Suresh
译者 | Sambodhi
编辑 | 万佳

《论语·述而》曾写道:“三人行必有我师焉,择其善者而从之,其不善者而改之。”在职业生涯中,问问那些成功的过来人,他们都做了什么,为什么要这么做,又是怎么做的。

这是我作为一个年轻工程师得到的建议。我有幸与良师益友一起工作,很自然的,我想要报答这份感激之情。在本文中,我将分享到目前为止我在职业生涯中所学到的全栈工程师的经验。

为什么是我,为什么是此时?

我在科技行业和硅谷已经打拼十年了。在这十年里,我一直在高速增长的初创公司工作,经历了与此相关的所有起起落落。从构建 nextgen 电子邮件客户端,到在全球范围内推广电动汽车,再到网上购物结账,我学到了很多很多。当我回顾所学到的经验教训、所犯的错误和曾错过的机会时,我不禁想到,其中一些是完全可以避免的,因此,我想与大家分享,便有了本文。

技术

1.gif

这是一份很难编辑的列表,但我可以向你保证,在过去十年来我已经用过每一个待办事项管理(ToDo List)软件(如 Google Keep、Mac Notes、Evernote、Gmail 等)。我从中总结了十大经验教训,我相信,这些经验教训将经得起时间的考验,并在未来的几年里依然适用。这份列表从前端开始,然后是后端 API 和数据库,最后是工程最佳实践 / 流程。

经验总结

  1. CSS 特指度(Specificity)
  2. 组件层次结构中的设计状态
  3. 后端编程中的面条式代码、千层饼式代码和馄饨式代码
  4. 生产中的 Postgres
  5. 欲速则不达
  6. 投资自动化
  7. 掌握你的工具
  8. 最简可行产品(Minimum Viable Product,MVP)
  9. 研究支持开发
  10. 科学调试

1. CSS 特指度

错误:我的 CSS 不适用。我要用 !important

教训:应该为特殊情况保留使用 !important,因为它们破坏了整个 CSS 层次结构,并强制使用特定样式。所以,你有必要了解 CSS 的特指度(Specificity)。

CSS 特指度是浏览器应用的一组规则,用来确定哪个 CSS 样式更特定。可以将其视为基于点的系统,它决定哪种 CSS 样式获得优先权,并最终应用于 DOM 元素。

如果你想知道为什么你的 CSS 没有被应用,这与 CSS 的特指度有关。在大型项目中,这是一个非常常见的问题,在这类项目中,像 SCSS 这样的预处理程序与复杂的 CSS 层次结构一起使用。了解 CSS 的特指度将有助于你保留使用 !important,仅在极罕见的覆盖情况下使用,例如,当你想要覆盖 CSS 库或让 iframe 覆盖主机站点样式时。

undefined

undefined

本质上,优先顺序是这样的:ID 选择器》类选择器》类型选择器。!important 和内联 style 属性会覆盖所有的 CSS。对于应用于元素的每个 CSS,你都可以轻松地确定哪种样式将生效。例如,如果你加载上面的 HTML 代码:

undefined

在本例中,ID 选择器优先级高于类型选择器。如果冲突的 CSS 选择器具有相同的优先级,那么将选择 CSS 文件中的最后一个。最后,Chrome DevTools(为什么要使用其他浏览器:))将会显示如上图所示的特指度顺序。如果你无法使用 CSS,请查看 Chrome 使用的特指度顺序,然后添加一个更具体的选择器(如 ID 选择器、类选择器、类型选择器),使你的 CSS 更特定,并指示浏览器选择它。如果你不想这么做,请看看这个特指度计算器:https://specificity.keegan.st/

2. 组件层次结构的设计状态

错误:我需要添加这个新的状态,我只需要将它放到这个 reducer 中,但不知道为什么这个 reducer 还有这么多其他状态?

教训:管理不当的 redux 状态会在开发人员中造成混乱,并导致 bug。如果你使用 react 和 redux 来构建前端应用程序,那么你可以考虑使用这种可视化技术,从用户界面组件层次结构中构建状态和 reducer 层次结构。从零到统一的组件状态层次结构有三个步骤:

让我们来看一个例子,在这个例子中,我们构建了一个博客网站,它有两个页面,一个页面用于博客列表,另一个页面用于个人博客:

第一步:在线框图中将用户界面进行可视化

undefined

undefined

主页和个人博客页面

第二步:将状态层次结构进行可视化以反映用户界面

相应的状态层次结构图如下所示:

undefined

状态层次结构

请注意,公共 Header 状态是如何被拉到根状态的。类似的,任何共享状态都可以在层次结构中冒泡,因此很明显,子组件共享该状态。

第三步:构建 reducer 层次结构以反映状态层次结构

undefined

reducer 层次结构

这是一个简单而强大的例子,展示了如何构造状态和 reducer 层次结构来匹配用户界面。这一过程可以轻松扩展到复杂的应用程序和大型团队。最后,你可以在这个结构之上构建操作和表示层。

3. 后端编程中的面条式代码、千层饼式代码和馄饨式代码

undefined

错误:这个代码库是如何组织的?也许我可以在这里添加这个文件,就像所有其他仓库代码一样。

教训:我已经写过所有意大利面食式的代码。说实话,我觉得在馄饨式代码里放上迷你千层饼式代码也许是个好办法。组织和培训所有开发人员以这种方式构建代码库,可以保持代码的可维护性、可测试性,最重要的是能够保持敏捷性。你可以在不影响其他功能的情况下,轻松修改特定的馄饨式代码(也就是功能)的实现细节。

千层饼式代码是:

馄饨式代码是:

将它们放在一起,就可以得到一个可扩展的、可维护的代码库。如果你能想出一种方法,通过特征名(某段馄饨式代码)来组织文件夹,并且在每个特征中实现干净的架构方法,这将会让你使用很长时间。

4. 生产中的 Postgres

错误:为什么这个查询速度这么慢?我认为是因为 Postgres 很慢的缘故。我需要切片,或者我认为这是 ORM,或者,我需要一个不同的数据库,Postgres 并不适合我。

教训:如果你在生产环境中运行 Postgres,那么你将会遇到查询缓慢、表锁、无限等待迁移和错误的问题。如果不是这样的话,那么对你有好处,你又是怎么做到的呢?这并不意味着 Postgres 不再是正确的工具,而是意味着你需要揭开帷幕,看看下面发生了什么。子曰:“学而不思则罔,思而不学则殆。”

到目前为止,我发现的最好工具是 pgbadger,它可以用来解决几乎所有的 Postgres 问题。这是一个 Perl 命令行工具,它将 Postgres(如果你使用 AWS 的话,就是 RDS)日志作为输入,并输出报告。该报告的好坏取决于你在 Postgres 上启用的日志。因此,在第一步时你可能需要启用这些日志:

  1. log_checkpoints = on
  2. log_connections = on
  3. log_disconnections = on
  4. log_lock_waits = on
  5. log_temp_files = 0
  6. log_autovacuum_min_duration = 0
  7. log_error_verbosity = default
  8. log_min_duration_statement = 1s

此外,你可能还需要启用 pg_stat_statements 语句来实时分析查询,并启用 auto_explain 来自动解释日志中运行缓慢的查询。

运行报告:

  1. pgbadger --prefix '%m %u@%d %p %r %a : ' /pglog/postgresql.log

undefined

该报告将汇总数据,并提供有关 Postgres 所做工作的大量信息。你可以找到关于错误、最慢查询、等待最长查询、获取的锁类型、临时文件是否用于排序、检查点运行的频率、真空运行的频率以及其他类似信息。有了这些数据,你就可以识别和修复运行缓慢的查询,并通过调优提高 Postgres 的性能。

你可以持续运行此报告(CLI 支持增量模式),从而随时掌握新问题。

另外,如果你想了解解解释输出,可以用这款工具:https://tatiyants.com/pev/#/plans/new 该工具将解释 JSON 和原始查询作为输入,并将在如下所示的可视化树形图中对解释输出进行解释:

undefined

正如你所见,节点将有最大、最慢、最贵等标签。这将帮助你根据 Postgres 的执行方式来优化查询。

最后,如果在 Postgres 中建立能力是不可行的,我建议聘请像 Percona 这样的数据库咨询公司。

欲速则不达

错误:看起来不错嘛,让我们交付吧!

教训

undefined

在这个快速行动、打破常规的时代,这似乎不是最受欢迎的建议,但是“慢慢来”会有回报的。与其快速地发布糟糕的代码,不如有条不紊地慢下来,发布几乎没有生产 bug 的代码。

优秀的工程师会考虑到软件系统的所有问题。他们关心的不仅仅是代码覆盖率,还关心可能破坏相同代码路径的奇怪输入。通过分层架构,它们可以实现模拟层,并只测试所考虑的层。他们不仅实现了单元测试,还实现了集成和功能测试,或者如果你的团队还有 QA 工程师,就与他们一起测试这些用例。

子曰:“无欲速,无见小利。欲速则不达,见小利则大事不成。”

6. 投资自动化

错误:我们手动部署到 staging 和沙箱,是临时的。生产也是手工部署的,但每天执行一次。

教训:拥有一个 CI/CD 系统管理部署意味着更可预测的结果。软件以促销策略在管道中移动,而临时部署则被降级到特殊情况下。这可以确保你正在交付的软件的稳定性和可靠性,这是工程团队的主要责任。

要投资于:

undefined

7. 掌握你的工具

错误:噢,我需要找到这个实现的接口,让我来搜索一下。我记得它以前就在这个文件夹里,咦?现在不见了呢?让我们看看那个文件夹里有没有……算了,我还是问问别人吧!

教训:不知道如何操作你的工具,会让你效率低下。你能想象一个邋遢的裁缝使用缝纫机的样子吗?这不仅关系到代码的结果,还关系到构建软件的效率。

undefined

了解你的工具,掌握捷径。你的代码编辑器可能是需要掌握的第一个工具。这是你谋生的家伙。你应该知道如何设置选项卡的排序、原位源代码搜索,向前和向后代码深度遍历,打开最后的编辑文件,导航到接口 / 实现,读取文件中的文件架构,显示调用图等。如果你使用的是基于文本的编辑器,而不是用图形用户界面,那也可以。像 VIM 这样的编辑器,有很多有用的技巧可以掌握。

请注意手动执行的常见操作,并学习如何通过快捷键来执行这些操作。要做到这点,一个简单的方法是先记下 5 条捷径,掌握它们,直到它们变成你的肌肉记忆,然后再记忆下 5 个捷径。

全栈工程师每天接触的其他常用工具有终端、docker、tableplus/pgadmin/ 其他一些数据库客户端用户界面、Chrome 开发工具等。

8. 最简可行产品

错误:我觉得这个功能会很有用。我要使用分布式容错复制高可用数据存储。我还要构建一个基于插件的架构,使这个软件具有超强的可扩展性。

教训:在构建某个东西之前,请确保它是你要构建的正确的东西。这就是最简可行产品的用武之地。

微信截图_20200305230505.png

理想的最简可行产品(Minimum Viable Product,MVP)应该尽可能少地触及所有层,而不仅仅是一个层。这是降低风险的一种做法。最好是最低限度地构建所有层,而不是完善单个层。最简可行产品并不意味着技术债务、糟糕的代码或缺乏测试。它不是抛弃型代码(throw-away code)。

如果最简可行产品花的时间太长(在某种程度上),那么它就有可能是错误的方案,并且可能有更简单的解决方案。

奥卡姆剃刀:在其他一切同等的情况下,较简单的解释普遍比较复杂的好。

undefined

这个纸杯蛋糕 的比喻是解释最简可行产品的另一种方式。与其尝试制作一个大蛋糕或构建一个完美的基础层,不如去制作一个纸杯蛋糕,获取反馈,然后迭代。至少,你可以知道人们是否喜欢这种口味。

9. 研究支持开发

错误:我(工程师)认为这是我们应该构建的。

教训:在开发之前,应该先进行大量的研究以佐证。与其跟随你的直觉,不如进行一项用户研究。要了解用户的需求,可以亲自或者通过视频采访,进行调查、查看日志等。这将帮助你更好地了解你的用户。然后,你可以提出一个假设并进行实验。当形成一个假设时,请使用反演 来反驳自己的主张。投资一个 A/B 测试该框架,它可以让你进行实验。

时间是宝贵的。要明智地使用它。最聪明的工程师会尝试优化一些不应该存在的东西。尽早提出正确的问题非常重要。

10. 科学调试

错误:有一个 bug。嗯,我想是因为代码改变所致。让我看看这个文件。没准也许是内存问题所致。也有可能是这两个原因所致呢。

教训:作为一名工程师,无论是作为事件的一部分,还是在本地环境中,你都将调试软件中的问题。如果不是通过结构化推理来完成的话,调试可能会非常痛苦、缓慢。

我们该如何系统地找出程序失败的原因呢?如果没有“直觉”、“敏锐思维”等模糊的概念,我们又该怎么才能做到这一点呢?我们想要的是找到一种查找失败原因的方法——一种方法:

将科学方法应用于调试问题,是发展失败理论的公正方法。科学调试的步骤如下:

  1. 重现错误(通常是一些时间、数据、用户、操作系统、调试器的组合)。
  2. 观察事实(彻底读取日志、错误跟踪等)。
  3. 在日志中明确地陈述假设,而不是在心里做假设。
  4. 如果你发现程序中的某部分存在错误,使用结构化的方法来缩小错误范围,如二分搜索。
  5. 测试假设:使用日志、断点、断言。
  6. 如果通过验证,应用修复并确保没有新的错误。
  7. 如果无效,请重复步骤 3 到步骤 6。

对于简单的调试情况来说,这可能看起来有点过头了,但对于设计多个团队的复杂分布式系统来说,一个系统的科学调试过程为消除模糊性提供了必要的结构。

额外福利

如果你已经读到这里,不妨继续阅读下面这 3 个额外的内容吧,这些内容涵盖软技能、个人成长方面。

1. 分享知识,服务他人

优秀的行为就是帮助他人成长。当你需要用别人能够理解的方式来解释某事时,你就会获得一定程度的清晰思维。

每天在 Slack 上分享有思想的链接,进行餐叙、演示、称赞他人的积极行为,挑战不明确的决定,并在你希望与某人或某项决定有着不同的方向时,给予建设性的反馈。你可以使用“感谢 ABC……。希望 XYZ”的句式表达你的反馈,你所要感激的事情和希望的事情之间的平均比例是 3:1。

通过这样做,你可以为自己打造个人品牌,从而获得职业资本。研究表明,那些拥有强大的个人品牌、网络形象和帮助他人的记录的人,会取得成功,更重要的是,还会拥有令人满意的职业生涯。

2. 塑造自己的世界

你无需接受这一现实世界。你可以通过坐在驾驶座上,塑造你所感知的世界。

这可能意味着在设计讨论和代码审查期间发表意见,或者修复关键的不稳定的测试(flaky test)。很多人会告诉你要多发言,提高知名度,以便在公司内部发展,但他们却从来不告诉你要怎么才能做到这一点。要做到这一点,最好的方法是拥有坚定的观点和信心,将人们拉向你的方向。不要畏惧组建小型团队来构建 / 改进事物。不要屈服于你的恐惧。要大声说出来,只要不是无礼的,你都可以说出来。

负面情绪是改变的巨大动力。如果有问题让你感到困扰,你要扪心自问,并想出该如何引导改变。如果你将每一天都当作成长的途径,那么生活就会成为一种锻炼。

3. 结识朋友

如果你像我一样,想弄清楚什么对你真正重要,那么就去结识许多朋友吧,尤其是那些让你感兴趣的人。这可能意味着去参加会议,参加在线社区,在黑客松活动和项目上进行合作,或进参加任何类似的活动。这种接触会帮助你弄清楚你想要做什么。这样你就可以对那些无关紧要的事情说“No”,并抓住对你来说重要的那些机会。

许多成功人士感到幸运,称只是因为他们在一个正确的时间和正确的地点做了正确的事情而已,得以知道自己想从什么开始。这让他们能随机应变,抓住机会,减少遗憾。遇到聪明人时,不要轻易拒绝。

通过这样做,我发现我更喜欢的是广度而不是深度,更看重创造力和自由,喜欢多样化和非正式的关系。我不适合从事结构化的重复性工作、例行公事、稳定和安全的事情。

如果你知道你想要什么,世界会给你所需的信息。

全栈编程是很好玩的事儿。这是一个不断发展的领域,有一片学习冲浪的海洋。不要把自己或错误看得太重。分享它们,继续成长。

作者介绍:

Pranay Suresh,工程师,供职于 Bolt 公司。曾在 Tesla 供职,硅谷创业者。佐治亚理工学院(Georgia Tech)计算机科学硕士。同时也是博主、演讲者和导师。个人主页为 https://pranaysuresh.com

原文链接:

https://blog.bitsrc.io/10-fullstack-engineering-mistakes-to-avoid-d6bec039c81a

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注